packages feed

cryptonite 0.27 → 0.28

raw patch · 29 files changed

+490/−10 lines, 29 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Crypto.Hash: hashFinalizePrefix :: forall a ba. (HashAlgorithmPrefix a, ByteArrayAccess ba) => Context a -> ba -> Int -> Digest a
+ Crypto.Hash: hashPrefix :: (ByteArrayAccess ba, HashAlgorithmPrefix a) => ba -> Int -> Digest a
+ Crypto.Hash: hashPrefixWith :: (ByteArrayAccess ba, HashAlgorithmPrefix alg) => alg -> ba -> Int -> Digest alg
+ Crypto.Hash.Algorithms: class HashAlgorithm a => HashAlgorithmPrefix a
- Crypto.Cipher.Types: AEAD :: AEADModeImpl st -> st -> AEAD cipher
+ Crypto.Cipher.Types: AEAD :: AEADModeImpl st -> !st -> AEAD cipher
- Crypto.Cipher.Types: [aeadState] :: AEAD cipher -> st
+ Crypto.Cipher.Types: [aeadState] :: AEAD cipher -> !st
- Crypto.Hash.Algorithms: Blake2b :: Blake2b
+ Crypto.Hash.Algorithms: Blake2b :: Blake2b (bitlen :: Nat)
- Crypto.Hash.Algorithms: Blake2bp :: Blake2bp
+ Crypto.Hash.Algorithms: Blake2bp :: Blake2bp (bitlen :: Nat)
- Crypto.Hash.Algorithms: Blake2s :: Blake2s
+ Crypto.Hash.Algorithms: Blake2s :: Blake2s (bitlen :: Nat)
- Crypto.Hash.Algorithms: Blake2sp :: Blake2sp
+ Crypto.Hash.Algorithms: Blake2sp :: Blake2sp (bitlen :: Nat)
- Crypto.Hash.Algorithms: SHAKE128 :: SHAKE128
+ Crypto.Hash.Algorithms: SHAKE128 :: SHAKE128 (bitlen :: Nat)
- Crypto.Hash.Algorithms: SHAKE256 :: SHAKE256
+ Crypto.Hash.Algorithms: SHAKE256 :: SHAKE256 (bitlen :: Nat)
- Crypto.Number.Nat: isAtLeast :: (KnownNat value, KnownNat bound) => proxy value -> proxy' bound -> Maybe ((bound <=? value) :~: 'True)
+ Crypto.Number.Nat: isAtLeast :: (KnownNat value, KnownNat bound) => proxy value -> proxy' bound -> Maybe ((bound <=? value) :~: 'True)
- Crypto.Number.Nat: isAtMost :: (KnownNat value, KnownNat bound) => proxy value -> proxy' bound -> Maybe ((value <=? bound) :~: 'True)
+ Crypto.Number.Nat: isAtMost :: (KnownNat value, KnownNat bound) => proxy value -> proxy' bound -> Maybe ((value <=? bound) :~: 'True)
- Crypto.Number.Nat: isDivisibleBy8 :: KnownNat n => proxy n -> Maybe (IsDiv8 n n :~: 'True)
+ Crypto.Number.Nat: isDivisibleBy8 :: KnownNat n => proxy n -> Maybe (IsDiv8 n n :~: 'True)
- Crypto.Number.Nat: type IsAtLeast (bitlen :: Nat) (n :: Nat) = IsGE bitlen n (n <=? bitlen) ~ 'True
+ Crypto.Number.Nat: type IsAtLeast (bitlen :: Nat) (n :: Nat) = IsGE bitlen n (n <=? bitlen) ~ 'True
- Crypto.Number.Nat: type IsAtMost (bitlen :: Nat) (n :: Nat) = IsLE bitlen n (bitlen <=? n) ~ 'True
+ Crypto.Number.Nat: type IsAtMost (bitlen :: Nat) (n :: Nat) = IsLE bitlen n (bitlen <=? n) ~ 'True
- Crypto.Number.Nat: type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True
+ Crypto.Number.Nat: type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+## 0.28++* Add hash constant time capability+* Prevent possible overflow during hashing by hashing in 4GB chunks+ ## 0.27  * Optimise AES GCM and CCM
Crypto/Cipher/RC4.hs view
@@ -30,6 +30,11 @@ import           Crypto.Internal.Imports  -- | The encryption state for RC4+--+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal+-- layout is architecture dependent, may contain uninitialized data fragments,+-- and change in future versions.  The bytearray should not be used as input to+-- cryptographic algorithms. newtype State = State ScrubbedBytes     deriving (ByteArrayAccess,NFData) 
Crypto/Cipher/Types/AEAD.hs view
@@ -27,24 +27,24 @@ -- | Authenticated Encryption with Associated Data algorithms data AEAD cipher = forall st . AEAD     { aeadModeImpl :: AEADModeImpl st-    , aeadState    :: st+    , aeadState    :: !st     }  -- | Append some header information to an AEAD context aeadAppendHeader :: ByteArrayAccess aad => AEAD cipher -> aad -> AEAD cipher-aeadAppendHeader (AEAD impl st) aad = AEAD impl $ (aeadImplAppendHeader impl) st aad+aeadAppendHeader (AEAD impl st) aad = AEAD impl $ aeadImplAppendHeader impl st aad  -- | Encrypt some data and update the AEAD context aeadEncrypt :: ByteArray ba => AEAD cipher -> ba -> (ba, AEAD cipher)-aeadEncrypt (AEAD impl st) ba = second (AEAD impl) $ (aeadImplEncrypt impl) st ba+aeadEncrypt (AEAD impl st) ba = second (AEAD impl) $ aeadImplEncrypt impl st ba  -- | Decrypt some data and update the AEAD context aeadDecrypt :: ByteArray ba => AEAD cipher -> ba -> (ba, AEAD cipher)-aeadDecrypt (AEAD impl st) ba = second (AEAD impl) $ (aeadImplDecrypt impl) st ba+aeadDecrypt (AEAD impl st) ba = second (AEAD impl) $ aeadImplDecrypt impl st ba  -- | Finalize the AEAD context and return the authentication tag aeadFinalize :: AEAD cipher -> Int -> AuthTag-aeadFinalize (AEAD impl st) n = (aeadImplFinalize impl) st n+aeadFinalize (AEAD impl st) = aeadImplFinalize impl st  -- | Simple AEAD encryption aeadSimpleEncrypt :: (ByteArrayAccess aad, ByteArray ba)
Crypto/Hash.hs view
@@ -28,14 +28,17 @@     -- * Hash methods parametrized by algorithm     , hashInitWith     , hashWith+    , hashPrefixWith     -- * Hash methods     , hashInit     , hashUpdates     , hashUpdate     , hashFinalize+    , hashFinalizePrefix     , hashBlockSize     , hashDigestSize     , hash+    , hashPrefix     , hashlazy     -- * Hash algorithms     , module Crypto.Hash.Algorithms@@ -47,16 +50,20 @@ import           Crypto.Internal.Compat (unsafeDoIO) import           Crypto.Hash.Types import           Crypto.Hash.Algorithms-import           Foreign.Ptr (Ptr)+import           Foreign.Ptr (Ptr, plusPtr) import           Crypto.Internal.ByteArray (ByteArrayAccess) import qualified Crypto.Internal.ByteArray as B import qualified Data.ByteString.Lazy as L-import           Data.Word (Word8)+import           Data.Word (Word8, Word32)  -- | Hash a strict bytestring into a digest. hash :: (ByteArrayAccess ba, HashAlgorithm a) => ba -> Digest a hash bs = hashFinalize $ hashUpdate hashInit bs +-- | Hash the first N bytes of a bytestring, with code path independent from N.+hashPrefix :: (ByteArrayAccess ba, HashAlgorithmPrefix a) => ba -> Int -> Digest a+hashPrefix = hashFinalizePrefix hashInit+ -- | Hash a lazy bytestring into a digest. hashlazy :: HashAlgorithm a => L.ByteString -> Digest a hashlazy lbs = hashFinalize $ hashUpdates hashInit (L.toChunks lbs)@@ -81,9 +88,17 @@ hashUpdates c l     | null ls   = c     | otherwise = Context $ B.copyAndFreeze c $ \(ctx :: Ptr (Context a)) ->-        mapM_ (\b -> B.withByteArray b $ \d -> hashInternalUpdate ctx d (fromIntegral $ B.length b)) ls+        mapM_ (\b -> B.withByteArray b (processBlocks ctx (B.length b))) ls   where     ls = filter (not . B.null) l+    -- process the data in 4GB chunks to fit in uint32_t+    processBlocks ctx bytesLeft dataPtr+        | bytesLeft == 0 = return ()+        | otherwise = do+            hashInternalUpdate ctx dataPtr (fromIntegral actuallyProcessed)+            processBlocks ctx (bytesLeft - actuallyProcessed) (dataPtr `plusPtr` actuallyProcessed)+        where+            actuallyProcessed = min bytesLeft (fromIntegral (maxBound :: Word32))  -- | Finalize a context and return a digest. hashFinalize :: forall a . HashAlgorithm a@@ -94,6 +109,24 @@         ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) -> hashInternalFinalize ctx dig         return () +-- | Update the context with the first N bytes of a bytestring and return the+-- digest.  The code path is independent from N but much slower than a normal+-- 'hashUpdate'.  The function can be called for the last bytes of a message, in+-- order to exclude a variable padding, without leaking the padding length.  The+-- begining of the message, never impacted by the padding, should preferably go+-- through 'hashUpdate' for better performance.+hashFinalizePrefix :: forall a ba . (HashAlgorithmPrefix a, ByteArrayAccess ba)+                   => Context a+                   -> ba+                   -> Int+                   -> Digest a+hashFinalizePrefix !c b len =+    Digest $ B.allocAndFreeze (hashDigestSize (undefined :: a)) $ \(dig :: Ptr (Digest a)) -> do+        ((!_) :: B.Bytes) <- B.copy c $ \(ctx :: Ptr (Context a)) ->+            B.withByteArray b $ \d ->+                hashInternalFinalizePrefix ctx d (fromIntegral $ B.length b) (fromIntegral len) dig+        return ()+ -- | Initialize a new context for a specified hash algorithm hashInitWith :: HashAlgorithm alg => alg -> Context alg hashInitWith _ = hashInit@@ -101,6 +134,10 @@ -- | Run the 'hash' function but takes an explicit hash algorithm parameter hashWith :: (ByteArrayAccess ba, HashAlgorithm alg) => alg -> ba -> Digest alg hashWith _ = hash++-- | Run the 'hashPrefix' function but takes an explicit hash algorithm parameter+hashPrefixWith :: (ByteArrayAccess ba, HashAlgorithmPrefix alg) => alg -> ba -> Int -> Digest alg+hashPrefixWith _ = hashPrefix  -- | Try to transform a bytearray into a Digest of specific algorithm. --
Crypto/Hash/Algorithms.hs view
@@ -9,6 +9,7 @@ -- module Crypto.Hash.Algorithms     ( HashAlgorithm+    , HashAlgorithmPrefix     -- * Hash algorithms     , Blake2s_160(..)     , Blake2s_224(..)@@ -54,7 +55,7 @@     , Whirlpool(..)     ) where -import           Crypto.Hash.Types (HashAlgorithm)+import           Crypto.Hash.Types (HashAlgorithm, HashAlgorithmPrefix) import           Crypto.Hash.Blake2s import           Crypto.Hash.Blake2sp import           Crypto.Hash.Blake2b
Crypto/Hash/IO.hs view
@@ -24,6 +24,11 @@ import           Foreign.Ptr  -- | A Mutable hash context+--+-- This type is an instance of 'B.ByteArrayAccess' for debugging purpose.+-- Internal layout is architecture dependent, may contain uninitialized data+-- fragments, and change in future versions.  The bytearray should not be used+-- as input to cryptographic algorithms. newtype MutableContext a = MutableContext B.Bytes     deriving (B.ByteArrayAccess) 
Crypto/Hash/MD5.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_md5_update     hashInternalFinalize      = c_md5_finalize +instance HashAlgorithmPrefix MD5 where+    hashInternalFinalizePrefix = c_md5_finalize_prefix+ foreign import ccall unsafe "cryptonite_md5_init"     c_md5_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_md5_finalize"     c_md5_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_md5_finalize_prefix"+    c_md5_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/SHA1.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_sha1_update     hashInternalFinalize      = c_sha1_finalize +instance HashAlgorithmPrefix SHA1 where+    hashInternalFinalizePrefix = c_sha1_finalize_prefix+ foreign import ccall unsafe "cryptonite_sha1_init"     c_sha1_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_sha1_finalize"     c_sha1_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_sha1_finalize_prefix"+    c_sha1_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/SHA224.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_sha224_update     hashInternalFinalize      = c_sha224_finalize +instance HashAlgorithmPrefix SHA224 where+    hashInternalFinalizePrefix = c_sha224_finalize_prefix+ foreign import ccall unsafe "cryptonite_sha224_init"     c_sha224_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_sha224_finalize"     c_sha224_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_sha224_finalize_prefix"+    c_sha224_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/SHA256.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_sha256_update     hashInternalFinalize      = c_sha256_finalize +instance HashAlgorithmPrefix SHA256 where+    hashInternalFinalizePrefix = c_sha256_finalize_prefix+ foreign import ccall unsafe "cryptonite_sha256_init"     c_sha256_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_sha256_finalize"     c_sha256_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_sha256_finalize_prefix"+    c_sha256_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/SHA384.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_sha384_update     hashInternalFinalize      = c_sha384_finalize +instance HashAlgorithmPrefix SHA384 where+    hashInternalFinalizePrefix = c_sha384_finalize_prefix+ foreign import ccall unsafe "cryptonite_sha384_init"     c_sha384_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_sha384_finalize"     c_sha384_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_sha384_finalize_prefix"+    c_sha384_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/SHA512.hs view
@@ -34,6 +34,9 @@     hashInternalUpdate        = c_sha512_update     hashInternalFinalize      = c_sha512_finalize +instance HashAlgorithmPrefix SHA512 where+    hashInternalFinalizePrefix = c_sha512_finalize_prefix+ foreign import ccall unsafe "cryptonite_sha512_init"     c_sha512_init :: Ptr (Context a)-> IO () @@ -42,3 +45,6 @@  foreign import ccall unsafe "cryptonite_sha512_finalize"     c_sha512_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()++foreign import ccall "cryptonite_sha512_finalize_prefix"+    c_sha512_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()
Crypto/Hash/Types.hs view
@@ -14,6 +14,7 @@ {-# LANGUAGE TypeFamilies #-} module Crypto.Hash.Types     ( HashAlgorithm(..)+    , HashAlgorithmPrefix(..)     , Context(..)     , Digest(..)     ) where@@ -59,12 +60,28 @@     -- | Finalize the context and set the digest raw memory to the right value     hashInternalFinalize :: Ptr (Context a) -> Ptr (Digest a) -> IO () +-- | Hashing algorithms with a constant-time implementation.+class HashAlgorithm a => HashAlgorithmPrefix a where+    -- | Update the context with the first N bytes of a buffer and finalize this+    -- context.  The code path executed is independent from N and depends only+    -- on the complete buffer length.+    hashInternalFinalizePrefix :: Ptr (Context a)+                               -> Ptr Word8 -> Word32+                               -> Word32+                               -> Ptr (Digest a)+                               -> IO ()+ {- hashContextGetAlgorithm :: HashAlgorithm a => Context a -> a hashContextGetAlgorithm = undefined -}  -- | Represent a context for a given hash algorithm.+--+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal+-- layout is architecture dependent, may contain uninitialized data fragments,+-- and change in future versions.  The bytearray should not be used as input to+-- cryptographic algorithms. newtype Context a = Context Bytes     deriving (ByteArrayAccess,NFData) 
Crypto/MAC/Poly1305.hs view
@@ -33,6 +33,11 @@ import           Crypto.Error  -- | Poly1305 State+--+-- This type is an instance of 'ByteArrayAccess' for debugging purpose. Internal+-- layout is architecture dependent, may contain uninitialized data fragments,+-- and change in future versions.  The bytearray should not be used as input to+-- cryptographic algorithms. newtype State = State ScrubbedBytes     deriving (ByteArrayAccess) 
Crypto/Random.hs view
@@ -84,6 +84,9 @@ -- Note that the @Arbitrary@ instance provided by QuickCheck for 'Word64' does -- not have a uniform distribution.  It is often better to use instead -- @arbitraryBoundedRandom@.+--+-- System endianness impacts how the tuple is interpreted and therefore changes+-- the resulting DRG. drgNewTest :: (Word64, Word64, Word64, Word64, Word64) -> ChaChaDRG drgNewTest = initializeWords 
README.md view
@@ -52,6 +52,10 @@ with the lack of autodetection feature builtin in .cabal file, it is left on the user to disable the aesni. See the [Disabling AESNI] section +On CentOS 7 the default C compiler includes intrinsic header files incompatible+with per-function target options.  Solutions are to use GCC >= 4.9 or disable+flag *use_target_attributes* (see flag configuration examples below).+ Disabling AESNI --------------- @@ -71,6 +75,13 @@ ```  For help with cabal flags, see: [stackoverflow : is there a way to define flags for cabal](http://stackoverflow.com/questions/23523869/is-there-any-way-to-define-flags-for-cabal-dependencies)++Enabling PCLMULDQ+-----------------++When the C toolchain supports it, enabling flag *support_pclmuldq* can bring+additional security and performance for AES GCM.  A CPU with the necessary+instruction set will use an alternate implementation selected at runtime.  Links -----
cbits/cryptonite_align.h view
@@ -44,11 +44,21 @@ 	*((uint32_t *) dst) = cpu_to_le32(v); } +static inline void xor_le32_aligned(uint8_t *dst, const uint32_t v)+{+	*((uint32_t *) dst) ^= cpu_to_le32(v);+}+ static inline void store_be32_aligned(uint8_t *dst, const uint32_t v) { 	*((uint32_t *) dst) = cpu_to_be32(v); } +static inline void xor_be32_aligned(uint8_t *dst, const uint32_t v)+{+	*((uint32_t *) dst) ^= cpu_to_be32(v);+}+ static inline void store_le64_aligned(uint8_t *dst, const uint64_t v) { 	*((uint64_t *) dst) = cpu_to_le64(v);@@ -59,6 +69,11 @@ 	*((uint64_t *) dst) = cpu_to_be64(v); } +static inline void xor_be64_aligned(uint8_t *dst, const uint64_t v)+{+	*((uint64_t *) dst) ^= cpu_to_be64(v);+}+ #ifdef UNALIGNED_ACCESS_OK #define load_le32(a) load_le32_aligned(a) #else@@ -70,20 +85,30 @@  #ifdef UNALIGNED_ACCESS_OK #define store_le32(a, b) store_le32_aligned(a, b)+#define xor_le32(a, b) xor_le32_aligned(a, b) #else static inline void store_le32(uint8_t *dst, const uint32_t v) { 	dst[0] = v; dst[1] = v >> 8; dst[2] = v >> 16; dst[3] = v >> 24; }+static inline void xor_le32(uint8_t *dst, const uint32_t v)+{+	dst[0] ^= v; dst[1] ^= v >> 8; dst[2] ^= v >> 16; dst[3] ^= v >> 24;+} #endif  #ifdef UNALIGNED_ACCESS_OK #define store_be32(a, b) store_be32_aligned(a, b)+#define xor_be32(a, b) xor_be32_aligned(a, b) #else static inline void store_be32(uint8_t *dst, const uint32_t v) { 	dst[3] = v; dst[2] = v >> 8; dst[1] = v >> 16; dst[0] = v >> 24; }+static inline void xor_be32(uint8_t *dst, const uint32_t v)+{+	dst[3] ^= v; dst[2] ^= v >> 8; dst[1] ^= v >> 16; dst[0] ^= v >> 24;+} #endif  #ifdef UNALIGNED_ACCESS_OK@@ -98,11 +123,17 @@  #ifdef UNALIGNED_ACCESS_OK #define store_be64(a, b) store_be64_aligned(a, b)+#define xor_be64(a, b) xor_be64_aligned(a, b) #else static inline void store_be64(uint8_t *dst, const uint64_t v) { 	dst[7] = v      ; dst[6] = v >> 8 ; dst[5] = v >> 16; dst[4] = v >> 24; 	dst[3] = v >> 32; dst[2] = v >> 40; dst[1] = v >> 48; dst[0] = v >> 56;+}+static inline void xor_be64(uint8_t *dst, const uint64_t v)+{+	dst[7] ^= v      ; dst[6] ^= v >> 8 ; dst[5] ^= v >> 16; dst[4] ^= v >> 24;+	dst[3] ^= v >> 32; dst[2] ^= v >> 40; dst[1] ^= v >> 48; dst[0] ^= v >> 56; } #endif 
+ cbits/cryptonite_hash_prefix.c view
@@ -0,0 +1,90 @@+/*+ * Copyright (C) 2020 Olivier Chéron <olivier.cheron@gmail.com>+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *    notice, this list of conditions and the following disclaimer in the+ *    documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#include <cryptonite_hash_prefix.h>++void CRYPTONITE_HASHED(finalize_prefix)(struct HASHED_LOWER(ctx) *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)+{+	uint64_t bits[HASHED(BITS_ELEMS)];+	uint8_t *p = (uint8_t *) &bits;+	uint32_t index, padidx, padlen, pos, out_mask;+	static const uint32_t cut_off = HASHED(BLOCK_SIZE) - sizeof(bits);++	/* Make sure n <= len */+	n += (len - n) & constant_time_lt(len, n);++	/* Initial index, based on current context state */+	index = CRYPTONITE_HASHED(get_index)(ctx);++	/* Final size after n bytes */+	CRYPTONITE_HASHED(incr_sz)(ctx, bits, n);++	/* Padding index and length */+	padidx = CRYPTONITE_HASHED(get_index)(ctx);+	padlen = HASHED(BLOCK_SIZE) + cut_off - padidx;+	padlen -= HASHED(BLOCK_SIZE) & constant_time_lt(padidx, cut_off);++	/* Initialize buffers because we will XOR into them */+	memset(ctx->buf + index, 0, HASHED(BLOCK_SIZE) - index);+	memset(out, 0, HASHED(DIGEST_SIZE));+	pos = 0;++	/* Iterate based on the full buffer length, regardless of n, and include+	 * the maximum overhead with padding and size bytes+	 */+	while (pos < len + HASHED(BLOCK_SIZE) + sizeof(bits)) {+		uint8_t b;++		/* Take as many bytes from the input buffer as possible */+		if (pos < len)+			b = *(data++) & (uint8_t) constant_time_lt(pos, n);+		else+			b = 0;++		/* First padding byte */+		b |= 0x80 & (uint8_t) constant_time_eq(pos, n);++		/* Size bytes are always at the end of a block */+		if (index >= cut_off)+			b |= p[index - cut_off] & (uint8_t) constant_time_ge(pos, n + padlen);++		/* Store this byte into the buffer */+		ctx->buf[index++] ^= b;+		pos++;++		/* Process a full block, at a boundary which is independent from n */+		if (index >= HASHED(BLOCK_SIZE)) {+			index = 0;+			HASHED_LOWER(do_chunk)(ctx, (void *) ctx->buf);+			memset(ctx->buf, 0, HASHED(BLOCK_SIZE));++			/* Try to store the result: this is a no-op except when we reach the+			 * actual size based on n, more iterations may continue after that+			 * when len is really larger+			 */+			out_mask = constant_time_eq(pos, n + padlen + sizeof(bits));+			CRYPTONITE_HASHED(select_digest)(ctx, out, out_mask);+		}+	}+}
+ cbits/cryptonite_hash_prefix.h view
@@ -0,0 +1,65 @@+/*+ * Copyright (C) 2020 Olivier Chéron <olivier.cheron@gmail.com>+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *	notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ *	notice, this list of conditions and the following disclaimer in the+ *	documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+ */++#ifndef CRYPTONITE_HASH_PREFIX_H+#define CRYPTONITE_HASH_PREFIX_H++#include <stdint.h>++static inline uint32_t constant_time_msb(uint32_t a)+{+	return 0 - (a >> 31);+}++static inline uint32_t constant_time_lt(uint32_t a, uint32_t b)+{+	return constant_time_msb(a ^ ((a ^ b) | ((a - b) ^ b)));+}++static inline uint32_t constant_time_ge(uint32_t a, uint32_t b)+{+	return ~constant_time_lt(a, b);+}++static inline uint32_t constant_time_is_zero(uint32_t a)+{+	return constant_time_msb(~a & (a - 1));+}++static inline uint32_t constant_time_eq(uint32_t a, uint32_t b)+{+	return constant_time_is_zero(a ^ b);+}++static inline uint64_t constant_time_msb_64(uint64_t a)+{+	return 0 - (a >> 63);+}++static inline uint64_t constant_time_lt_64(uint64_t a, uint64_t b)+{+	return constant_time_msb_64(a ^ ((a ^ b) | ((a - b) ^ b)));+}++#endif
cbits/cryptonite_md5.c view
@@ -185,3 +185,30 @@ 	store_le32(out+ 8, ctx->h[2]); 	store_le32(out+12, ctx->h[3]); }++#define HASHED(m) MD5_##m+#define HASHED_LOWER(m) md5_##m+#define CRYPTONITE_HASHED(m) cryptonite_md5_##m+#define MD5_BLOCK_SIZE 64+#define MD5_BITS_ELEMS 1++static inline uint32_t cryptonite_md5_get_index(const struct md5_ctx *ctx)+{+	return (uint32_t) (ctx->sz & 0x3f);+}++static inline void cryptonite_md5_incr_sz(struct md5_ctx *ctx, uint64_t *bits, uint32_t n)+{+	ctx->sz += n;+	*bits = cpu_to_le64(ctx->sz << 3);+}++static inline void cryptonite_md5_select_digest(const struct md5_ctx *ctx, uint8_t *out, uint32_t out_mask)+{+	xor_le32(out   , ctx->h[0] & out_mask);+	xor_le32(out+ 4, ctx->h[1] & out_mask);+	xor_le32(out+ 8, ctx->h[2] & out_mask);+	xor_le32(out+12, ctx->h[3] & out_mask);+}++#include <cryptonite_hash_prefix.c>
cbits/cryptonite_md5.h view
@@ -39,5 +39,6 @@ void cryptonite_md5_init(struct md5_ctx *ctx); void cryptonite_md5_update(struct md5_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_md5_finalize(struct md5_ctx *ctx, uint8_t *out);+void cryptonite_md5_finalize_prefix(struct md5_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  #endif
cbits/cryptonite_sha1.c view
@@ -216,3 +216,31 @@ 	store_be32(out+12, ctx->h[3]); 	store_be32(out+16, ctx->h[4]); }++#define HASHED(m) SHA1_##m+#define HASHED_LOWER(m) sha1_##m+#define CRYPTONITE_HASHED(m) cryptonite_sha1_##m+#define SHA1_BLOCK_SIZE 64+#define SHA1_BITS_ELEMS 1++static inline uint32_t cryptonite_sha1_get_index(const struct sha1_ctx *ctx)+{+	return (uint32_t) (ctx->sz & 0x3f);+}++static inline void cryptonite_sha1_incr_sz(struct sha1_ctx *ctx, uint64_t *bits, uint32_t n)+{+	ctx->sz += n;+	*bits = cpu_to_be64(ctx->sz << 3);+}++static inline void cryptonite_sha1_select_digest(const struct sha1_ctx *ctx, uint8_t *out, uint32_t out_mask)+{+	xor_be32(out   , ctx->h[0] & out_mask);+	xor_be32(out+ 4, ctx->h[1] & out_mask);+	xor_be32(out+ 8, ctx->h[2] & out_mask);+	xor_be32(out+12, ctx->h[3] & out_mask);+	xor_be32(out+16, ctx->h[4] & out_mask);+}++#include <cryptonite_hash_prefix.c>
cbits/cryptonite_sha1.h view
@@ -41,5 +41,6 @@ void cryptonite_sha1_init(struct sha1_ctx *ctx); void cryptonite_sha1_update(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha1_finalize(struct sha1_ctx *ctx, uint8_t *out);+void cryptonite_sha1_finalize_prefix(struct sha1_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  #endif
cbits/cryptonite_sha256.c view
@@ -161,6 +161,14 @@ 	memcpy(out, intermediate, SHA224_DIGEST_SIZE); } +void cryptonite_sha224_finalize_prefix(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)+{+	uint8_t intermediate[SHA256_DIGEST_SIZE];++	cryptonite_sha256_finalize_prefix(ctx, data, len, n, intermediate);+	memcpy(out, intermediate, SHA224_DIGEST_SIZE);+}+ void cryptonite_sha256_finalize(struct sha256_ctx *ctx, uint8_t *out) { 	static uint8_t padding[64] = { 0x80, };@@ -182,3 +190,29 @@ 	for (i = 0; i < 8; i++) 		store_be32(out+4*i, ctx->h[i]); }++#define HASHED(m) SHA256_##m+#define HASHED_LOWER(m) sha256_##m+#define CRYPTONITE_HASHED(m) cryptonite_sha256_##m+#define SHA256_BLOCK_SIZE 64+#define SHA256_BITS_ELEMS 1++static inline uint32_t cryptonite_sha256_get_index(const struct sha256_ctx *ctx)+{+	return (uint32_t) (ctx->sz & 0x3f);+}++static inline void cryptonite_sha256_incr_sz(struct sha256_ctx *ctx, uint64_t *bits, uint32_t n)+{+	ctx->sz += n;+	*bits = cpu_to_be64(ctx->sz << 3);+}++static inline void cryptonite_sha256_select_digest(const struct sha256_ctx *ctx, uint8_t *out, uint32_t out_mask)+{+	uint32_t i;+	for (i = 0; i < 8; i++)+		xor_be32(out+4*i, ctx->h[i] & out_mask);+}++#include <cryptonite_hash_prefix.c>
cbits/cryptonite_sha256.h view
@@ -47,9 +47,11 @@ void cryptonite_sha224_init(struct sha224_ctx *ctx); void cryptonite_sha224_update(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha224_finalize(struct sha224_ctx *ctx, uint8_t *out);+void cryptonite_sha224_finalize_prefix(struct sha224_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  void cryptonite_sha256_init(struct sha256_ctx *ctx); void cryptonite_sha256_update(struct sha256_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha256_finalize(struct sha256_ctx *ctx, uint8_t *out);+void cryptonite_sha256_finalize_prefix(struct sha256_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  #endif
cbits/cryptonite_sha512.c view
@@ -180,6 +180,14 @@ 	memcpy(out, intermediate, SHA384_DIGEST_SIZE); } +void cryptonite_sha384_finalize_prefix(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out)+{+	uint8_t intermediate[SHA512_DIGEST_SIZE];++	cryptonite_sha512_finalize_prefix(ctx, data, len, n, intermediate);+	memcpy(out, intermediate, SHA384_DIGEST_SIZE);+}+ void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out) { 	static uint8_t padding[128] = { 0x80, };@@ -202,6 +210,38 @@ 	for (i = 0; i < 8; i++) 		store_be64(out+8*i, ctx->h[i]); }++#define HASHED(m) SHA512_##m+#define HASHED_LOWER(m) sha512_##m+#define CRYPTONITE_HASHED(m) cryptonite_sha512_##m+#define SHA512_BLOCK_SIZE 128+#define SHA512_BITS_ELEMS 2++#include <cryptonite_hash_prefix.h>++static inline uint32_t cryptonite_sha512_get_index(const struct sha512_ctx *ctx)+{+	return (uint32_t) (ctx->sz[0] & 0x7f);+}++static inline void cryptonite_sha512_incr_sz(struct sha512_ctx *ctx, uint64_t *bits, uint32_t n)+{+	ctx->sz[0] += n;+	ctx->sz[1] += 1 & constant_time_lt_64(ctx->sz[0], n);+	bits[0] = cpu_to_be64((ctx->sz[1] << 3 | ctx->sz[0] >> 61));+	bits[1] = cpu_to_be64((ctx->sz[0] << 3));+}++static inline void cryptonite_sha512_select_digest(const struct sha512_ctx *ctx, uint8_t *out, uint32_t out_mask)+{+	uint32_t i;+	uint64_t out_mask_64 = out_mask;+	out_mask_64 |= out_mask_64 << 32;+	for (i = 0; i < 8; i++)+		xor_be64(out+8*i, ctx->h[i] & out_mask_64);+}++#include <cryptonite_hash_prefix.c>  #include <stdio.h> 
cbits/cryptonite_sha512.h view
@@ -46,10 +46,12 @@ void cryptonite_sha384_init(struct sha384_ctx *ctx); void cryptonite_sha384_update(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha384_finalize(struct sha384_ctx *ctx, uint8_t *out);+void cryptonite_sha384_finalize_prefix(struct sha384_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  void cryptonite_sha512_init(struct sha512_ctx *ctx); void cryptonite_sha512_update(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out);+void cryptonite_sha512_finalize_prefix(struct sha512_ctx *ctx, const uint8_t *data, uint32_t len, uint32_t n, uint8_t *out);  /* only multiples of 8 are supported as valid t values */ void cryptonite_sha512t_init(struct sha512_ctx *ctx, uint32_t hashlen);
cryptonite.cabal view
@@ -1,5 +1,5 @@ Name:                cryptonite-version:             0.27+version:             0.28 Synopsis:            Cryptography Primitives sink Description:     A repository of cryptographic primitives.@@ -57,6 +57,7 @@                      cbits/argon2/*.h                      cbits/argon2/*.c                      cbits/aes/x86ni_impl.c+                     cbits/cryptonite_hash_prefix.c                      tests/*.hs  source-repository head
tests/Hash.hs view
@@ -221,6 +221,24 @@ runhashinc (HashAlg hashAlg) v = B.convertToBase B.Base16 $ hashinc $ v   where hashinc = hashFinalize . foldl hashUpdate (hashInitWith hashAlg) +data HashPrefixAlg = forall alg . HashAlgorithmPrefix alg => HashPrefixAlg alg++expectedPrefix :: [ (String, HashPrefixAlg) ]+expectedPrefix =+    [ ("MD5", HashPrefixAlg MD5)+    , ("SHA1", HashPrefixAlg SHA1)+    , ("SHA224", HashPrefixAlg SHA224)+    , ("SHA256", HashPrefixAlg SHA256)+    , ("SHA384", HashPrefixAlg SHA384)+    , ("SHA512", HashPrefixAlg SHA512)+    ]++runhashpfx :: HashPrefixAlg -> ByteString -> ByteString+runhashpfx (HashPrefixAlg hashAlg) v = B.convertToBase B.Base16 $ hashWith hashAlg v++runhashpfxpfx :: HashPrefixAlg -> ByteString -> Int -> ByteString+runhashpfxpfx (HashPrefixAlg hashAlg) v len = B.convertToBase B.Base16 $ hashPrefixWith hashAlg v len+ makeTestAlg (name, hashAlg, results) =     testGroup name $ concatMap maketest (zip3 is vectors results)   where@@ -236,6 +254,19 @@         runhash hashAlg inp `propertyEq` runhashinc hashAlg (chunkS ckLen inp)     ] +makeTestPrefix (hashName, hashAlg) =+    [ testProperty hashName $ \(ArbitraryBS0_2901 inp) (Int0_2901 len) ->+        runhashpfx hashAlg (B.take len inp) `propertyEq` runhashpfxpfx hashAlg inp len+    ]++makeTestHybrid (hashName, HashPrefixAlg alg) =+    [ testProperty hashName $ \(ArbitraryBS0_2901 start) (ArbitraryBS0_2901 end) -> do+        len <- choose (0, B.length end)+        let ref = hashWith alg (start `B.append` B.take len end)+            hyb = hashFinalizePrefix (hashUpdate (hashInitWith alg) start) end len+        return (ref `propertyEq` hyb)+    ]+ -- SHAKE128 truncation example with expected byte at final position -- <https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/ShakeTruncation.pdf> shake128TruncationBytes = [0x01, 0x03, 0x07, 0x0f, 0x0f, 0x2f, 0x6f, 0x6f]@@ -253,6 +284,8 @@ tests = testGroup "hash"     [ testGroup "KATs" (map makeTestAlg expected)     , testGroup "Chunking" (concatMap makeTestChunk expected)+    , testGroup "Prefix" (concatMap makeTestPrefix expectedPrefix)+    , testGroup "Hybrid" (concatMap makeTestHybrid expectedPrefix)     , testGroup "Truncating"         [ testGroup "SHAKE128"             (zipWith makeTestSHAKE128Truncation [1..] shake128TruncationBytes)