From d8ed5ce9f137086b76b1720a6311262c384ebc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Wed, 8 Feb 2017 20:58:16 +0100 Subject: [PATCH 1/8] Add SHAKE128 and SHAKE256 as HashAlgorithm instances Generalizes SHA-3 code for SHAKE support and uses GHC type-level literals to keep the output length variable. --- Crypto/Hash/Algorithms.hs | 7 ++++ Crypto/Hash/SHA3.hs | 4 +- Crypto/Hash/SHAKE.hs | 87 +++++++++++++++++++++++++++++++++++++++ cbits/cryptonite_sha3.c | 74 +++++++++++++++++++++++++++++---- cbits/cryptonite_sha3.h | 5 ++- cryptonite.cabal | 4 +- tests/Hash.hs | 14 +++++++ 7 files changed, 181 insertions(+), 14 deletions(-) create mode 100644 Crypto/Hash/SHAKE.hs mode change 100644 => 100755 cbits/cryptonite_sha3.c mode change 100644 => 100755 cbits/cryptonite_sha3.h diff --git a/Crypto/Hash/Algorithms.hs b/Crypto/Hash/Algorithms.hs index 094a595..45ee759 100644 --- a/Crypto/Hash/Algorithms.hs +++ b/Crypto/Hash/Algorithms.hs @@ -42,6 +42,10 @@ module Crypto.Hash.Algorithms , SHA3_256(..) , SHA3_384(..) , SHA3_512(..) +#if MIN_VERSION_base(4,7,0) + , SHAKE128(..) + , SHAKE256(..) +#endif , Skein256_224(..) , Skein256_256(..) , Skein512_224(..) @@ -72,3 +76,6 @@ import Crypto.Hash.Tiger import Crypto.Hash.Skein256 import Crypto.Hash.Skein512 import Crypto.Hash.Whirlpool +#if MIN_VERSION_base(4,7,0) +import Crypto.Hash.SHAKE +#endif diff --git a/Crypto/Hash/SHA3.hs b/Crypto/Hash/SHA3.hs index c43b01c..43f564a 100644 --- a/Crypto/Hash/SHA3.hs +++ b/Crypto/Hash/SHA3.hs @@ -6,7 +6,7 @@ -- Portability : unknown -- -- module containing the binding functions to work with the --- SHA3 cryptographic hash. +-- SHA3 cryptographic hash and extendable output functions. -- {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE DeriveDataTypeable #-} @@ -20,7 +20,6 @@ import Data.Data import Data.Typeable import Data.Word (Word8, Word32) - -- | SHA3 (224 bits) cryptographic hash algorithm data SHA3_224 = SHA3_224 deriving (Show,Data,Typeable) @@ -69,7 +68,6 @@ instance HashAlgorithm SHA3_512 where hashInternalUpdate = c_sha3_update hashInternalFinalize p = c_sha3_finalize p 512 - foreign import ccall unsafe "cryptonite_sha3_init" c_sha3_init :: Ptr (Context a) -> Word32 -> IO () diff --git a/Crypto/Hash/SHAKE.hs b/Crypto/Hash/SHAKE.hs new file mode 100644 index 0000000..938d249 --- /dev/null +++ b/Crypto/Hash/SHAKE.hs @@ -0,0 +1,87 @@ +-- | +-- Module : Crypto.Hash.SHAKE +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : unknown +-- +-- module containing the binding functions to work with the +-- SHA3 extendable output functions (SHAKE). +-- +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +module Crypto.Hash.SHAKE + ( SHAKE128 (..), SHAKE256 (..) + ) where + +import Crypto.Hash.Types +import Foreign.Ptr (Ptr) +import Data.Typeable +import Data.Word (Word8, Word32) + +import Data.Proxy (Proxy(..)) +import GHC.TypeLits (Nat, KnownNat, natVal) + +-- | SHAKE128 (128 bits) extendable output function. Supports an arbitrary +-- digest size (multiple of 8 bits), to be specified as a type parameter +-- of kind 'Nat'. +-- +-- Note: outputs from @'SHAKE128' n@ and @'SHAKE128' m@ for the same input are +-- correlated (one being a prefix of the other). Results are unrelated to +-- 'SHAKE256' results. +data SHAKE128 (bitlen :: Nat) = SHAKE128 + deriving (Show) + +instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where + hashBlockSize _ = 168 + hashDigestSize _ = byteLen (Proxy :: Proxy bitlen) + hashInternalContextSize _ = 376 + hashInternalInit p = c_sha3_init p 128 + hashInternalUpdate = c_sha3_update + hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitlen) + +-- | SHAKE256 (256 bits) extendable output function. Supports an arbitrary +-- digest size (multiple of 8 bits), to be specified as a type parameter +-- of kind 'Nat'. +-- +-- Note: outputs from @'SHAKE256' n@ and @'SHAKE256' m@ for the same input are +-- correlated (one being a prefix of the other). Results are unrelated to +-- 'SHAKE128' results. +data SHAKE256 (bitlen :: Nat) = SHAKE256 + deriving (Show) + +instance KnownNat bitlen => HashAlgorithm (SHAKE256 bitlen) where + hashBlockSize _ = 136 + hashDigestSize _ = byteLen (Proxy :: Proxy bitlen) + hashInternalContextSize _ = 344 + hashInternalInit p = c_sha3_init p 256 + hashInternalUpdate = c_sha3_update + hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitlen) + +shakeFinalizeOutput :: KnownNat bitlen + => proxy bitlen + -> Ptr (Context a) + -> Ptr (Digest a) + -> IO () +shakeFinalizeOutput d ctx dig = do + c_sha3_finalize_shake ctx + c_sha3_output ctx dig (byteLen d) + +byteLen :: (KnownNat bitlen, Num a) => proxy bitlen -> a +byteLen d = fromInteger (natVal d `div` 8) + + +foreign import ccall unsafe "cryptonite_sha3_init" + c_sha3_init :: Ptr (Context a) -> Word32 -> IO () + +foreign import ccall "cryptonite_sha3_update" + c_sha3_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO () + +foreign import ccall unsafe "cryptonite_sha3_finalize_shake" + c_sha3_finalize_shake :: Ptr (Context a) -> IO () + +foreign import ccall unsafe "cryptonite_sha3_output" + c_sha3_output :: Ptr (Context a) -> Ptr (Digest a) -> Word32 -> IO () diff --git a/cbits/cryptonite_sha3.c b/cbits/cryptonite_sha3.c old mode 100644 new mode 100755 index a09a3b4..c9df6fb --- a/cbits/cryptonite_sha3.c +++ b/cbits/cryptonite_sha3.c @@ -98,6 +98,10 @@ static inline void sha3_do_chunk(uint64_t state[25], uint64_t buf[], int bufsz) } } +/* + * Initialize a SHA-3 / SHAKE context: hashlen is the security level (and + * half the capacity) in bits + */ void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen) { int bufsz = 200 - 2 * (hashlen / 8); @@ -105,6 +109,7 @@ void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen) ctx->bufsz = bufsz; } +/* Update a SHA-3 / SHAKE context */ void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t len) { uint32_t to_fill; @@ -126,7 +131,7 @@ void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t } if (need_alignment(data, 8)) { - uint64_t tramp[200 - 2 * (224 / 8)]; + uint64_t tramp[200 - 2 * (128 / 8)]; ASSERT_ALIGNMENT(tramp, 8); for (; len >= ctx->bufsz; len -= ctx->bufsz, data += ctx->bufsz) { memcpy(tramp, data, ctx->bufsz / 8); @@ -146,10 +151,8 @@ void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t } } -void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *out) +void cryptonite_sha3_finalize_with_pad_byte(struct sha3_ctx *ctx, uint8_t pad_byte) { - uint64_t w[25]; - /* process full buffer if needed */ if (ctx->bufindex == ctx->bufsz) { sha3_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8); @@ -157,14 +160,67 @@ void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *o } /* add the 10*1 padding */ - ctx->buf[ctx->bufindex++] = 0x06; + ctx->buf[ctx->bufindex++] = pad_byte; memset(ctx->buf + ctx->bufindex, 0, ctx->bufsz - ctx->bufindex); ctx->buf[ctx->bufsz - 1] |= 0x80; /* process */ sha3_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8); - - /* output */ - cpu_to_le64_array(w, ctx->state, 25); - memcpy(out, w, hashlen / 8); + ctx->bufindex = 0; +} + +/* + * Extract some bytes from a finalized SHA-3 / SHAKE context. + * May be called multiple times. + */ +void cryptonite_sha3_output(struct sha3_ctx *ctx, uint8_t *out, uint32_t len) +{ + uint64_t w[25]; + uint8_t *wptr = (uint8_t *) w; + uint32_t still_avail; + + still_avail = ctx->bufsz - ctx->bufindex; + + if (ctx->bufindex == ctx->bufsz) { + /* squeeze the sponge again, without any input */ + sha3_do_chunk(ctx->state, NULL, 0); + ctx->bufindex = 0; + } + + /* use bytes already available if this block is fully consumed */ + if (ctx->bufindex && len >= still_avail) { + cpu_to_le64_array(w, ctx->state, 25); + memcpy(out, wptr + ctx->bufindex, still_avail); + sha3_do_chunk(ctx->state, NULL, 0); + len -= still_avail; + out += still_avail; + ctx->bufindex = 0; + } + + /* output as much ctx->bufsz-block */ + for (; len > ctx->bufsz; len -= ctx->bufsz, out += ctx->bufsz) { + cpu_to_le64_array(w, ctx->state, 25); + memcpy(out, w, ctx->bufsz); + sha3_do_chunk(ctx->state, NULL, 0); + } + + /* output from partial buffer */ + if (len) { + cpu_to_le64_array(w, ctx->state, 25); + memcpy(out, wptr + ctx->bufindex, len); + ctx->bufindex += len; + } +} + +/* Finalize a SHA-3 context and return the digest value */ +void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *out) +{ + cryptonite_sha3_finalize_with_pad_byte(ctx, 0x06); + cryptonite_sha3_output(ctx, out, hashlen / 8); +} + +/* Finalize a SHAKE context. Output is read using cryptonite_sha3_output. */ +void cryptonite_sha3_finalize_shake(struct sha3_ctx *ctx) +{ + cryptonite_sha3_finalize_with_pad_byte(ctx, 0x1F); } diff --git a/cbits/cryptonite_sha3.h b/cbits/cryptonite_sha3.h old mode 100644 new mode 100755 index 966f47d..233f1e1 --- a/cbits/cryptonite_sha3.h +++ b/cbits/cryptonite_sha3.h @@ -31,7 +31,7 @@ struct sha3_ctx uint32_t bufindex; uint32_t bufsz; uint64_t state[25]; - uint8_t buf[0]; /* maximum SHA3-224 is 144 bytes, otherwise buffer can be decreases */ + uint8_t buf[0]; /* maximum SHAKE128 is 168 bytes, otherwise buffer can be decreased */ }; #define SHA3_CTX_SIZE sizeof(struct sha3_ctx) @@ -40,4 +40,7 @@ void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen); void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t len); void cryptonite_sha3_finalize(struct sha3_ctx *ctx, uint32_t hashlen, uint8_t *out); +void cryptonite_sha3_finalize_shake(struct sha3_ctx *ctx); +void cryptonite_sha3_output(struct sha3_ctx *ctx, uint8_t *out, uint32_t len); + #endif diff --git a/cryptonite.cabal b/cryptonite.cabal index cf1cb78..16b48ae 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -203,7 +203,9 @@ Library Crypto.Internal.Imports Crypto.Internal.Words Crypto.Internal.WordArray - Build-depends: base >= 4.3 && < 5 + if impl(ghc >= 7.8) + Other-modules: Crypto.Hash.SHAKE + Build-depends: base >= 4.7 && < 5 , bytestring , memory >= 0.12 , ghc-prim diff --git a/tests/Hash.hs b/tests/Hash.hs index 19d6edf..ad07b71 100644 --- a/tests/Hash.hs +++ b/tests/Hash.hs @@ -1,6 +1,10 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ViewPatterns #-} +#if MIN_VERSION_base(4,7,0) {-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE DataKinds #-} +#endif module Hash ( tests ) where @@ -170,6 +174,16 @@ expected = [ "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "94662583a600a12dff357c0a6f1b514a710ef0f587a38e8d2e4d7f67e9c81667" ]) +#if MIN_VERSION_base(4,7,0) + , ("SHAKE128_4096", HashAlg (SHAKE128 :: SHAKE128 4096), [ + "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef263cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc873c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef47cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32def58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c922a96188d032675c8ac850933c7aff1533b94c834adbb69c6115bad4692d8619f90b0cdf8a7b9c264029ac185b70b83f2801f2f4b3f70c593ea3aeeb613a7f1b1de33fd75081f592305f2e4526edc09631b10958f464d889f31ba010250fda7f1368ec2967fc84ef2ae9aff268e0b1700affc6820b523a3d917135f2dff2ee06bfe72b3124721d4a26c04e53a75e30e73a7a9c4a95d91c55d495e9f51dd0b5e9d83c6d5e8ce803aa62b8d654db53d09b8dcff273cdfeb573fad8bcd45578bec2e770d01efde86e721a3f7c6cce275dabe6e2143f1af18da7efddc4c7b70b5e345db93cc936bea323491ccb38a388f546a9ff00dd4e1300b9b2153d2041d205b443e41b45a653f2a5c4492c1add544512dda2529833462b71a41a45be97290b6f", + "f4202e3c5852f9182a0430fd8144f0a74b95e7417ecae17db0f8cfeed0e3e66eb5585ec6f86021cacf272c798bcf97d368b886b18fec3a571f096086a523717a3732d50db2b0b7998b4117ae66a761ccf1847a1616f4c07d5178d0d965f9feba351420f8bfb6f5ab9a0cb102568eabf3dfa4e22279f8082dce8143eb78235a1a54914ab71abb07f2f3648468370b9fbb071e074f1c030a4030225f40c39480339f3dc71d0f04f71326de1381674cc89e259e219927fae8ea2799a03da862a55afafe670957a2af3318d919d0a3358f3b891236d6a8e8d19999d1076b529968faefbd880d77bb300829dca87e9c8e4c28e0800ff37490a5bd8c36c0b0bdb2701a5d58d03378b9dbd384389e3ef0fd4003b08998fd3f32fe1a0810fc0eccaad94bca8dd83b34559c333f0b16dfc2896ed87b30ba14c81f87cd8b4bb6317db89b0e7e94c0616f9a665fba5b0e6fb3549c9d7b68e66d08a86eb2faec05cc462a771806b93cc38b0a4feb9935c6c8945da6a589891ba5ee99753cfdd38e1abc7147fd74b7c7d1ce0609b6680a2e18888d84949b6e6cf6a2aa4113535aaee079459e3f257b569a9450523c41f5b5ba4b79b3ba5949140a74bb048de0657d04954bdd71dae76f61e2a1f88aecb91cfa5b36c1bf3350a798dc4dcf48628effe3a0c5340c756bd922f78d0e36ef7df12ce78c179cc721ad087e15ea496bf5f60b21b5822d", + "22fd225fb8f2c8d0d2097e1f8d38b6a9e619d39664dad3795f0336fda544d305e1be56a9953ecd6e4bec14b622f53da492eccbe257b9af84775a6bdf81aab6b1ba3492149b7ce4ea402c56e343939f78b9a9727193269798420c9323a9eb1fa63006e1482fcf15e3696aa1ae5cb66eb8aad4ac6041ca0b576b78785ad95bc5fb6f8a420ba9e1726552c0f2b97050ae69fc018d3f63b3a812a2d39ef64b8ae368472dec4341511b02cf77363c74f216055d5905412bc2bf57b4010b1bdce2882cb28fc7e4d470ed05219fc4d1ce11d11e10db369c807cd71891653638956b2f9d176e116188aff2fbb8519d9ad8c3fb52fa8bb4a0413364e89d9f9d7db627f2a2288babedcc314a19e45c6b7fb93b16a15d9953ff619ab4d523c481552588f711b450f854c858ebcb8cf49492d6240a753bde2109c486cac666bdba767c6e9254cc723f67855534c34d08ed486c67c1b8dd288c6010ce8e6c1c9b7f927acf4d71ee99729cee55ecec544245d0b51b31dd78eb99c2723e8ba5cf92ac7720e8933d9fd3596b90073f6980c5ed3f1cbfada26bdb6946e72391198e3d1cedebdba092324500e32b8e04e32550f6dd6c4befafa95acc206c5708300d2a8df2765751102f9738a1449c4c0d588f0076d0c1a5ee445eb85c97ada5837d5dc1d34ea081c8411d64a4d9ce9279bfe9feb25696cf741c705ed46f171e2239216d6c45dc8d15" ]) + , ("SHAKE256_4096", HashAlg (SHAKE256 :: SHAKE256 4096), [ + "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846c185c15111e595522a6bcd16cf86f3d122109e3b1fdd943b6aec468a2d621a7c06c6a957c62b54dafc3be87567d677231395f6147293b68ceab7a9e0c58d864e8efde4e1b9a46cbe854713672f5caaae314ed9083dab4b099f8e300f01b8650f1f4b1d8fcf3f3cb53fb8e9eb2ea203bdc970f50ae55428a91f7f53ac266b28419c3778a15fd248d339ede785fb7f5a1aaa96d313eacc890936c173cdcd0fab882c45755feb3aed96d477ff96390bf9a66d1368b208e21f7c10d04a3dbd4e360633e5db4b602601c14cea737db3dcf722632cc77851cbdde2aaf0a33a07b373445df490cc8fc1e4160ff118378f11f0477de055a81a9eda57a4a2cfb0c83929d310912f729ec6cfa36c6ac6a75837143045d791cc85eff5b21932f23861bcf23a52b5da67eaf7baae0f5fb1369db78f3ac45f8c4ac5671d85735cdddb09d2b1e34a1fc066ff4a162cb263d6541274ae2fcc865f618abe27c124cd8b074ccd516301b91875824d09958f341ef274bdab0bae316339894304e35877b0c28a9b1fd166c796b9cc258a064a8f57e27f2a", + "2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca1d01d1369a23539cd80f7c054b6e5daf9c962cad5b8ed5bd11998b40d5734442bed798f6e5c915bd8bb07e0188d0a55c1290074f1c287af06352299184492cbdec9acba737ee292e5adaa445547355e72a03a3bac3aac770fe5d6b66600ff15d37d5b4789994ea2aeb097f550aa5e88e4d8ff0ba07b88c1c88573063f5d96df820abc2abd177ab037f351c375e553af917132cf2f563c79a619e1bb76e8e2266b0c5617d695f2c496a25f4073b6840c1833757ebb386f16757a8e16a21e9355e9b248f3b33be672da700266be99b8f8725e8ab06075f0219e655ebc188976364b7db139390d34a6ea67b4b223229183a94cf455ece91fdaf5b9c707fa4b40ec39816c1120c7aaaf47920977be900e6b9ca4b8940e192b927c475bd58e836f512ae3e52924e36ff8e9b1d0251047770a5e465905622b1f159be121ab93819c5e5c6dae299ac73bf1c4ed4a1e2c7fa3caa1039b05e94c9f993d04feb272b6e00bb0276939cf746c42936831fc8f2b4cb0cf94808ae0af405ce4bc67d1e7acfc6fd6590d3de91f795df5aaf57e2cee1845a303d0ea564be3f1299acdce67efe0d62cfc6d6829ff4ecc0a05153c24696c4d34c076453827e796f3062f94f62f4528b7cfc870f0dcd615b7c97b95da4b9be5830e8b3f66cce71e0f622c771994443e2", + "fffcaac0606c0edb7bc0d15f033accb68538159016e5ae8470bf9ebea89fa6c9fcc3e027d94f7f967b7246346bd9f6b8084e45a057b976847c4db03bf383c834054866f6a8282a497368c46e1852fc09e20f22c45607a27c8b2a4798ebefada54f8d3795b9f07606b1cd6e41f90d765480ef5c0d5790659cf1d210adfd412378b92e1dd9bd7fd95a1a66677fc6baa0e3a53c9031c1fb59cbad9f5dc5881a3c8e25c80ecb1abf0971488ada1f533dcbf8d37031335378574b8d3fad61159c9fae28caa543b3072ce308d369be340e78c6edc664cc6dde9b2f0a4ad2e60ce9c8b1e5722b8d5b73d0962b74fb9ed86307a180f53933339f9d56d3b345c2a0e98fcf5de7754f3845f6be30089f0e142ad4602f18abdc750bda7c91c3f32872e66640db46045ab4c276b379f1b834c2cbb1bd8601305649ec6b3bf20618695136dee6541492d1d985ea1fb765fd7a559e810eba30f2f710233ae5a411b94ddcaa01a08f1c31320d111c0714422cd5e987c9a76fc865de34003ab12664081be8017d23d977f2bf4ed9e3ce09ea3d64bb4ae8ebfa9d0721f57841008c297e2f455a0441a2bd618ca379dbd239a21e410defb4001b1e11f87e36bf894c222f76f12ddcc3771bbb17d5c0dfd86d89a3e13e084f6dc1c4762bcd393c1757db7afb1434221569e7ddaaffd6318253ec3df8cf5f826b81896d6474ee06a2e30ccc8c6a96bdd5" ]) +#endif ] runhash :: HashAlg -> ByteString -> ByteString From eb661e653e89d56456bad4d9e5fe3473320599f6 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Tue, 14 Feb 2017 10:54:28 +0000 Subject: [PATCH 2/8] add Typeable for SHAKE --- Crypto/Hash/SHAKE.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Crypto/Hash/SHAKE.hs b/Crypto/Hash/SHAKE.hs index 938d249..2262f74 100644 --- a/Crypto/Hash/SHAKE.hs +++ b/Crypto/Hash/SHAKE.hs @@ -33,7 +33,7 @@ import GHC.TypeLits (Nat, KnownNat, natVal) -- correlated (one being a prefix of the other). Results are unrelated to -- 'SHAKE256' results. data SHAKE128 (bitlen :: Nat) = SHAKE128 - deriving (Show) + deriving (Show, Typeable) instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where hashBlockSize _ = 168 @@ -51,7 +51,7 @@ instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where -- correlated (one being a prefix of the other). Results are unrelated to -- 'SHAKE128' results. data SHAKE256 (bitlen :: Nat) = SHAKE256 - deriving (Show) + deriving (Show, Typeable) instance KnownNat bitlen => HashAlgorithm (SHAKE256 bitlen) where hashBlockSize _ = 136 From 4b5ee833967f4cd4e5daa51ade7c2d46582d85af Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Tue, 14 Feb 2017 11:02:02 +0000 Subject: [PATCH 3/8] revert base back to >= 4.3 --- cryptonite.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cryptonite.cabal b/cryptonite.cabal index 16b48ae..5dc616c 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -205,7 +205,7 @@ Library Crypto.Internal.WordArray if impl(ghc >= 7.8) Other-modules: Crypto.Hash.SHAKE - Build-depends: base >= 4.7 && < 5 + Build-depends: base >= 4.3 && < 5 , bytestring , memory >= 0.12 , ghc-prim From 343b7593b595003fb547c77c9419e342e2110d3f Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Tue, 14 Feb 2017 15:26:06 +0000 Subject: [PATCH 4/8] add Constraint for divisibility --- Crypto/Hash/SHAKE.hs | 26 +++++++----- Crypto/Internal/Nat.hs | 95 ++++++++++++++++++++++++++++++++++++++++++ cryptonite.cabal | 1 + 3 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 Crypto/Internal/Nat.hs diff --git a/Crypto/Hash/SHAKE.hs b/Crypto/Hash/SHAKE.hs index 2262f74..2e4f3e4 100644 --- a/Crypto/Hash/SHAKE.hs +++ b/Crypto/Hash/SHAKE.hs @@ -12,7 +12,11 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} module Crypto.Hash.SHAKE ( SHAKE128 (..), SHAKE256 (..) ) where @@ -24,6 +28,7 @@ import Data.Word (Word8, Word32) import Data.Proxy (Proxy(..)) import GHC.TypeLits (Nat, KnownNat, natVal) +import Crypto.Internal.Nat -- | SHAKE128 (128 bits) extendable output function. Supports an arbitrary -- digest size (multiple of 8 bits), to be specified as a type parameter @@ -35,13 +40,13 @@ import GHC.TypeLits (Nat, KnownNat, natVal) data SHAKE128 (bitlen :: Nat) = SHAKE128 deriving (Show, Typeable) -instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where +instance (IsDivisibleBy8 bitLen, KnownNat bitLen) => HashAlgorithm (SHAKE128 bitLen) where hashBlockSize _ = 168 - hashDigestSize _ = byteLen (Proxy :: Proxy bitlen) + hashDigestSize _ = byteLen (Proxy :: Proxy bitLen) hashInternalContextSize _ = 376 hashInternalInit p = c_sha3_init p 128 hashInternalUpdate = c_sha3_update - hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitlen) + hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitLen) -- | SHAKE256 (256 bits) extendable output function. Supports an arbitrary -- digest size (multiple of 8 bits), to be specified as a type parameter @@ -53,27 +58,26 @@ instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where data SHAKE256 (bitlen :: Nat) = SHAKE256 deriving (Show, Typeable) -instance KnownNat bitlen => HashAlgorithm (SHAKE256 bitlen) where +instance (IsDivisibleBy8 bitLen, KnownNat bitLen) => HashAlgorithm (SHAKE256 bitLen) where hashBlockSize _ = 136 - hashDigestSize _ = byteLen (Proxy :: Proxy bitlen) + hashDigestSize _ = byteLen (Proxy :: Proxy bitLen) hashInternalContextSize _ = 344 hashInternalInit p = c_sha3_init p 256 hashInternalUpdate = c_sha3_update - hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitlen) + hashInternalFinalize = shakeFinalizeOutput (Proxy :: Proxy bitLen) -shakeFinalizeOutput :: KnownNat bitlen - => proxy bitlen +shakeFinalizeOutput :: (IsDivisibleBy8 bitLen, KnownNat bitLen) + => proxy bitLen -> Ptr (Context a) -> Ptr (Digest a) -> IO () shakeFinalizeOutput d ctx dig = do c_sha3_finalize_shake ctx - c_sha3_output ctx dig (byteLen d) + c_sha3_output ctx dig (fromInteger (natVal d `div` 8)) -byteLen :: (KnownNat bitlen, Num a) => proxy bitlen -> a +byteLen :: (KnownNat bitlen, IsDivisibleBy8 bitlen, Num a) => proxy bitlen -> a byteLen d = fromInteger (natVal d `div` 8) - foreign import ccall unsafe "cryptonite_sha3_init" c_sha3_init :: Ptr (Context a) -> Word32 -> IO () diff --git a/Crypto/Internal/Nat.hs b/Crypto/Internal/Nat.hs new file mode 100644 index 0000000..a01b1d2 --- /dev/null +++ b/Crypto/Internal/Nat.hs @@ -0,0 +1,95 @@ +{-# LANGUAGE ForeignFunctionInterface #-} +{-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} +module Crypto.Internal.Nat + ( type IsDivisibleBy8 + ) where + +import GHC.TypeLits (Nat, KnownNat, natVal, type (+), type (-), TypeError, ErrorMessage(..)) + +type family IsDiv8 (bitLen :: Nat) (n :: Nat) where + IsDiv8 bitLen 0 = 'True + IsDiv8 bitLen 1 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 2 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 3 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 4 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 5 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 6 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen 7 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") + IsDiv8 bitLen n = IsDiv8 n (Mod8 n) + +type family Mod8 (n :: Nat) where + Mod8 0 = 0 + Mod8 1 = 1 + Mod8 2 = 2 + Mod8 3 = 3 + Mod8 4 = 4 + Mod8 5 = 5 + Mod8 6 = 6 + Mod8 7 = 7 + Mod8 8 = 0 + Mod8 9 = 1 + Mod8 10 = 2 + Mod8 11 = 3 + Mod8 12 = 4 + Mod8 13 = 5 + Mod8 14 = 6 + Mod8 15 = 7 + Mod8 16 = 0 + Mod8 17 = 1 + Mod8 18 = 2 + Mod8 19 = 3 + Mod8 20 = 4 + Mod8 21 = 5 + Mod8 22 = 6 + Mod8 23 = 7 + Mod8 24 = 0 + Mod8 25 = 1 + Mod8 26 = 2 + Mod8 27 = 3 + Mod8 28 = 4 + Mod8 29 = 5 + Mod8 30 = 6 + Mod8 31 = 7 + Mod8 32 = 0 + Mod8 33 = 1 + Mod8 34 = 2 + Mod8 35 = 3 + Mod8 36 = 4 + Mod8 37 = 5 + Mod8 38 = 6 + Mod8 39 = 7 + Mod8 40 = 0 + Mod8 41 = 1 + Mod8 42 = 2 + Mod8 43 = 3 + Mod8 44 = 4 + Mod8 45 = 5 + Mod8 46 = 6 + Mod8 47 = 7 + Mod8 48 = 0 + Mod8 49 = 1 + Mod8 50 = 2 + Mod8 51 = 3 + Mod8 52 = 4 + Mod8 53 = 5 + Mod8 54 = 6 + Mod8 55 = 7 + Mod8 56 = 0 + Mod8 57 = 1 + Mod8 58 = 2 + Mod8 59 = 3 + Mod8 60 = 4 + Mod8 61 = 5 + Mod8 62 = 6 + Mod8 63 = 7 + Mod8 n = Mod8 (n - 64) + +type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True + diff --git a/cryptonite.cabal b/cryptonite.cabal index 5dc616c..de95e37 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -205,6 +205,7 @@ Library Crypto.Internal.WordArray if impl(ghc >= 7.8) Other-modules: Crypto.Hash.SHAKE + Crypto.Internal.Nat Build-depends: base >= 4.3 && < 5 , bytestring , memory >= 0.12 From c342d284360fe22b24892ba6920a059e6d98748c Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Tue, 14 Feb 2017 15:59:56 +0000 Subject: [PATCH 5/8] Compatibility with older version --- Crypto/Internal/Nat.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Crypto/Internal/Nat.hs b/Crypto/Internal/Nat.hs index a01b1d2..fae1848 100644 --- a/Crypto/Internal/Nat.hs +++ b/Crypto/Internal/Nat.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE KindSignatures #-} @@ -11,10 +12,11 @@ module Crypto.Internal.Nat ( type IsDivisibleBy8 ) where -import GHC.TypeLits (Nat, KnownNat, natVal, type (+), type (-), TypeError, ErrorMessage(..)) +import GHC.TypeLits type family IsDiv8 (bitLen :: Nat) (n :: Nat) where IsDiv8 bitLen 0 = 'True +#if MIN_VERSION_base(4,9,0) IsDiv8 bitLen 1 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") IsDiv8 bitLen 2 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") IsDiv8 bitLen 3 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") @@ -22,6 +24,15 @@ type family IsDiv8 (bitLen :: Nat) (n :: Nat) where IsDiv8 bitLen 5 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") IsDiv8 bitLen 6 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") IsDiv8 bitLen 7 = TypeError ('Text "bitLen " ':<>: 'ShowType bitLen ':<>: 'Text " is not divisible by 8") +#else + IsDiv8 bitLen 1 = 'False + IsDiv8 bitLen 2 = 'False + IsDiv8 bitLen 3 = 'False + IsDiv8 bitLen 4 = 'False + IsDiv8 bitLen 5 = 'False + IsDiv8 bitLen 6 = 'False + IsDiv8 bitLen 7 = 'False +#endif IsDiv8 bitLen n = IsDiv8 n (Mod8 n) type family Mod8 (n :: Nat) where From 7286cb832ad48977790ccdb0cee063a5d05d5d75 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Tue, 14 Feb 2017 17:51:40 +0000 Subject: [PATCH 6/8] Add better constants for trampoline buffer --- cbits/cryptonite_sha3.c | 5 +++-- cbits/cryptonite_sha3.h | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/cbits/cryptonite_sha3.c b/cbits/cryptonite_sha3.c index c9df6fb..e942fe6 100755 --- a/cbits/cryptonite_sha3.c +++ b/cbits/cryptonite_sha3.c @@ -104,7 +104,8 @@ static inline void sha3_do_chunk(uint64_t state[25], uint64_t buf[], int bufsz) */ void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen) { - int bufsz = 200 - 2 * (hashlen / 8); + /* assert(hashlen >= SHA3_BITSIZE_MIN && hashlen <= SHA3_BITSIZE_MAX) */ + int bufsz = SHA3_BUF_SIZE(hashlen); memset(ctx, 0, sizeof(*ctx) + bufsz); ctx->bufsz = bufsz; } @@ -131,7 +132,7 @@ void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t } if (need_alignment(data, 8)) { - uint64_t tramp[200 - 2 * (128 / 8)]; + uint64_t tramp[SHA3_BUF_SIZE_MAX/8]; ASSERT_ALIGNMENT(tramp, 8); for (; len >= ctx->bufsz; len -= ctx->bufsz, data += ctx->bufsz) { memcpy(tramp, data, ctx->bufsz / 8); diff --git a/cbits/cryptonite_sha3.h b/cbits/cryptonite_sha3.h index 233f1e1..ca4e6f9 100755 --- a/cbits/cryptonite_sha3.h +++ b/cbits/cryptonite_sha3.h @@ -35,6 +35,22 @@ struct sha3_ctx }; #define SHA3_CTX_SIZE sizeof(struct sha3_ctx) +#define SHA3_CTX_BUF_MAX_SIZE (SHA3_CTX_SIZE + SHA3_BUF_SIZE_MAX) +#define SHA3_BITSIZE_MIN 128 +#define SHA3_BITSIZE_MAX 512 + +#define SHA3_BUF_SIZE(bitsize) (200 - 2 * ((bitsize) / 8)) + +#define SHA3_BUF_SIZE_MIN SHA3_BUF_SIZE(SHA3_BITSIZE_MAX) +#define SHA3_BUF_SIZE_MAX SHA3_BUF_SIZE(SHA3_BITSIZE_MIN) + +/* + * buffer size: + * + * 128 bits (shake 128 bits) => 200 - 2 * (128 / 8) = 200 - 2*16 = 200 - 32 = 168 bytes + * 224 bits (SHA3 224 bits) => 200 - 2 * (224 / 8) = 200 - 2*28 = 200 - 56 = 144 bytes + * 512 bits (SHA3 512 bits) => 200 - 2 * (512 / 8) = 200 - 2*64 = 200 - 128 = 72 bytes + */ void cryptonite_sha3_init(struct sha3_ctx *ctx, uint32_t hashlen); void cryptonite_sha3_update(struct sha3_ctx *ctx, const uint8_t *data, uint32_t len); From 10d72c8779201a3c4255cb1d1110a56f287cba40 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Sun, 19 Feb 2017 17:17:35 +0000 Subject: [PATCH 7/8] remove unneeded extensions --- Crypto/Internal/Nat.hs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Crypto/Internal/Nat.hs b/Crypto/Internal/Nat.hs index fae1848..ed3a0c0 100644 --- a/Crypto/Internal/Nat.hs +++ b/Crypto/Internal/Nat.hs @@ -1,10 +1,6 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE ForeignFunctionInterface #-} -{-# LANGUAGE DeriveDataTypeable #-} -{-# LANGUAGE KindSignatures #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -103,4 +99,3 @@ type family Mod8 (n :: Nat) where Mod8 n = Mod8 (n - 64) type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True - From 26237c5c6d44d646f9b60ca7ba02eb57965daf3a Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Sun, 19 Feb 2017 17:17:49 +0000 Subject: [PATCH 8/8] remove spurious header modification --- Crypto/Hash/SHA3.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Crypto/Hash/SHA3.hs b/Crypto/Hash/SHA3.hs index 43f564a..09b53be 100644 --- a/Crypto/Hash/SHA3.hs +++ b/Crypto/Hash/SHA3.hs @@ -6,7 +6,7 @@ -- Portability : unknown -- -- module containing the binding functions to work with the --- SHA3 cryptographic hash and extendable output functions. +-- SHA3 cryptographic hash. -- {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE DeriveDataTypeable #-}