Leverage an ambiguities between Ciphers
Cipher is now a datatype data Cipher = Cipher String | MacOnlyCipher String which makes more precise its interpretation MAC-only vs. MAC + used to derive a key for symmetric crypto.
This commit is contained in:
parent
6883c17d62
commit
ac9807c887
4 changed files with 53 additions and 46 deletions
42
Crypto.hs
42
Crypto.hs
|
@ -64,20 +64,22 @@ cipherSize = 512
|
||||||
|
|
||||||
cipherPassphrase :: Cipher -> String
|
cipherPassphrase :: Cipher -> String
|
||||||
cipherPassphrase (Cipher c) = drop cipherBeginning c
|
cipherPassphrase (Cipher c) = drop cipherBeginning c
|
||||||
|
cipherPassphrase (MacOnlyCipher _) = error "MAC-only cipher"
|
||||||
|
|
||||||
cipherMac :: Cipher -> String
|
cipherMac :: Cipher -> String
|
||||||
cipherMac (Cipher c) = take cipherBeginning c
|
cipherMac (Cipher c) = take cipherBeginning c
|
||||||
|
cipherMac (MacOnlyCipher c) = c
|
||||||
|
|
||||||
{- Creates a new Cipher, encrypted to the specified key id. -}
|
{- Creates a new Cipher, encrypted to the specified key id. -}
|
||||||
genEncryptedCipher :: String -> EncryptedCipherVariant -> Bool -> IO StorableCipher
|
genEncryptedCipher :: String -> EncryptedCipherVariant -> Bool -> IO StorableCipher
|
||||||
genEncryptedCipher keyid variant highQuality = do
|
genEncryptedCipher keyid variant highQuality = do
|
||||||
ks <- Gpg.findPubKeys keyid
|
ks <- Gpg.findPubKeys keyid
|
||||||
random <- Gpg.genRandom highQuality size
|
random <- Gpg.genRandom highQuality size
|
||||||
encryptCipher (Cipher random) variant ks
|
encryptCipher (mkCipher random) variant ks
|
||||||
where
|
where
|
||||||
size = case variant of
|
(mkCipher, size) = case variant of
|
||||||
HybridCipher -> cipherSize -- used for MAC + symmetric
|
HybridCipher -> (Cipher, cipherSize) -- used for MAC + symmetric
|
||||||
PubKeyCipher -> cipherBeginning -- only used for MAC
|
PubKeyCipher -> (MacOnlyCipher, cipherBeginning) -- only used for MAC
|
||||||
|
|
||||||
{- Creates a new, shared Cipher. -}
|
{- Creates a new, shared Cipher. -}
|
||||||
genSharedCipher :: Bool -> IO StorableCipher
|
genSharedCipher :: Bool -> IO StorableCipher
|
||||||
|
@ -89,7 +91,7 @@ genSharedCipher highQuality =
|
||||||
updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
|
updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
|
||||||
updateEncryptedCipher _ SharedCipher{} = undefined
|
updateEncryptedCipher _ SharedCipher{} = undefined
|
||||||
updateEncryptedCipher [] encipher = return encipher
|
updateEncryptedCipher [] encipher = return encipher
|
||||||
updateEncryptedCipher newkeys encipher@(EncryptedCipher _ symmetric (KeyIds ks)) = do
|
updateEncryptedCipher newkeys encipher@(EncryptedCipher _ variant (KeyIds ks)) = do
|
||||||
dropKeys <- listKeyIds [ k | (False, k) <- newkeys ]
|
dropKeys <- listKeyIds [ k | (False, k) <- newkeys ]
|
||||||
forM_ dropKeys $ \k -> unless (k `elem` ks) $
|
forM_ dropKeys $ \k -> unless (k `elem` ks) $
|
||||||
error $ "Key " ++ k ++ " was not present; cannot remove."
|
error $ "Key " ++ k ++ " was not present; cannot remove."
|
||||||
|
@ -98,7 +100,7 @@ updateEncryptedCipher newkeys encipher@(EncryptedCipher _ symmetric (KeyIds ks))
|
||||||
when (null ks') $
|
when (null ks') $
|
||||||
error "Cannot remove the last key."
|
error "Cannot remove the last key."
|
||||||
cipher <- decryptCipher encipher
|
cipher <- decryptCipher encipher
|
||||||
encryptCipher cipher symmetric $ KeyIds ks'
|
encryptCipher cipher variant $ KeyIds ks'
|
||||||
where
|
where
|
||||||
listKeyIds = mapM (Gpg.findPubKeys >=*> keyIds) >=*> concat
|
listKeyIds = mapM (Gpg.findPubKeys >=*> keyIds) >=*> concat
|
||||||
|
|
||||||
|
@ -115,18 +117,26 @@ describeCipher (EncryptedCipher _ variant (KeyIds ks)) =
|
||||||
|
|
||||||
{- Encrypts a Cipher to the specified KeyIds. -}
|
{- Encrypts a Cipher to the specified KeyIds. -}
|
||||||
encryptCipher :: Cipher -> EncryptedCipherVariant -> KeyIds -> IO StorableCipher
|
encryptCipher :: Cipher -> EncryptedCipherVariant -> KeyIds -> IO StorableCipher
|
||||||
encryptCipher (Cipher c) variant (KeyIds ks) = do
|
encryptCipher c variant (KeyIds ks) = do
|
||||||
-- gpg complains about duplicate recipient keyids
|
-- gpg complains about duplicate recipient keyids
|
||||||
let ks' = nub $ sort ks
|
let ks' = nub $ sort ks
|
||||||
let params = Gpg.pkEncTo ks' ++ Gpg.stdEncryptionParams False
|
let params = Gpg.pkEncTo ks' ++ Gpg.stdEncryptionParams False
|
||||||
encipher <- Gpg.pipeStrict params c
|
encipher <- Gpg.pipeStrict params cipher
|
||||||
return $ EncryptedCipher encipher variant (KeyIds ks')
|
return $ EncryptedCipher encipher variant (KeyIds ks')
|
||||||
|
where
|
||||||
|
cipher = case c of
|
||||||
|
Cipher x -> x
|
||||||
|
MacOnlyCipher x -> x
|
||||||
|
|
||||||
{- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -}
|
{- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -}
|
||||||
decryptCipher :: StorableCipher -> IO Cipher
|
decryptCipher :: StorableCipher -> IO Cipher
|
||||||
decryptCipher (SharedCipher t) = return $ Cipher t
|
decryptCipher (SharedCipher t) = return $ Cipher t
|
||||||
decryptCipher (EncryptedCipher t _ _) =
|
decryptCipher (EncryptedCipher t variant _) =
|
||||||
Cipher <$> Gpg.pipeStrict [ Param "--decrypt" ] t
|
mkCipher <$> Gpg.pipeStrict [ Param "--decrypt" ] t
|
||||||
|
where
|
||||||
|
mkCipher = case variant of
|
||||||
|
HybridCipher -> Cipher
|
||||||
|
PubKeyCipher -> MacOnlyCipher
|
||||||
|
|
||||||
{- Generates an encrypted form of a Key. The encryption does not need to be
|
{- Generates an encrypted form of a Key. The encryption does not need to be
|
||||||
- reversable, nor does it need to be the same type of encryption used
|
- reversable, nor does it need to be the same type of encryption used
|
||||||
|
@ -158,16 +168,18 @@ readBytes a h = L.hGetContents h >>= a
|
||||||
- recipients MUST be included in 'params' (for instance using
|
- recipients MUST be included in 'params' (for instance using
|
||||||
- 'getGpgEncParams'). -}
|
- 'getGpgEncParams'). -}
|
||||||
encrypt :: [CommandParam] -> Cipher -> Feeder -> Reader a -> IO a
|
encrypt :: [CommandParam] -> Cipher -> Feeder -> Reader a -> IO a
|
||||||
encrypt params cipher = Gpg.feedRead params' pass
|
encrypt params cipher = case cipher of
|
||||||
where
|
Cipher{} -> Gpg.feedRead (params ++ Gpg.stdEncryptionParams True) $
|
||||||
pass = cipherPassphrase cipher
|
cipherPassphrase cipher
|
||||||
params' = params ++ Gpg.stdEncryptionParams (not $ null pass)
|
MacOnlyCipher{} -> Gpg.pipeLazy $ params ++ Gpg.stdEncryptionParams False
|
||||||
|
|
||||||
{- Runs a Feeder action, that generates content that is decrypted with the
|
{- Runs a Feeder action, that generates content that is decrypted with the
|
||||||
- Cipher (or using a private key if the Cipher is empty), and read by the
|
- Cipher (or using a private key if the Cipher is empty), and read by the
|
||||||
- Reader action. -}
|
- Reader action. -}
|
||||||
decrypt :: Cipher -> Feeder -> Reader a -> IO a
|
decrypt :: Cipher -> Feeder -> Reader a -> IO a
|
||||||
decrypt = Gpg.feedRead [Param "--decrypt"] . cipherPassphrase
|
decrypt cipher = case cipher of
|
||||||
|
Cipher{} -> Gpg.feedRead [Param "--decrypt"] $ cipherPassphrase cipher
|
||||||
|
MacOnlyCipher{} -> Gpg.pipeLazy [Param "--decrypt"]
|
||||||
|
|
||||||
macWithCipher :: Mac -> Cipher -> String -> String
|
macWithCipher :: Mac -> Cipher -> String -> String
|
||||||
macWithCipher mac c = macWithCipher' mac (cipherMac c)
|
macWithCipher mac c = macWithCipher' mac (cipherMac c)
|
||||||
|
|
|
@ -133,18 +133,11 @@ embedCreds c
|
||||||
| isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c) = True
|
| isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c) = True
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
|
||||||
{- Gets encryption Cipher, and encrypted version of Key. In case we want
|
{- Gets encryption Cipher, and encrypted version of Key. -}
|
||||||
- asymmetric encryption, leave the first empty, but encrypt the Key
|
|
||||||
- regardless. (Empty ciphers imply asymmetric encryption.) We could
|
|
||||||
- also check how long is the cipher (MAC'ing-only ciphers are shorter),
|
|
||||||
- but we don't want to rely on that only. -}
|
|
||||||
cipherKey :: RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
|
cipherKey :: RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
|
||||||
cipherKey c k = fmap make <$> remoteCipher c
|
cipherKey c k = fmap make <$> remoteCipher c
|
||||||
where
|
where
|
||||||
make ciphertext = (cipContent ciphertext, encryptKey mac ciphertext k)
|
make ciphertext = (ciphertext, encryptKey mac ciphertext k)
|
||||||
cipContent
|
|
||||||
| M.lookup "encryption" c /= Just "pubkey" = id
|
|
||||||
| otherwise = const $ Cipher ""
|
|
||||||
mac = fromMaybe defaultMac $ M.lookup "mac" c >>= readMac
|
mac = fromMaybe defaultMac $ M.lookup "mac" c >>= readMac
|
||||||
|
|
||||||
{- Stores an StorableCipher in a remote's configuration. -}
|
{- Stores an StorableCipher in a remote's configuration. -}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import Data.Digest.Pure.SHA
|
||||||
import Utility.Gpg (KeyIds(..))
|
import Utility.Gpg (KeyIds(..))
|
||||||
|
|
||||||
-- XXX ideally, this would be a locked memory region
|
-- XXX ideally, this would be a locked memory region
|
||||||
newtype Cipher = Cipher String
|
data Cipher = Cipher String | MacOnlyCipher String
|
||||||
|
|
||||||
data StorableCipher = EncryptedCipher String EncryptedCipherVariant KeyIds
|
data StorableCipher = EncryptedCipher String EncryptedCipherVariant KeyIds
|
||||||
| SharedCipher String
|
| SharedCipher String
|
||||||
|
|
|
@ -99,31 +99,33 @@ pipeStrict params input = do
|
||||||
- Note that to avoid deadlock with the cleanup stage,
|
- Note that to avoid deadlock with the cleanup stage,
|
||||||
- the reader must fully consume gpg's input before returning. -}
|
- the reader must fully consume gpg's input before returning. -}
|
||||||
feedRead :: [CommandParam] -> String -> (Handle -> IO ()) -> (Handle -> IO a) -> IO a
|
feedRead :: [CommandParam] -> String -> (Handle -> IO ()) -> (Handle -> IO a) -> IO a
|
||||||
feedRead params passphrase feeder reader = if null passphrase
|
feedRead params passphrase feeder reader = do
|
||||||
then go =<< stdParams (Param "--batch" : params)
|
|
||||||
else do
|
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
-- pipe the passphrase into gpg on a fd
|
-- pipe the passphrase into gpg on a fd
|
||||||
(frompipe, topipe) <- createPipe
|
(frompipe, topipe) <- createPipe
|
||||||
void $ forkIO $ do
|
void $ forkIO $ do
|
||||||
toh <- fdToHandle topipe
|
toh <- fdToHandle topipe
|
||||||
hPutStrLn toh passphrase
|
hPutStrLn toh passphrase
|
||||||
hClose toh
|
hClose toh
|
||||||
let Fd pfd = frompipe
|
let Fd pfd = frompipe
|
||||||
let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]
|
let passphrasefd = [Param "--passphrase-fd", Param $ show pfd]
|
||||||
|
closeFd frompipe `after` go (passphrasefd ++ params)
|
||||||
params' <- stdParams $ Param "--batch" : passphrasefd ++ params
|
|
||||||
closeFd frompipe `after` go params'
|
|
||||||
#else
|
#else
|
||||||
-- store the passphrase in a temp file for gpg
|
-- store the passphrase in a temp file for gpg
|
||||||
withTmpFile "gpg" $ \tmpfile h -> do
|
withTmpFile "gpg" $ \tmpfile h -> do
|
||||||
hPutStr h passphrase
|
hPutStr h passphrase
|
||||||
hClose h
|
hClose h
|
||||||
let passphrasefile = [Param "--passphrase-file", File tmpfile]
|
let passphrasefile = [Param "--passphrase-file", File tmpfile]
|
||||||
go =<< stdParams $ Param "--batch" : passphrasefile ++ params
|
go $ passphrasefile ++ params
|
||||||
#endif
|
#endif
|
||||||
where
|
where
|
||||||
go params' = withBothHandles createProcessSuccess (proc gpgcmd params')
|
go params' = pipeLazy params' feeder reader
|
||||||
|
|
||||||
|
{- Like feedRead, but without passphrase. -}
|
||||||
|
pipeLazy :: [CommandParam] -> (Handle -> IO ()) -> (Handle -> IO a) -> IO a
|
||||||
|
pipeLazy params feeder reader = do
|
||||||
|
params' <- stdParams $ Param "--batch" : params
|
||||||
|
withBothHandles createProcessSuccess (proc gpgcmd params')
|
||||||
$ \(to, from) -> do
|
$ \(to, from) -> do
|
||||||
void $ forkIO $ do
|
void $ forkIO $ do
|
||||||
feeder to
|
feeder to
|
||||||
|
|
Loading…
Reference in a new issue