clientsession 0.9.1.2 → 0.9.2.0
raw patch · 3 files changed
+63/−71 lines, 3 filesdep +cryptonitedep −cipher-aesdep −cprng-aesdep −crypto-randomdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: cryptonite
Dependencies removed: cipher-aes, cprng-aes, crypto-random
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- ChangeLog.md +6/−0
- clientsession.cabal +6/−5
- src/Web/ClientSession.hs +51/−66
ChangeLog.md view
@@ -1,3 +1,9 @@+# ChangeLog for clientsession++## 0.9.2.0++* Migrate crypto-aes and cprng-aes to cryptonite. [#36](https://github.com/yesodweb/clientsession/pull/36)+ ## 0.9.1.2 * Clarify that we're using MIT license
clientsession.cabal view
@@ -1,5 +1,5 @@ name: clientsession-version: 0.9.1.2+version: 0.9.2.0 license: MIT license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>, Felipe Lessa <felipe.lessa@gmail.com>@@ -10,7 +10,7 @@ encoding to avoid any issues with characters. category: Web stability: stable-cabal-version: >= 1.8+cabal-version: >= 1.10 build-type: Simple homepage: http://github.com/yesodweb/clientsession/tree/master extra-source-files: tests/runtests.hs bench.hs ChangeLog.md README.md@@ -20,6 +20,7 @@ default: False executable clientsession-generate+ default-language: Haskell2010 main-is: generate.hs build-depends: base , clientsession@@ -27,6 +28,7 @@ hs-source-dirs: bin library+ default-language: Haskell2010 build-depends: base >=4 && < 5 , bytestring >= 0.9 , cereal >= 0.3@@ -36,9 +38,7 @@ , skein == 1.0.* , base64-bytestring >= 0.1.1.1 , entropy >= 0.2.1- , cprng-aes >= 0.2- , cipher-aes >= 0.1.7- , crypto-random+ , cryptonite >= 0.15 , setenv exposed-modules: Web.ClientSession other-modules: System.LookupEnv@@ -46,6 +46,7 @@ hs-source-dirs: src test-suite runtests+ default-language: Haskell2010 type: exitcode-stdio-1.0 build-depends: base , bytestring >= 0.9
src/Web/ClientSession.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE CPP #-} {-# LANGUAGE PackageImports #-}@@ -61,6 +62,7 @@ import Control.Applicative ((<$>)) import Control.Concurrent (forkIO) import Control.Monad (guard, when)+import Data.Bifunctor (first) import Data.Function (on) #if MIN_VERSION_base(4,7,0)@@ -92,11 +94,12 @@ -- from crypto-api import Crypto.Classes (constTimeEq)-import "crypto-api" Crypto.Random (genSeedLength, reseed)-import Crypto.Types (ByteLength) --- from cipher-aes+-- from cryptonite import qualified Crypto.Cipher.AES as A+import Crypto.Cipher.Types(Cipher(..),BlockCipher(..),makeIV)+import Crypto.Error (eitherCryptoError)+import "cryptonite" Crypto.Random (ChaChaDRG,drgNew,randomBytesGenerate) -- from skein import Crypto.Skein (skeinMAC', Skein_512_256)@@ -104,15 +107,7 @@ -- from entropy import System.Entropy (getEntropy) --- from cprng-aes-#if MIN_VERSION_cprng_aes(0,5,0)-import Crypto.Random.AESCtr (AESRNG, makeSystem)-import "crypto-random" Crypto.Random (cprgGenerate)-#else-import Crypto.Random.AESCtr (AESRNG, makeSystem, genRandomBytes)-#endif - -- | The keys used to store the cookies. We have an AES key used -- to encrypt the cookie and a Skein-MAC-512-256 key used verify -- the authencity and integrity of the cookie. The AES key must@@ -121,11 +116,7 @@ -- -- See also 'getDefaultKey' and 'initKey'. data Key = Key { aesKey ::-#if MIN_VERSION_cipher_aes(0, 2, 0)- !A.AES-#else- !A.Key-#endif+ !A.AES256 -- ^ AES key with 32 bytes. , macKey :: !(S.ByteString -> Skein_512_256) -- ^ Skein-MAC key. Instead of storing the key@@ -182,7 +173,7 @@ -- | Randomly construct a fresh initialization vector. You -- /MUST NOT/ reuse initialization vectors. randomIV :: IO IV-randomIV = aesRNG+randomIV = chaChaRNG -- | The default key file. defaultKeyFile :: FilePath@@ -264,12 +255,13 @@ initKey :: S.ByteString -> Either String Key initKey bs | S.length bs /= 96 = Left $ "Web.ClientSession.initKey: length of " ++ show (S.length bs) ++ " /= 96."-initKey bs = Right $ Key { aesKey = A.initKey preAesKey- , macKey = skeinMAC' preMacKey- , keyRaw = bs- }- where- (preMacKey, preAesKey) = S.splitAt 64 bs+initKey bs = do+ let (preMacKey, preAesKey) = S.splitAt 64 bs+ aesKey <- first show $ eitherCryptoError (cipherInit preAesKey)+ Right $ Key { aesKey+ , macKey = skeinMAC' preMacKey+ , keyRaw = bs+ } -- | Same as 'encrypt', however randomly generates the -- initialization vector for you.@@ -286,16 +278,14 @@ -> S.ByteString -- ^ Serialized cookie data. -> S.ByteString -- ^ Encoded cookie data to be given to -- the client browser.-encrypt key (IV iv) x = B.encode final- where-#if MIN_VERSION_cipher_aes(0, 2, 0)- encrypted = A.encryptCTR (aesKey key) iv x-#else- encrypted = A.encryptCTR (aesKey key) (A.IV iv) x-#endif- toBeAuthed = iv `S.append` encrypted- auth = macKey key toBeAuthed- final = encode auth `S.append` toBeAuthed+encrypt key (IV b) x = case makeIV b of+ Nothing -> error "Web.ClientSession.encrypt: Failed to makeIV"+ Just iv -> B.encode final+ where+ encrypted = ctrCombine (aesKey key) iv x+ toBeAuthed = b `S.append` encrypted+ auth = macKey key toBeAuthed+ final = encode auth `S.append` toBeAuthed -- | Decode (Base64), verify the integrity and authenticity -- (Skein-MAC-512-256) and decrypt (AES-CTR) the given encoded@@ -311,58 +301,53 @@ auth' = macKey key toBeAuthed guard (encode auth' `constTimeEq` auth) let (iv, encrypted) = S.splitAt 16 toBeAuthed-#if MIN_VERSION_cipher_aes(0, 2, 0)- let iv' = iv-#else- let iv' = A.IV iv-#endif- return $! A.decryptCTR (aesKey key) iv' encrypted+ iv' <- makeIV iv+ return $! ctrCombine (aesKey key) iv' encrypted +-- [from when the code used cprng-aes.AESRNG] -- Significantly more efficient random IV generation. Initial -- benchmarks placed it at 6.06 us versus 1.69 ms for -- Crypto.Modes.getIVIO, since it does not require /dev/urandom -- I/O for every call. -data AESState =- ASt {-# UNPACK #-} !AESRNG -- Our CPRNG using AES on CTR mode- {-# UNPACK #-} !Int -- How many IVs were generated with this- -- AESRNG. Used to control reseeding.+-- [now with cryptonite.ChaChaDRG]+-- I haven't run any benchmark; this conversion is a case of “code+-- that doesn't crash trumps performance.” +data ChaChaState =+ CCSt {-# UNPACK #-} !ChaChaDRG -- Our CPRNG using ChaCha+ {-# UNPACK #-} !Int -- How many IVs were generated with this+ -- CPRNG. Used to control reseeding.+ -- | Construct initial state of the CPRNG.-aesSeed :: IO AESState-aesSeed = do- rng <- makeSystem- return $! ASt rng 0+chaChaSeed :: IO ChaChaState+chaChaSeed = do+ drg <- drgNew+ return $! CCSt drg 0 -- | Reseed the CPRNG with new entropy from the system pool.-aesReseed :: IO ()-aesReseed = do- rng' <- makeSystem- I.writeIORef aesRef $ ASt rng' 0+chaChaReseed :: IO ()+chaChaReseed = do+ drg' <- drgNew+ I.writeIORef chaChaRef $ CCSt drg' 0 -- | 'IORef' that keeps the current state of the CPRNG. Yep, -- global state. Used in thread-safe was only, though.-aesRef :: I.IORef AESState-aesRef = unsafePerformIO $ aesSeed >>= I.newIORef-{-# NOINLINE aesRef #-}+chaChaRef :: I.IORef ChaChaState+chaChaRef = unsafePerformIO $ chaChaSeed >>= I.newIORef+{-# NOINLINE chaChaRef #-} -- | Construct a new 16-byte IV using our CPRNG. Forks another -- thread to reseed the CPRNG should its usage count reach a -- hardcoded threshold.-aesRNG :: IO IV-aesRNG = do+chaChaRNG :: IO IV+chaChaRNG = do (bs, count) <-- I.atomicModifyIORef aesRef $ \(ASt rng count) ->-#if MIN_VERSION_cprng_aes(0, 5, 0)- let (bs', rng') = cprgGenerate 16 rng-#elif MIN_VERSION_cprng_aes(0, 3, 2)- let (bs', rng') = genRandomBytes 16 rng-#else- let (bs', rng') = genRandomBytes rng 16-#endif- in (ASt rng' (succ count), (bs', count))- when (count == threshold) $ void $ forkIO aesReseed+ I.atomicModifyIORef chaChaRef $ \(CCSt drg count) ->+ let (bs', drg') = randomBytesGenerate 16 drg+ in (CCSt drg' (succ count), (bs', count))+ when (count == threshold) $ void $ forkIO chaChaReseed return $! unsafeMkIV bs where void f = f >> return ()