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 +5/−0
- Crypto/Cipher/RC4.hs +5/−0
- Crypto/Cipher/Types/AEAD.hs +5/−5
- Crypto/Hash.hs +40/−3
- Crypto/Hash/Algorithms.hs +2/−1
- Crypto/Hash/IO.hs +5/−0
- Crypto/Hash/MD5.hs +6/−0
- Crypto/Hash/SHA1.hs +6/−0
- Crypto/Hash/SHA224.hs +6/−0
- Crypto/Hash/SHA256.hs +6/−0
- Crypto/Hash/SHA384.hs +6/−0
- Crypto/Hash/SHA512.hs +6/−0
- Crypto/Hash/Types.hs +17/−0
- Crypto/MAC/Poly1305.hs +5/−0
- Crypto/Random.hs +3/−0
- README.md +11/−0
- cbits/cryptonite_align.h +31/−0
- cbits/cryptonite_hash_prefix.c +90/−0
- cbits/cryptonite_hash_prefix.h +65/−0
- cbits/cryptonite_md5.c +27/−0
- cbits/cryptonite_md5.h +1/−0
- cbits/cryptonite_sha1.c +28/−0
- cbits/cryptonite_sha1.h +1/−0
- cbits/cryptonite_sha256.c +34/−0
- cbits/cryptonite_sha256.h +2/−0
- cbits/cryptonite_sha512.c +40/−0
- cbits/cryptonite_sha512.h +2/−0
- cryptonite.cabal +2/−1
- tests/Hash.hs +33/−0
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)