Allow other MAC algorithms in the Remote Config.

This commit is contained in:
guilhem 2013-03-29 17:06:02 +01:00 committed by Joey Hess
parent cad52c9d9d
commit 55f0f858ee
6 changed files with 91 additions and 29 deletions

View file

@ -26,12 +26,11 @@ module Crypto (
GpgOpts(..),
getGpgOpts,
prop_hmacWithCipher_sane
prop_HmacSha1WithCipher_sane
) where
import qualified Data.ByteString.Lazy as L
import Data.ByteString.Lazy.UTF8 (fromString)
import Data.Digest.Pure.SHA
import Control.Applicative
import Common.Annex
@ -40,16 +39,20 @@ import Utility.Gpg.Types
import Types.Key
import Types.Crypto
{- The beginning of a Cipher is used for HMAC; the remainder
- is used as the GPG symmetric encryption passphrase.
{- The beginning of a Cipher is used for MAC'ing; the remainder is used
- as the GPG symmetric encryption passphrase. Note that the cipher
- itself is base-64 encoded, hence the string is longer than
- 'cipherSize': 683 characters, padded to 684.
-
- HMAC SHA1 needs only 64 bytes. The rest of the HMAC key is for expansion,
- perhaps to HMAC SHA512, which needs 128 bytes (ideally).
- It also provides room the Cipher to contain data in a form like base64,
- which does not pack a full byte of entropy into a byte of data.
- The 256 first characters that feed the MAC represent at best 192
- bytes of entropy. However that's more than enough for both the
- default MAC algorithm, namely HMAC-SHA1, and the "strongest"
- currently supported, namely HMAC-SHA512, which respectively needs
- (ideally) 64 and 128 bytes of entropy.
-
- 256 bytes is enough for gpg's symetric cipher; unlike weaker public key
- crypto, the key does not need to be too large.
- The remainder characters (320 bytes of entropy) is enough for GnuPG's
- symetric cipher; unlike weaker public key crypto, the key does not
- need to be too large.
-}
cipherBeginning :: Int
cipherBeginning = 256
@ -60,8 +63,8 @@ cipherSize = 512
cipherPassphrase :: Cipher -> String
cipherPassphrase (Cipher c) = drop cipherBeginning c
cipherHmac :: Cipher -> String
cipherHmac (Cipher c) = take cipherBeginning c
cipherMac :: Cipher -> String
cipherMac (Cipher c) = take cipherBeginning c
{- Creates a new Cipher, encrypted to the specified key id. -}
genEncryptedCipher :: String -> IO StorableCipher
@ -115,10 +118,10 @@ decryptCipher (EncryptedCipher t _) =
{- 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
- on content. It does need to be repeatable. -}
encryptKey :: Cipher -> Key -> Key
encryptKey c k = Key
{ keyName = hmacWithCipher c (key2file k)
, keyBackendName = "GPGHMACSHA1"
encryptKey :: Mac -> Cipher -> Key -> Key
encryptKey mac c k = Key
{ keyName = macWithCipher mac c (key2file k)
, keyBackendName = "GPG" ++ showMac mac
, keySize = Nothing -- size and mtime omitted
, keyMtime = Nothing -- to avoid leaking data
}
@ -147,13 +150,13 @@ encrypt opts = Gpg.feedRead ( Params "--symmetric --force-mdc" : toParams opts )
decrypt :: Cipher -> Feeder -> Reader a -> IO a
decrypt = Gpg.feedRead [Param "--decrypt"] . cipherPassphrase
hmacWithCipher :: Cipher -> String -> String
hmacWithCipher c = hmacWithCipher' (cipherHmac c)
hmacWithCipher' :: String -> String -> String
hmacWithCipher' c s = showDigest $ hmacSha1 (fromString c) (fromString s)
macWithCipher :: Mac -> Cipher -> String -> String
macWithCipher mac c = macWithCipher' mac (cipherMac c)
macWithCipher' :: Mac -> String -> String -> String
macWithCipher' mac c s = calcMac mac (fromString c) (fromString s)
{- Ensure that hmacWithCipher' returns the same thing forevermore. -}
prop_hmacWithCipher_sane :: Bool
prop_hmacWithCipher_sane = known_good == hmacWithCipher' "foo" "bar"
{- Ensure that macWithCipher' returns the same thing forevermore. -}
prop_HmacSha1WithCipher_sane :: Bool
prop_HmacSha1WithCipher_sane = known_good == macWithCipher' HmacSha1 "foo" "bar"
where
known_good = "46b4ec586117154dacd49d664e5d63fdc88efb51"

View file

@ -12,6 +12,7 @@ import qualified Data.Map as M
import Common.Annex
import Types.Remote
import Crypto
import Types.Crypto
import qualified Annex
import Config.Cost
import Utility.Base64
@ -107,7 +108,8 @@ embedCreds c
cipherKey :: RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
cipherKey c k = maybe Nothing make <$> remoteCipher c
where
make ciphertext = Just (ciphertext, encryptKey ciphertext k)
make ciphertext = Just (ciphertext, encryptKey mac ciphertext k)
mac = fromMaybe defaultMac $ M.lookup "mac" c >>= readMac
{- Stores an StorableCipher in a remote's configuration. -}
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig

View file

@ -103,7 +103,7 @@ quickcheck =
, check "prop_relPathDirToFile_basics" Utility.Path.prop_relPathDirToFile_basics
, check "prop_relPathDirToFile_regressionTest" Utility.Path.prop_relPathDirToFile_regressionTest
, check "prop_cost_sane" Config.Cost.prop_cost_sane
, check "prop_hmacWithCipher_sane" Crypto.prop_hmacWithCipher_sane
, check "prop_HmacSha1WithCipher_sane" Crypto.prop_HmacSha1WithCipher_sane
, check "prop_TimeStamp_sane" Logs.UUIDBased.prop_TimeStamp_sane
, check "prop_addLog_sane" Logs.UUIDBased.prop_addLog_sane
, check "prop_verifiable_sane" Utility.Verifiable.prop_verifiable_sane

View file

@ -9,8 +9,16 @@ module Types.Crypto (
Cipher(..),
StorableCipher(..),
KeyIds(..),
Mac(..),
readMac,
showMac,
defaultMac,
calcMac,
) where
import qualified Data.ByteString.Lazy as L
import Data.Digest.Pure.SHA
import Utility.Gpg (KeyIds(..))
-- XXX ideally, this would be a locked memory region
@ -18,3 +26,44 @@ newtype Cipher = Cipher String
data StorableCipher = EncryptedCipher String KeyIds | SharedCipher String
deriving (Ord, Eq)
{- File names are (client-side) MAC'ed on special remotes.
- The chosen MAC algorithm needs to be same for all files stored on the
- remote.
-}
data Mac = HmacSha1 | HmacSha224 | HmacSha256 | HmacSha384 | HmacSha512
deriving (Eq)
defaultMac :: Mac
defaultMac = HmacSha1
-- MAC algorithms are shown as follows in the file names.
showMac :: Mac -> String
showMac HmacSha1 = "HMACSHA1"
showMac HmacSha224 = "HMACSHA224"
showMac HmacSha256 = "HMACSHA256"
showMac HmacSha384 = "HMACSHA384"
showMac HmacSha512 = "HMACSHA512"
-- Read the MAC algorithm from the remote config.
readMac :: String -> Maybe Mac
readMac "HMACSHA1" = Just HmacSha1
readMac "HMACSHA224" = Just HmacSha224
readMac "HMACSHA256" = Just HmacSha256
readMac "HMACSHA384" = Just HmacSha384
readMac "HMACSHA512" = Just HmacSha512
readMac _ = Nothing
calcMac
:: Mac -- ^ MAC
-> L.ByteString -- ^ secret key
-> L.ByteString -- ^ message
-> String -- ^ MAC'ed message, in hexadecimals
calcMac mac = case mac of
HmacSha1 -> showDigest $* hmacSha1
HmacSha224 -> showDigest $* hmacSha224
HmacSha256 -> showDigest $* hmacSha256
HmacSha384 -> showDigest $* hmacSha384
HmacSha512 -> showDigest $* hmacSha512
where
($*) g f x y = g $ f x y

View file

@ -59,10 +59,11 @@ for each file in the repository, contact the encrypted remote to check
if it has the file. This can be done without enumeration, although it will
mean running gpg once per file fscked, to get the encrypted filename.
So, the files stored in the remote should be encrypted. But, it needs
to be a repeatable encryption, so they cannot just be gpg encrypted,
that would yeild a new name each time. Instead, HMAC is used. Any hash
could be used with HMAC; currently SHA1 is used.
So, the files stored in the remote should be encrypted. But, it needs to
be a repeatable encryption, so they cannot just be gpg encrypted, that
would yeild a new name each time. Instead, HMAC is used. Any hash could
be used with HMAC. SHA-1 is the default, but [[other_hashes|/encryption]]
can be chosen for new remotes.
It was suggested that it might not be wise to use the same cipher for both
gpg and HMAC. Being paranoid, it's best not to tie the security of one

View file

@ -21,6 +21,13 @@ If you want to use encryption, run `git annex initremote` with
Typically, you will say "encryption=2512E3C7" to use a specific gpg key.
Or, you might say "encryption=joey@kitenet.net" to search for matching keys.
The default MAC algorithm to be applied on the filenames is HMACSHA1. A
stronger one, for instance HMACSHA512, one can be chosen upon creation
of the special remote with the option `mac=HMACSHA512`. The available
MAC algorithms are HMACSHA1, HMACSHA224, HMACSHA256, HMACSHA384, and
HMACSHA512. Note that it is not possible to change algorithm for a
non-empty remote.
The [[encryption_design|design/encryption]] allows additional encryption keys
to be added on to a special remote later. Once a key is added, it is able
to access content that has already been stored in the special remote.