From ba3ab1f0cda4616063e55f21c0b94db6e71be09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Fri, 26 Jun 2020 07:16:18 +0200 Subject: [PATCH 1/5] Add HashAlgorithmPrefix API --- Crypto/Hash.hs | 29 +++++++++++++++++++++++++++++ Crypto/Hash/Algorithms.hs | 3 ++- Crypto/Hash/Types.hs | 12 ++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Crypto/Hash.hs b/Crypto/Hash.hs index 37e6f9f..50abed6 100644 --- a/Crypto/Hash.hs +++ b/Crypto/Hash.hs @@ -28,14 +28,17 @@ module Crypto.Hash -- * 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 @@ -57,6 +60,10 @@ import Data.Word (Word8) 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) @@ -94,6 +101,24 @@ hashFinalize !c = ((!_) :: 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 @@ -102,6 +127,10 @@ hashInitWith _ = hashInit 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. -- -- If the digest is not the right size for the algorithm specified, then diff --git a/Crypto/Hash/Algorithms.hs b/Crypto/Hash/Algorithms.hs index 1565c0b..41ab7ee 100644 --- a/Crypto/Hash/Algorithms.hs +++ b/Crypto/Hash/Algorithms.hs @@ -9,6 +9,7 @@ -- module Crypto.Hash.Algorithms ( HashAlgorithm + , HashAlgorithmPrefix -- * Hash algorithms , Blake2s_160(..) , Blake2s_224(..) @@ -54,7 +55,7 @@ module Crypto.Hash.Algorithms , 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 diff --git a/Crypto/Hash/Types.hs b/Crypto/Hash/Types.hs index 65a3b61..c5170fa 100644 --- a/Crypto/Hash/Types.hs +++ b/Crypto/Hash/Types.hs @@ -14,6 +14,7 @@ {-# LANGUAGE TypeFamilies #-} module Crypto.Hash.Types ( HashAlgorithm(..) + , HashAlgorithmPrefix(..) , Context(..) , Digest(..) ) where @@ -59,6 +60,17 @@ class HashAlgorithm a where -- | 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 From caec601cd1d00275572ceaab0de15a81f9b2002d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Fri, 26 Jun 2020 07:16:30 +0200 Subject: [PATCH 2/5] Add finalize_prefix functions --- cbits/cryptonite_align.h | 31 ++++++++++++ cbits/cryptonite_hash_prefix.c | 90 ++++++++++++++++++++++++++++++++++ cbits/cryptonite_hash_prefix.h | 65 ++++++++++++++++++++++++ cbits/cryptonite_md5.c | 27 ++++++++++ cbits/cryptonite_md5.h | 1 + cbits/cryptonite_sha1.c | 28 +++++++++++ cbits/cryptonite_sha1.h | 1 + cbits/cryptonite_sha256.c | 34 +++++++++++++ cbits/cryptonite_sha256.h | 2 + cbits/cryptonite_sha512.c | 40 +++++++++++++++ cbits/cryptonite_sha512.h | 2 + cryptonite.cabal | 1 + 12 files changed, 322 insertions(+) create mode 100644 cbits/cryptonite_hash_prefix.c create mode 100644 cbits/cryptonite_hash_prefix.h diff --git a/cbits/cryptonite_align.h b/cbits/cryptonite_align.h index 41172a9..01e8a36 100644 --- a/cbits/cryptonite_align.h +++ b/cbits/cryptonite_align.h @@ -44,11 +44,21 @@ static inline void store_le32_aligned(uint8_t *dst, const uint32_t v) *((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 @@ static inline void store_be64_aligned(uint8_t *dst, const uint64_t v) *((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 @@ static inline uint32_t load_le32(const uint8_t *p) #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,12 +123,18 @@ static inline void store_le64(uint8_t *dst, const uint64_t v) #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 #endif diff --git a/cbits/cryptonite_hash_prefix.c b/cbits/cryptonite_hash_prefix.c new file mode 100644 index 0000000..06df581 --- /dev/null +++ b/cbits/cryptonite_hash_prefix.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Olivier Chéron + * + * 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 + +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); + } + } +} diff --git a/cbits/cryptonite_hash_prefix.h b/cbits/cryptonite_hash_prefix.h new file mode 100644 index 0000000..3eed4e0 --- /dev/null +++ b/cbits/cryptonite_hash_prefix.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Olivier Chéron + * + * 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 + +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 diff --git a/cbits/cryptonite_md5.c b/cbits/cryptonite_md5.c index 126bac4..48ac4c6 100644 --- a/cbits/cryptonite_md5.c +++ b/cbits/cryptonite_md5.c @@ -185,3 +185,30 @@ void cryptonite_md5_finalize(struct md5_ctx *ctx, uint8_t *out) 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 diff --git a/cbits/cryptonite_md5.h b/cbits/cryptonite_md5.h index 7187f96..e6fc67a 100644 --- a/cbits/cryptonite_md5.h +++ b/cbits/cryptonite_md5.h @@ -39,5 +39,6 @@ struct md5_ctx 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 diff --git a/cbits/cryptonite_sha1.c b/cbits/cryptonite_sha1.c index 533eded..3a94707 100644 --- a/cbits/cryptonite_sha1.c +++ b/cbits/cryptonite_sha1.c @@ -216,3 +216,31 @@ void cryptonite_sha1_finalize(struct sha1_ctx *ctx, uint8_t *out) 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 diff --git a/cbits/cryptonite_sha1.h b/cbits/cryptonite_sha1.h index ee3f202..73cd306 100644 --- a/cbits/cryptonite_sha1.h +++ b/cbits/cryptonite_sha1.h @@ -41,5 +41,6 @@ struct sha1_ctx 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 diff --git a/cbits/cryptonite_sha256.c b/cbits/cryptonite_sha256.c index d82f5df..fb783a4 100644 --- a/cbits/cryptonite_sha256.c +++ b/cbits/cryptonite_sha256.c @@ -161,6 +161,14 @@ void cryptonite_sha224_finalize(struct sha224_ctx *ctx, uint8_t *out) 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 @@ void cryptonite_sha256_finalize(struct sha256_ctx *ctx, uint8_t *out) 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 diff --git a/cbits/cryptonite_sha256.h b/cbits/cryptonite_sha256.h index 705ff9a..49e18cc 100644 --- a/cbits/cryptonite_sha256.h +++ b/cbits/cryptonite_sha256.h @@ -47,9 +47,11 @@ struct sha256_ctx 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 diff --git a/cbits/cryptonite_sha512.c b/cbits/cryptonite_sha512.c index 7d345ae..cdc1eec 100644 --- a/cbits/cryptonite_sha512.c +++ b/cbits/cryptonite_sha512.c @@ -180,6 +180,14 @@ void cryptonite_sha384_finalize(struct sha384_ctx *ctx, uint8_t *out) 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, }; @@ -203,6 +211,38 @@ void cryptonite_sha512_finalize(struct sha512_ctx *ctx, uint8_t *out) 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 + +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 + #include void cryptonite_sha512t_init(struct sha512_ctx *ctx, uint32_t hashlen) diff --git a/cbits/cryptonite_sha512.h b/cbits/cryptonite_sha512.h index 38fc560..53893c0 100644 --- a/cbits/cryptonite_sha512.h +++ b/cbits/cryptonite_sha512.h @@ -46,10 +46,12 @@ struct sha512_ctx 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); diff --git a/cryptonite.cabal b/cryptonite.cabal index b4bf6e5..b9af22f 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -57,6 +57,7 @@ extra-source-files: cbits/*.h cbits/argon2/*.h cbits/argon2/*.c cbits/aes/x86ni_impl.c + cbits/cryptonite_hash_prefix.c tests/*.hs source-repository head From e67d8fb22347f8f1d55fe095d3dc34fe6d474300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Fri, 26 Jun 2020 07:16:41 +0200 Subject: [PATCH 3/5] Generate HashAlgorithmPrefix instances --- Crypto/Hash/MD5.hs | 6 ++++++ Crypto/Hash/SHA1.hs | 6 ++++++ Crypto/Hash/SHA224.hs | 6 ++++++ Crypto/Hash/SHA256.hs | 6 ++++++ Crypto/Hash/SHA384.hs | 6 ++++++ Crypto/Hash/SHA512.hs | 6 ++++++ gen/Gen.hs | 30 +++++++++++++++++------------- gen/Template.hs | 2 +- gen/template/hash.hs | 10 ++++++++-- 9 files changed, 62 insertions(+), 16 deletions(-) diff --git a/Crypto/Hash/MD5.hs b/Crypto/Hash/MD5.hs index 17a09ef..5574a79 100644 --- a/Crypto/Hash/MD5.hs +++ b/Crypto/Hash/MD5.hs @@ -34,6 +34,9 @@ instance HashAlgorithm MD5 where 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 "cryptonite_md5_update" 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 () diff --git a/Crypto/Hash/SHA1.hs b/Crypto/Hash/SHA1.hs index 87e44a3..4f60739 100644 --- a/Crypto/Hash/SHA1.hs +++ b/Crypto/Hash/SHA1.hs @@ -34,6 +34,9 @@ instance HashAlgorithm SHA1 where 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 "cryptonite_sha1_update" 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 () diff --git a/Crypto/Hash/SHA224.hs b/Crypto/Hash/SHA224.hs index a609d57..5d0569f 100644 --- a/Crypto/Hash/SHA224.hs +++ b/Crypto/Hash/SHA224.hs @@ -34,6 +34,9 @@ instance HashAlgorithm SHA224 where 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 "cryptonite_sha224_update" 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 () diff --git a/Crypto/Hash/SHA256.hs b/Crypto/Hash/SHA256.hs index eacd502..18211f3 100644 --- a/Crypto/Hash/SHA256.hs +++ b/Crypto/Hash/SHA256.hs @@ -34,6 +34,9 @@ instance HashAlgorithm SHA256 where 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 "cryptonite_sha256_update" 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 () diff --git a/Crypto/Hash/SHA384.hs b/Crypto/Hash/SHA384.hs index 2b19f52..22fc595 100644 --- a/Crypto/Hash/SHA384.hs +++ b/Crypto/Hash/SHA384.hs @@ -34,6 +34,9 @@ instance HashAlgorithm SHA384 where 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 "cryptonite_sha384_update" 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 () diff --git a/Crypto/Hash/SHA512.hs b/Crypto/Hash/SHA512.hs index 20449b3..9a66df9 100644 --- a/Crypto/Hash/SHA512.hs +++ b/Crypto/Hash/SHA512.hs @@ -34,6 +34,9 @@ instance HashAlgorithm SHA512 where 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 "cryptonite_sha512_update" 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 () diff --git a/gen/Gen.hs b/gen/Gen.hs index c2e43ad..2bf81d6 100644 --- a/gen/Gen.hs +++ b/gen/Gen.hs @@ -54,6 +54,7 @@ data Prop = data HashCustom = HashSimple Bits -- digest size in bits Bytes -- block length in bytes + Bool -- has HashAlgorithmPrefix instance? | HashMulti [Prop] [(Bits, Bytes)] -- list of (digest output size in *bits*, block size in bytes) hashModules = @@ -62,22 +63,22 @@ hashModules = , GenHashModule "Blake2sp" "blake2.h" "blake2sp" 1752 (HashMulti [] [(224,64), (256,64)]) , GenHashModule "Blake2b" "blake2.h" "blake2b" 248 (HashMulti [] [(160, 128), (224, 128), (256, 128), (384, 128), (512,128)]) , GenHashModule "Blake2bp" "blake2.h" "blake2bp" 1768 (HashMulti [] [(512,128)]) - , GenHashModule "MD2" "md2.h" "md2" 96 (HashSimple 128 16) - , GenHashModule "MD4" "md4.h" "md4" 96 (HashSimple 128 64) - , GenHashModule "MD5" "md5.h" "md5" 96 (HashSimple 128 64) - , GenHashModule "SHA1" "sha1.h" "sha1" 96 (HashSimple 160 64) - , GenHashModule "SHA224" "sha256.h" "sha224" 192 (HashSimple 224 64) - , GenHashModule "SHA256" "sha256.h" "sha256" 192 (HashSimple 256 64) - , GenHashModule "SHA384" "sha512.h" "sha384" 256 (HashSimple 384 128) - , GenHashModule "SHA512" "sha512.h" "sha512" 256 (HashSimple 512 128) + , GenHashModule "MD2" "md2.h" "md2" 96 (HashSimple 128 16 False) + , GenHashModule "MD4" "md4.h" "md4" 96 (HashSimple 128 64 False) + , GenHashModule "MD5" "md5.h" "md5" 96 (HashSimple 128 64 True) + , GenHashModule "SHA1" "sha1.h" "sha1" 96 (HashSimple 160 64 True) + , GenHashModule "SHA224" "sha256.h" "sha224" 192 (HashSimple 224 64 True) + , GenHashModule "SHA256" "sha256.h" "sha256" 192 (HashSimple 256 64 True) + , GenHashModule "SHA384" "sha512.h" "sha384" 256 (HashSimple 384 128 True) + , GenHashModule "SHA512" "sha512.h" "sha512" 256 (HashSimple 512 128 True) , GenHashModule "SHA512t" "sha512.h" "sha512t" 256 (HashMulti [] [(224,128),(256,128)]) , GenHashModule "Keccak" "keccak.h" "keccak" 352 (HashMulti [VarCtx sha3CtxSize] [(224,144),(256,136),(384,104),(512,72)]) , GenHashModule "SHA3" "sha3.h" "sha3" 352 (HashMulti [VarCtx sha3CtxSize] [(224,144),(256,136),(384,104),(512,72)]) - , GenHashModule "RIPEMD160" "ripemd.h" "ripemd160" 128 (HashSimple 160 64) + , GenHashModule "RIPEMD160" "ripemd.h" "ripemd160" 128 (HashSimple 160 64 False) , GenHashModule "Skein256" "skein256.h" "skein256" 96 (HashMulti [] [(224,32),(256,32)]) , GenHashModule "Skein512" "skein512.h" "skein512" 160 (HashMulti [] [(224,64),(256,64),(384,64),(512,64)]) - , GenHashModule "Tiger" "tiger.h" "tiger" 96 (HashSimple 192 64) - , GenHashModule "Whirlpool" "whirlpool.h" "whirlpool" 168 (HashSimple 512 64) + , GenHashModule "Tiger" "tiger.h" "tiger" 96 (HashSimple 192 64 False) + , GenHashModule "Whirlpool" "whirlpool.h" "whirlpool" 168 (HashSimple 512 64 False) ] sha3CtxSize :: Bits -> Bytes @@ -105,13 +106,16 @@ renderHashModules genOpts = do let (tpl, addVars, multiVars) = case ghmCustomizable ghm of - HashSimple digestSize blockLength -> + HashSimple digestSize blockLength hasPrefixInstance -> (hashTemplate, [ ("DIGEST_SIZE_BITS" , showBits digestSize) , ("DIGEST_SIZE_BYTES", showBytes digestSize) , ("BLOCK_SIZE_BYTES" , showBytes blockLength) + ], + [ ("HASPREFIXINSTANCE", + [[] | hasPrefixInstance] + ) ] - , [] ) HashMulti props customSizes -> let customCtxSize = diff --git a/gen/Template.hs b/gen/Template.hs index 2cfff18..43984e6 100644 --- a/gen/Template.hs +++ b/gen/Template.hs @@ -45,7 +45,7 @@ renderTemplate template attrs multiAttrs = renderAtom (Tpl n t) = case lookup n multiAttrs of Nothing -> error ("cannot find inner template attributes for: " ++ n) - Just [] -> error ("empty multiattrs for: " ++ n) + Just [] -> "" Just (i:is) -> renderTemplate t (i ++ attrs) [] ++ concatMap (\inAttrs -> renderTemplate t (inAttrs ++ attrs ++ [("COMMA", ",")]) []) is diff --git a/gen/template/hash.hs b/gen/template/hash.hs index 4748054..9682180 100644 --- a/gen/template/hash.hs +++ b/gen/template/hash.hs @@ -32,7 +32,10 @@ instance HashAlgorithm %%MODULENAME%% where hashInternalContextSize _ = %%CTX_SIZE_BYTES%% hashInternalInit = c_%%HASHNAME%%_init hashInternalUpdate = c_%%HASHNAME%%_update - hashInternalFinalize = c_%%HASHNAME%%_finalize + hashInternalFinalize = c_%%HASHNAME%%_finalize%{HASPREFIXINSTANCE%} + +instance HashAlgorithmPrefix %%MODULENAME%% where + hashInternalFinalizePrefix = c_%%HASHNAME%%_finalize_prefix%{HASPREFIXINSTANCE%} foreign import ccall unsafe "cryptonite_%%HASHNAME%%_init" c_%%HASHNAME%%_init :: Ptr (Context a)-> IO () @@ -41,4 +44,7 @@ foreign import ccall "cryptonite_%%HASHNAME%%_update" c_%%HASHNAME%%_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () foreign import ccall unsafe "cryptonite_%%HASHNAME%%_finalize" - c_%%HASHNAME%%_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO () + c_%%HASHNAME%%_finalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()%{HASPREFIXINSTANCE%} + +foreign import ccall "cryptonite_%%HASHNAME%%_finalize_prefix" + c_%%HASHNAME%%_finalize_prefix :: Ptr (Context a) -> Ptr Word8 -> Word32 -> Word32 -> Ptr (Digest a) -> IO ()%{HASPREFIXINSTANCE%} From c8199872e7adfa8101c7c0795542b8b33d6bcacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Fri, 26 Jun 2020 07:16:49 +0200 Subject: [PATCH 4/5] Test HashAlgorithmPrefix API --- tests/Hash.hs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Hash.hs b/tests/Hash.hs index d0f82c0..dbe752b 100644 --- a/tests/Hash.hs +++ b/tests/Hash.hs @@ -221,6 +221,24 @@ runhashinc :: HashAlg -> [ByteString] -> ByteString 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 @@ makeTestChunk (hashName, hashAlg, _) = 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 -- shake128TruncationBytes = [0x01, 0x03, 0x07, 0x0f, 0x0f, 0x2f, 0x6f, 0x6f] @@ -253,6 +284,8 @@ makeTestSHAKE128Truncation i byte = 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) From 72544ea9aafc101dc267489b22a2fa329992a842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Thu, 2 Jul 2020 19:35:06 +0200 Subject: [PATCH 5/5] Removed extra semicolon --- cbits/cryptonite_hash_prefix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cbits/cryptonite_hash_prefix.c b/cbits/cryptonite_hash_prefix.c index 06df581..b487708 100644 --- a/cbits/cryptonite_hash_prefix.c +++ b/cbits/cryptonite_hash_prefix.c @@ -63,7 +63,7 @@ void CRYPTONITE_HASHED(finalize_prefix)(struct HASHED_LOWER(ctx) *ctx, const uin b = 0; /* First padding byte */ - b |= 0x80 & (uint8_t) constant_time_eq(pos, n);; + b |= 0x80 & (uint8_t) constant_time_eq(pos, n); /* Size bytes are always at the end of a block */ if (index >= cut_off)