diff -ur crypto-api-0.5.2/Crypto/Modes.hs crypto-api-0.5.2.new/Crypto/Modes.hs --- crypto-api-0.5.2/Crypto/Modes.hs 2011-02-16 17:46:11.000000000 +0100 +++ crypto-api-0.5.2.new/Crypto/Modes.hs 2011-04-09 01:44:06.743745436 +0200 @@ -12,7 +12,7 @@ ( -- * Initialization Vector Type (for all ciphers for all modes that use IVs) IV - , getIV, getIVIO + , getIV, getIVIO, zeroIV -- * Blockcipher modes of operation. Note name' (with a prime) means strict, without a prime means lazy bytestrings. , ecb, unEcb , cbc, unCbc @@ -28,7 +28,9 @@ -- , gmc -- , xts -- , ccm - -- , ctr, unCtr, ctr', unCtr' + , cMac', cMac + , ctr, unCtr, ctr', unCtr', incIV, dblIV + , siv, unSiv, siv', unSiv' ) where import qualified Data.ByteString as B @@ -36,12 +38,15 @@ import Data.Serialize import qualified Data.Serialize.Put as SP import qualified Data.Serialize.Get as SG -import Data.Bits (xor) +import Data.Bits (xor,shift,(.&.),(.|.),testBit,setBit,clearBit,Bits,complementBit) import Data.Tagged import Crypto.Classes import Crypto.Random +import Crypto.CPoly import System.Crypto.Random (getEntropy) import Control.Monad (liftM) +import Data.Word (Word8) +import Data.List (genericDrop,genericReplicate,genericLength) #if MIN_VERSION_tagged(0,2,0) import Data.Proxy #endif @@ -120,10 +125,10 @@ in (c:cs, ivFinal) cbcMac' :: BlockCipher k => k -> B.ByteString -> B.ByteString -cbcMac' k pt = encode $ snd $ cbc' k (IV (B.replicate (blockSize `for` k) 0)) pt +cbcMac' k pt = encode $ snd $ cbc' k zeroIV pt cbcMac :: BlockCipher k => k -> L.ByteString -> L.ByteString -cbcMac k pt = L.fromChunks [encode $ snd $ cbc k (IV (B.replicate (blockSize `for` k) 0)) pt] +cbcMac k pt = L.fromChunks [encode $ snd $ cbc k zeroIV pt] -- |Cipher block chaining decryption for strict bytestrings unCbc' :: BlockCipher k => k -> IV k -> B.ByteString -> (B.ByteString, IV k) @@ -261,6 +266,253 @@ newIV = IV . B.concat . L.toChunks . L.take (fromIntegral ivLen) . L.drop mLen . L.fromChunks $ ivStr in (zwp' (B.concat ivStr) msg, newIV) +-- |Counter mode for lazy bytestrings +ctr :: BlockCipher k => (IV k -> IV k) -> k -> IV k -> L.ByteString -> (L.ByteString, IV k) +ctr = unCtr + +-- |Counter mode for lazy bytestrings +unCtr :: BlockCipher k => (IV k -> IV k) -> k -> IV k -> L.ByteString -> (L.ByteString, IV k) +unCtr f k (IV iv) msg = + let ivStr = iterate f $ IV iv + ivLen = fromIntegral $ B.length iv + newIV = head $ genericDrop ((ivLen - 1 + L.length msg) `div` ivLen) ivStr + in (zwp (L.fromChunks $ map (encryptBlock k) $ map initializationVector ivStr) msg, newIV) + +-- |Counter mode for strict bytestrings +ctr' :: BlockCipher k => (IV k -> IV k) -> k -> IV k -> B.ByteString -> (B.ByteString, IV k) +ctr' = unCtr' + +-- |Counter mode for strict bytestrings +unCtr' :: BlockCipher k => (IV k -> IV k) -> k -> IV k -> B.ByteString -> (B.ByteString, IV k) +unCtr' f k (IV iv) msg = + let ivStr = iterate f $ IV iv + ivLen = fromIntegral $ B.length iv + newIV = head $ genericDrop ((ivLen - 1 + B.length msg) `div` ivLen) ivStr + in (zwp' (B.concat $ collect (B.length msg) (map (encryptBlock k . initializationVector) ivStr)) msg, newIV) + +-- |Generate cmac subkeys +-- |The usage of seq tries to force evaluation of both keys avoiding posible timing attacks +cMacSubk :: BlockCipher k => k -> (IV k, IV k) +cMacSubk k = (k1, k2) `seq` (k1, k2) + where + bSize = blockSizeBytes `for` k + k1 = dblIV $ IV $ encryptBlock k $ B.replicate bSize 0 + k2 = dblIV $ k1 + +-- |Pad the string as required by the cmac algorithm. In theory this should work +-- | at bit level but since the API works at byte level we do the same +cMacPad :: ([Word8], Bool, Int) -> Maybe (Word8,([Word8], Bool, Int)) +cMacPad (_, _, 0) = Nothing +cMacPad ([], False, n) = Just (0,([], False, n-1)) +cMacPad ([], True, n) = Just (128,([], False, n-1)) +cMacPad (x:xs, b, n) = Just (x,(xs, b, n-1)) + +-- |Obtain the cmac with the specified subkey for lazy bytestrings +cMacWithSubK :: BlockCipher k => k -> (IV k, IV k) -> L.ByteString -> L.ByteString +cMacWithSubK k (IV k1, IV k2) l = L.fromChunks $ [go (chunkFor k t) $ B.replicate bSize1 0] + where + bSize1 = fromIntegral $ blockSizeBytes `for` k + bSize2 = fromIntegral $ blockSizeBytes `for` k + (t,e) = L.splitAt (((L.length l-1)`div` bSize2)*bSize2) l + pe = fst $ B.unfoldrN (bSize1) cMacPad (L.unpack e,True,bSize1) + fe | bSize2 == L.length e = zwp' k1 pe + | otherwise = zwp' k2 pe + go [] c = encryptBlock k (zwp' c fe) + go (x:xs) c = go xs $ encryptBlock k $ zwp' c x + +-- |Obtain the cmac for lazy bytestrings +cMac :: BlockCipher k => k -> L.ByteString -> L.ByteString +cMac k = cMacWithSubK k (cMacSubk k) + +-- |Obtain the cmac with the specified subkey for strict bytestrings +cMacWithSubK' :: BlockCipher k => k -> (IV k, IV k) -> B.ByteString -> B.ByteString +cMacWithSubK' k (IV k1, IV k2) b = go (chunkFor' k t) $ B.replicate bSize1 0 + where + bSize1 = fromIntegral $ blockSizeBytes `for` k + bSize2 = fromIntegral $ blockSizeBytes `for` k + (t,e) = B.splitAt (((B.length b-1)`div` bSize2)*bSize2) b + pe = fst $ B.unfoldrN (bSize1) cMacPad (B.unpack e,True,bSize1) + fe | bSize2 == B.length e = zwp' k1 pe + | otherwise = zwp' k2 pe + go [] c = encryptBlock k (zwp' c fe) + go (x:xs) c = go xs $ encryptBlock k $ zwp' c x + +-- |Obtain the cmac for strict bytestrings +cMac' :: BlockCipher k => k -> B.ByteString -> B.ByteString +cMac' k = cMacWithSubK' k (cMacSubk k) + +-- |Generate the xor stream for the last step of the CMAC* algorithm +xorend :: Int -> (Int,[Word8]) -> Maybe (Word8,(Int,[Word8])) +xorend bsize (0, []) = Nothing +xorend bsize (n, x:xs) | n <= bsize = Just (x,((n-1),xs)) + | otherwise = Just (0,((n-1),(x:xs))) + +-- |Obtain the CMAC* on lazy bytestrings +cMacStar :: BlockCipher k => k -> [L.ByteString] -> L.ByteString +cMacStar k l = go (lcmac (L.replicate bSize 0)) l + where + bSize = fromIntegral $ blockSizeBytes `for` k + bSizeb = fromIntegral $ blockSize `for` k + lcmac = cMacWithSubK k (cMacSubk k) + go s [] = s + go s [x] | (L.length x) >= bSize = lcmac $ zwp x $ L.unfoldr (xorend $ fromIntegral bSize) (fromIntegral $ L.length x,L.unpack s) + | otherwise = lcmac $ zwp (dblL s) (L.unfoldr cMacPad (L.unpack x,True,fromIntegral bSize)) + go s (x:xs) = go (zwp (dblL s) (lcmac x)) xs + +-- |Obtain the CMAC* on strict bytestrings +cMacStar' :: BlockCipher k => k -> [B.ByteString] -> B.ByteString +cMacStar' k s = go (lcmac (B.replicate bSize 0)) s + where + bSize = fromIntegral $ blockSizeBytes `for` k + bSizeb = fromIntegral $ blockSize `for` k + lcmac = cMacWithSubK' k (cMacSubk k) + go s [] = s + go s [x] | (B.length x) >= bSize = lcmac $ zwp' x $ fst $ B.unfoldrN (B.length x) (xorend bSize) (fromIntegral $ B.length x,B.unpack s) + | otherwise = lcmac $ zwp' (dblB s) (fst $ B.unfoldrN bSize cMacPad (B.unpack x,True,bSize)) + go s (x:xs) = go (zwp' (dblB s) (lcmac x)) xs + +-- |Create the mask for SIV based ciphers +sivMask :: B.ByteString -> B.ByteString +sivMask b = snd $ B.mapAccumR (go) 0 b + where go :: Int -> Word8 -> (Int,Word8) + go 24 w = (32,clearBit w 7) + go 56 w = (64,clearBit w 7) + go n w = (n+8,w) + +-- |SIV (Synthetic IV) mode for lazy bytestrings +-- |First argument is the optional list of bytestrings to be authenticated +-- | but not encrypted +-- |As required by the specification this algorithm may return nothing when +-- | certain constraints aren't met. +siv :: BlockCipher k => k -> k -> [L.ByteString] -> L.ByteString -> Maybe L.ByteString +siv k1 k2 xs m | length xs > bSizeb - 1 = Nothing + | otherwise = Just $ L.append iv $ fst $ ctr incIV k2 (IV $ sivMask $ B.concat $ L.toChunks iv) m + where + bSize = fromIntegral $ blockSizeBytes `for` k1 + bSizeb = fromIntegral $ blockSize `for` k1 + iv = cMacStar k1 $ xs ++ [m] + + +-- |SIV (Synthetic IV) for lazy bytestrings +-- |First argument is the optional list of bytestrings to be authenticated +-- | but not encrypted +-- |As required by the specification this algorithm may return nothing when +-- | authentication fails +unSiv :: BlockCipher k => k -> k -> [L.ByteString] -> L.ByteString -> Maybe L.ByteString +unSiv k1 k2 xs c | length xs > bSizeb - 1 = Nothing + | L.length c < fromIntegral bSize = Nothing + | iv /= (cMacStar k1 $ xs ++ [dm]) = Nothing + | otherwise = Just dm + where + bSize = fromIntegral $ blockSizeBytes `for` k1 + bSizeb = fromIntegral $ blockSize `for` k1 + (iv,m) = L.splitAt (fromIntegral bSize) c + dm = fst $ unCtr incIV k2 (IV $ sivMask $ B.concat $ L.toChunks iv) m + +-- |SIV (Synthetic IV) mode for strict bytestrings +-- |First argument is the optional list of bytestrings to be authenticated +-- | but not encrypted +-- |As required by the specification this algorithm may return nothing when +-- | certain constraints aren't met. +siv' :: BlockCipher k => k -> k -> [B.ByteString] -> B.ByteString -> Maybe B.ByteString +siv' k1 k2 xs m | length xs > bSizeb - 1 = Nothing + | otherwise = Just $ B.append iv $ fst $ ctr' incIV k2 (IV $ sivMask iv) m + where + bSize = fromIntegral $ blockSizeBytes `for` k1 + bSizeb = fromIntegral $ blockSize `for` k1 + iv = cMacStar' k1 $ xs ++ [m] + + +-- |SIV (Synthetic IV) for strict bytestrings +-- |First argument is the optional list of bytestrings to be authenticated +-- | but not encrypted +-- |As required by the specification this algorithm may return nothing when +-- | authentication fails +unSiv' :: BlockCipher k => k -> k -> [B.ByteString] -> B.ByteString -> Maybe B.ByteString +unSiv' k1 k2 xs c | length xs > bSizeb - 1 = Nothing + | B.length c < bSize = Nothing + | iv /= (cMacStar' k1 $ xs ++ [dm]) = Nothing + | otherwise = Just dm + where + bSize = fromIntegral $ blockSizeBytes `for` k1 + bSizeb = fromIntegral $ blockSize `for` k1 + (iv,m) = B.splitAt bSize c + dm = fst $ unCtr' incIV k2 (IV $ sivMask iv) m + +-- |Increase an `IV` by one +-- |This is way faster than decoding, increasing, encoding +incIV :: BlockCipher k => IV k -> IV k +incIV (IV b) = IV $ snd $ B.mapAccumR (incw) True b + where incw :: Bool -> Word8 -> (Bool, Word8) + incw True w = (w == maxBound, w + 1) + incw False w = (False, w) + +-- |Accumulator based double operation +dblw :: Bool -> (Int,[Int],Bool) -> Word8 -> ((Int,[Int],Bool), Word8) +dblw hb (i,xs,b) w = dblw' hb + where slw True w = (setBit (shift w 1) 0) + slw False w = (clearBit (shift w 1) 0) + cpolyw i [] w = ((i+8,[]),w) + cpolyw i (x:xs) w + | x < i +8 = (\(a,b) -> (a,complementBit b (x-i))) $ cpolyw i xs w + |otherwise = ((i+8,(x:xs)),w) + b' = testBit w 7 + w' = slw b w + ((i',xs'),w'') = cpolyw i xs w' + dblw' False = i'`seq`xs'`seq`w''`seq`((i,xs,b'),w') + dblw' True = ((i',xs',b'),w'') + + +-- |Perform doubling as defined by the CMAC and SIV papers +dblIV :: BlockCipher k => IV k -> IV k +dblIV (IV b) = IV $ dblB b + +-- |Perform doubling as defined by the CMAC and SIV papers +dblB :: B.ByteString -> B.ByteString +dblB b | B.null b = b + | otherwise = snd $ B.mapAccumR (dblw (testBit (B.head b) 7)) (0,cpoly2revlist (B.length b * 8),False) b + +-- |Perform doubling as defined by the CMAC and SIV papers +dblL :: L.ByteString -> L.ByteString +dblL b | L.null b = b + | otherwise = snd $ L.mapAccumR (dblw (testBit (L.head b) 7)) (0,cpoly2revlist (L.length b * 8),False) b + +-- |Cast a bigEndian ByteString into an Integer +decodeB :: B.ByteString -> Integer +decodeB = B.foldl' (\acc w -> (shift acc 8) + toInteger(w)) 0 + +-- |Cast an Integer into a bigEndian ByteString of size k +-- |It will drop the MSBs in case the number is bigger than k and add 00s if it +-- |is smaller +encodeB :: (Ord a,Num a) => a -> Integer -> B.ByteString +encodeB k n = B.pack $ if lr > k then takel (lr - k) r else pad (k - lr) r + where go 0 xs = xs + go n xs = go (shift n (-8)) (fromInteger (n .&. 255) : xs) + pad 0 xs = xs + pad n xs = 0 : pad (n-1) xs + takel 0 xs = xs + takel n (_:xs) = takel (n-1) xs + r = go n [] + lr = genericLength r + +-- |Cast a bigEndian ByteString into an Integer +decodeL :: L.ByteString -> Integer +decodeL = L.foldl' (\acc w -> (shift acc 8) + toInteger(w)) 0 + +-- |Cast an Integer into a bigEndian ByteString of size k +-- |It will drop the MSBs in case the number is bigger than k and add 00s if it +-- |is smaller +encodeL :: (Ord a,Num a) => a -> Integer -> L.ByteString +encodeL k n = L.pack $ if lr > k then takel (lr - k) r else pad (k - lr) r + where go 0 xs = xs + go n xs = go (shift n (-8)) (fromInteger (n .&. 255) : xs) + pad 0 xs = xs + pad n xs = 0 : pad (n-1) xs + takel 0 xs = xs + takel n (_:xs) = takel (n-1) xs + r = go n [] + lr = genericLength r + unfoldK :: (b -> Maybe (a,b)) -> b -> ([a],b) unfoldK f i = case (f i) of @@ -269,6 +521,13 @@ let (as, iF) = unfoldK f i' in (a:as, iF) + +-- |Obtain an `IV` made only of zeroes +zeroIV :: (BlockCipher k) => IV k +zeroIV = iv + where bytes = ivBlockSizeBytes iv + iv = IV $ B.replicate bytes 0 + -- |Obtain an `IV` using the provided CryptoRandomGenerator. getIV :: (BlockCipher k, CryptoRandomGen g) => g -> Either GenError (IV k, g) getIV g = S��lo en crypto-api-0.5.2.new/Crypto: Poly.hs