aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar guilhem <guilhem@fripost.org>2013-03-29 17:06:02 +0100
committerGravatar Joey Hess <joey@kitenet.net>2013-03-29 18:04:52 -0400
commit71760a359f5515053085f242258dc615a1db1fd8 (patch)
tree0e49337fb773b721173aa25fc36635e05d8918f8
parentf3ee9b50b3a9caff7ec65ddaa9197f6daaf6f745 (diff)
Allow other MAC algorithms in the Remote Config.
-rw-r--r--Crypto.hs49
-rw-r--r--Remote/Helper/Encryptable.hs4
-rw-r--r--Test.hs2
-rw-r--r--Types/Crypto.hs49
-rw-r--r--doc/design/encryption.mdwn9
-rw-r--r--doc/encryption.mdwn7
6 files changed, 91 insertions, 29 deletions
diff --git a/Crypto.hs b/Crypto.hs
index 2777a9a20..14cc6efe3 100644
--- a/Crypto.hs
+++ b/Crypto.hs
@@ -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"
diff --git a/Remote/Helper/Encryptable.hs b/Remote/Helper/Encryptable.hs
index 4f0404f2a..f3b6bb787 100644
--- a/Remote/Helper/Encryptable.hs
+++ b/Remote/Helper/Encryptable.hs
@@ -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
diff --git a/Test.hs b/Test.hs
index a383a0a48..08ca824ad 100644
--- a/Test.hs
+++ b/Test.hs
@@ -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
diff --git a/Types/Crypto.hs b/Types/Crypto.hs
index 135522ba1..e97d02ba8 100644
--- a/Types/Crypto.hs
+++ b/Types/Crypto.hs
@@ -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
diff --git a/doc/design/encryption.mdwn b/doc/design/encryption.mdwn
index b7acbb732..45eb43cc9 100644
--- a/doc/design/encryption.mdwn
+++ b/doc/design/encryption.mdwn
@@ -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
diff --git a/doc/encryption.mdwn b/doc/encryption.mdwn
index cc61fea6f..5349e8c7a 100644
--- a/doc/encryption.mdwn
+++ b/doc/encryption.mdwn
@@ -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.