crypto-api 0.6.2 → 0.6.2.1
raw patch · 6 files changed
+144/−107 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- Crypto/Classes.hs +5/−0
- Crypto/Modes.hs +66/−62
- Crypto/Padding.hs +19/−15
- Crypto/Random.hs +49/−29
- Crypto/Types.hs +4/−0
- crypto-api.cabal +1/−1
Crypto/Classes.hs view
@@ -132,9 +132,11 @@ buildKey :: B.ByteString -> Maybe k -- ^ smart constructor for keys from a bytestring. keyLength :: Tagged k BitLength -- ^ length of the cryptographic key +-- |The number of bytes in a block cipher block blockSizeBytes :: (BlockCipher k) => Tagged k ByteLength blockSizeBytes = fmap (`div` 8) blockSize +-- |Build a symmetric key using the system entropy (see 'System.Crypto.Random') buildKeyIO :: (BlockCipher k) => IO k buildKeyIO = go 0 where@@ -157,6 +159,7 @@ publicKeyLength :: p -> BitLength privateKeyLength :: v -> BitLength +-- |Build a pair of asymmetric keys using the system random generator. buildKeyPairIO :: AsymCipher p v => BitLength -> IO (Either GenError (p,v)) buildKeyPairIO bl = do g <- newGenIO :: IO SystemRandom@@ -173,6 +176,7 @@ decryptStream :: k -> iv -> B.ByteString -> (B.ByteString, iv) streamKeyLength :: Tagged k BitLength +-- |Build a stream key using the system random generator buildStreamKeyIO :: StreamCipher k iv => IO k buildStreamKeyIO = go 0 where@@ -196,6 +200,7 @@ signingKeyLength :: v -> BitLength verifyingKeyLength :: p -> BitLength +-- |Build a signing key using the system random generator buildSigningKeyPairIO :: (Signing p v) => BitLength -> IO (Either GenError (p,v)) buildSigningKeyPairIO bl = do g <- newGenIO :: IO SystemRandom
Crypto/Modes.hs view
@@ -5,12 +5,12 @@ Portability: portable Authors: Thomas DuBuisson, Francisco Blas Izquierdo Riera (klondike) - Generic mode implementations useable by any correct BlockCipher instance - - Be aware there are no tests for CFB mode yet. See "Test.Crypto".++ Generic mode implementations useable by any correct BlockCipher+instance Be aware there are no tests for CFB mode yet. See+'Test.Crypto'. -}-module Crypto.Modes- (+module Crypto.Modes ( -- * Initialization Vector Type, Modifiers (for all ciphers, all modes that use IVs) IV , getIV, getIVIO, zeroIV@@ -55,13 +55,14 @@ import Data.Proxy #endif --- |Initilization Vectors for BlockCipher implementations (IV k) are used--- for various modes and guarrenteed to be blockSize bits long. The common--- ways to obtain an IV are to generate one ('getIV' or 'getIVIO') or to--- use one provided with the ciphertext (using the 'Serialize' instance of IV).+-- |Initilization Vectors for BlockCipher implementations (IV k) are+-- used for various modes and guarrenteed to be blockSize bits long.+-- The common ways to obtain an IV are to generate one ('getIV' or+-- 'getIVIO') or to use one provided with the ciphertext (using the+-- 'Serialize' instance of IV). ----- 'zeroIV' also exists and is of particular use for starting 'ctr' mode with--- a fresh key.+-- 'zeroIV' also exists and is of particular use for starting 'ctr'+-- mode with a fresh key. data IV k = IV { initializationVector :: {-# UNPACK #-} !B.ByteString } deriving (Eq, Ord, Show) -- gather a specified number of bytes from the list of bytestrings@@ -93,10 +94,11 @@ {-# INLINE chunkFor' #-} -- |zipWith xor + Pack--- This is written intentionally to take advantage of the bytestring--- libraries 'zipWith'' rewrite rule but at the extra cost of the--- resulting lazy bytestring being more fragmented than either of the--- two inputs.+-- +-- This is written intentionally to take advantage+-- of the bytestring libraries 'zipWith'' rewrite rule but at the+-- extra cost of the resulting lazy bytestring being more fragmented+-- than either of the two inputs. zwp :: L.ByteString -> L.ByteString -> L.ByteString zwp a b = let as = L.toChunks a@@ -115,8 +117,10 @@ {-# INLINEABLE zwp #-} -- |zipWith xor + Pack--- As a result of rewrite rules, this should automatically be optimized (at compile time) --- to use the bytestring libraries 'zipWith'' function.+--+-- As a result of rewrite rules, this should automatically be+-- optimized (at compile time) to use the bytestring libraries+-- 'zipWith'' function. zwp' :: B.ByteString -> B.ByteString -> B.ByteString zwp' a = B.pack . B.zipWith xor a {-# INLINEABLE zwp' #-}@@ -209,8 +213,8 @@ 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+-- |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@@ -218,8 +222,9 @@ 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+-- |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))@@ -264,7 +269,7 @@ 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)))+ | otherwise = Just (0,((n-1),(x:xs))) -- |Obtain the CMAC* on lazy bytestrings cMacStar :: BlockCipher k => k -> [L.ByteString] -> L.ByteString@@ -298,44 +303,41 @@ 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 (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+ | 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+-- |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+ | 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 (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+ | otherwise = Just $ B.append iv $ fst $ ctr' incIV k2 (IV $ sivMask iv) m where bSize = fromIntegral $ blockSizeBytes `for` k1 bSizeb = fromIntegral $ blockSize `for` k1@@ -343,24 +345,23 @@ --- |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+-- |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+ | 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 +-- |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@@ -403,9 +404,9 @@ 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+-- |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@@ -422,9 +423,9 @@ 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+-- |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 @@ -468,7 +469,8 @@ in B.concat $ map (decryptBlock k) chunks {-# INLINEABLE unEcb' #-} --- |Ciphertext feed-back encryption mode for lazy bytestrings (with s == blockSize)+-- |Ciphertext feed-back encryption mode for lazy bytestrings (with s+-- == blockSize) cfb :: BlockCipher k => k -> IV k -> L.ByteString -> (L.ByteString, IV k) cfb k (IV v) msg = let blks = chunkFor k msg@@ -482,7 +484,8 @@ in (c:cs, ivFinal) {-# INLINEABLE cfb #-} --- |Ciphertext feed-back decryption mode for lazy bytestrings (with s == blockSize)+-- |Ciphertext feed-back decryption mode for lazy bytestrings (with s+-- == blockSize) unCfb :: BlockCipher k => k -> IV k -> L.ByteString -> (L.ByteString, IV k) unCfb k (IV v) msg = let blks = chunkFor k msg@@ -496,7 +499,8 @@ in (p:ps, ivF) {-# INLINEABLE unCfb #-} --- |Ciphertext feed-back encryption mode for strict bytestrings (with s == blockSize)+-- |Ciphertext feed-back encryption mode for strict bytestrings (with+-- s == blockSize) cfb' :: BlockCipher k => k -> IV k -> B.ByteString -> (B.ByteString, IV k) cfb' k (IV v) msg = let blks = chunkFor' k msg@@ -567,7 +571,7 @@ | otherwise -> Left (GenErrorOther "Generator failed to provide requested number of bytes") {-# INLINEABLE getIV #-} --- | Obtain an `IV` using the system entropy (see "System.Crypto.Random")+-- | Obtain an 'IV' using the system entropy (see 'System.Crypto.Random') getIVIO :: (BlockCipher k) => IO (IV k) getIVIO = do let p = Proxy
Crypto/Padding.hs view
@@ -1,15 +1,17 @@ {-|- - Maintainer: Thomas.DuBuisson@gmail.com- - Stability: beta- - Portability: portable - -- - PKCS5 (RFC 1423) and IPSec ESP (RFC 4303) padding methods are implemented both- - as trivial functions operating on bytestrings and as 'Put' routines usable from- - the "Data.Serialize" module. These methods do not work for algorithms or pad- - sizes in excess of 255 bytes (2040 bits, so extremely large as far as cipher needs- - are concerned).- -}+ Maintainer: Thomas.DuBuisson@gmail.com+ Stability: beta+ Portability: portable +PKCS5 (RFC 1423) and IPSec ESP (RFC 4303)+padding methods are implemented both as trivial functions operating on+bytestrings and as 'Put' routines usable from the "Data.Serialize"+module. These methods do not work for algorithms or pad sizes in+excess of 255 bytes (2040 bits, so extremely large as far as cipher+needs are concerned).++-}+ module Crypto.Padding ( -- * PKCS5 (RFC 1423) based [un]padding routines@@ -35,13 +37,14 @@ padPKCS5 :: ByteLength -> B.ByteString -> B.ByteString padPKCS5 len bs = runPut $ putPaddedPKCS5 len bs --- |+-- | Ex:+-- -- @ -- putPaddedPKCS5 m bs -- @ ----- Will pad out @bs@ to a byte multiple--- of @m@ and put both the bytestring and it's padding via 'Put'+-- Will pad out `bs` to a byte multiple+-- of `m` and put both the bytestring and it's padding via 'Put' -- (this saves on copying if you are already using Cereal). putPaddedPKCS5 :: ByteLength -> B.ByteString -> Put putPaddedPKCS5 0 bs = putByteString bs >> putWord8 1@@ -74,7 +77,8 @@ pLen = fromIntegral padLen (msg,pad) = B.splitAt (bsLen - pLen) bs --- |unpad a strict bytestring without checking the pad bytes and length any more than necessary.+-- |unpad a strict bytestring without checking the pad bytes and+-- length any more than necessary. unpadPKCS5 :: B.ByteString -> B.ByteString unpadPKCS5 bs = if bsLen == 0 then bs else msg where@@ -130,7 +134,7 @@ padLen = l - ((B.length bs + 1) `rem` l) pLen = fromIntegral padLen --- A static espPad allows reuse of a single B.pack'ed pad for all calls to padESP+-- |A static espPad allows reuse of a single B.pack'ed pad for all calls to padESP espPad = B.pack [1..255] -- | unpad and return the padded message (Nothing is returned if the padding is invalid)
Crypto/Random.hs view
@@ -5,8 +5,10 @@ Portability: portable - This module is for instantiating cryptographically strong determinitic random bit generators (DRBGs, aka PRNGs)- For the simple use case of using the system random number generator ('System.Crypto.Random') to seed the DRBG:+ This module is for instantiating cryptographically strong+determinitic random bit generators (DRBGs, aka PRNGs) For the simple+use case of using the system random number generator+('System.Crypto.Random') to seed the DRBG: @ g <- newGenIO @@@ -54,33 +56,42 @@ | NeedsInfiniteSeed -- ^ This generator can not be instantiated or reseeded with a finite seed (ex: 'SystemRandom') deriving (Eq, Ord, Show) --- |A class of random bit generators that allows for the possibility of failure,--- reseeding, providing entropy at the same time as requesting bytes+-- |A class of random bit generators that allows for the possibility+-- of failure, reseeding, providing entropy at the same time as+-- requesting bytes ----- Minimum complete definition: `newGen`, `genSeedLength`, `genBytes`, `reseed`.+-- Minimum complete definition: `newGen`, `genSeedLength`, `genBytes`,+-- `reseed`. class CryptoRandomGen g where- -- |Instantiate a new random bit generator. The provided bytestring should- -- be of length >= genSeedLength. If the bytestring is shorter- -- then the call may fail (suggested error: `NotEnoughEntropy`). If the- -- bytestring is of sufficent length the call should always succeed.+ -- |Instantiate a new random bit generator. The provided+ -- bytestring should be of length >= genSeedLength. If the+ -- bytestring is shorter then the call may fail (suggested+ -- error: `NotEnoughEntropy`). If the bytestring is of+ -- sufficent length the call should always succeed. newGen :: B.ByteString -> Either GenError g - -- |Length of input entropy necessary to instantiate or reseed a generator+ -- |Length of input entropy necessary to instantiate or reseed+ -- a generator genSeedLength :: Tagged g ByteLength - -- | @genBytes len g@ generates a random ByteString of length @len@ and new generator.- -- The "MonadCryptoRandom" package has routines useful for converting the ByteString- -- to commonly needed values (but "cereal" or other deserialization libraries would also work).+ -- | @genBytes len g@ generates a random ByteString of length+ -- @len@ and new generator. The "MonadCryptoRandom" package+ -- has routines useful for converting the ByteString to+ -- commonly needed values (but "cereal" or other+ -- deserialization libraries would also work). --- -- This routine can fail if the generator has gone too long without a reseed (usually this- -- is in the ball-park of 2^48 requests). Suggested error in this cases is `NeedReseed`+ -- This routine can fail if the generator has gone too long+ -- without a reseed (usually this is in the ball-park of 2^48+ -- requests). Suggested error in this cases is `NeedReseed` genBytes :: ByteLength -> g -> Either GenError (B.ByteString, g) - -- |@genBytesWithEntropy g i entropy@ generates @i@ random bytes and use the- -- additional input @entropy@ in the generation of the requested data to- -- increase the confidence our generated data is a secure random stream.+ -- |@genBytesWithEntropy g i entropy@ generates @i@ random+ -- bytes and use the additional input @entropy@ in the+ -- generation of the requested data to increase the confidence+ -- our generated data is a secure random stream. --- -- Some generators use @entropy@ to perturb the state of the generator, meaning:+ -- Some generators use @entropy@ to perturb the state of the+ -- generator, meaning: -- -- @ -- (_,g2') <- genBytesWithEntropy len g1 ent@@ -104,13 +115,16 @@ let entropy' = B.append entropy (B.replicate (len - B.length entropy) 0) in Right (zwp' entropy' bs, g') - -- |If the generator has produced too many random bytes on its existing seed- -- it will throw `NeedReseed`. In that case, reseed the generator using this function and- -- a new high-entropy seed of length >= `genSeedLength`. Using bytestrings that are too short- -- can result in an error (`NotEnoughEntropy`).+ -- |If the generator has produced too many random bytes on its+ -- existing seed it will throw `NeedReseed`. In that case,+ -- reseed the generator using this function and a new+ -- high-entropy seed of length >= `genSeedLength`. Using+ -- bytestrings that are too short can result in an error+ -- (`NotEnoughEntropy`). reseed :: B.ByteString -> g -> Either GenError g - -- |By default this uses "System.Crypto.Random" to obtain entropy for `newGen`.+ -- |By default this uses "System.Crypto.Random" to obtain+ -- entropy for `newGen`. newGenIO :: IO g newGenIO = go 0 where@@ -134,8 +148,10 @@ return (bs:more) liftM (SysRandom . L.fromChunks) getBS --- |Not that it is technically correct as an instance of 'CryptoRandomGen', but simply because--- it's a reasonable engineering choice here is a CryptoRandomGen which streams the system randoms. Take note:+-- |Not that it is technically correct as an instance of+-- 'CryptoRandomGen', but simply because it's a reasonable engineering+-- choice here is a CryptoRandomGen which streams the system+-- randoms. Take note: -- -- * It uses the default definition of 'genByteWithEntropy' --@@ -144,6 +160,7 @@ -- * 'reseed' will always fail! -- -- * the handle to the system random is never closed+-- data SystemRandom = SysRandom L.ByteString instance CryptoRandomGen SystemRandom where@@ -159,9 +176,11 @@ reseed _ _ = Left NeedsInfiniteSeed newGenIO = getSystemGen --- |While the safety and wisdom of a splitting function depends on the properties of the generator being split,--- several arguments from informed people indicate such a function is safe for NIST SP 800-90 generators.--- (see libraries@haskell.org discussion ~ Sept, Oct 2010)+-- | While the safety and wisdom of a splitting function depends on the+-- properties of the generator being split, several arguments from+-- informed people indicate such a function is safe for NIST SP 800-90+-- generators. (see libraries\@haskell.org discussion around Sept, Oct+-- 2010) splitGen :: CryptoRandomGen g => g -> Either GenError (g,g) splitGen g = do let e = genBytes (genSeedLength `for` g) g@@ -176,6 +195,7 @@ for :: Tagged a b -> a -> b for t _ = unTagged t +-- |Helper function to convert bytestrings to integers bs2i :: B.ByteString -> Integer bs2i bs = B.foldl' (\i b -> (i `shiftL` 8) + fromIntegral b) 0 bs {-# INLINE bs2i #-}
Crypto/Types.hs view
@@ -1,7 +1,11 @@+-- |Type aliases used throughout the crypto-api modules. module Crypto.Types where import Data.ByteString as B import Data.ByteString.Lazy as L +-- |The length of a field (usually a ByteString) in bits type BitLength = Int++-- |The length fo a field in bytes. type ByteLength = Int
crypto-api.cabal view
@@ -1,5 +1,5 @@ name: crypto-api-version: 0.6.2+version: 0.6.2.1 license: BSD3 license-file: LICENSE copyright: Thomas DuBuisson <thomas.dubuisson@gmail.com>, Francisco Blas Izquierdo Riera (klondike) (see AUTHORS)