Skip to content
Browse files

Add SHA3 implementation.

KATs were generated from reference implementation through python-sha3.
  • Loading branch information...
1 parent 126ccb9 commit 15b268d1175579f7d4943869e364de4683e35778 @vincenthz committed Nov 5, 2012
Showing with 346 additions and 1 deletion.
  1. +5 −0 Bench.hs
  2. +122 −0 Crypto/Hash/SHA3.hs
  3. +19 −0 Tests.hs
  4. +150 −0 cbits/sha3.c
  5. +45 −0 cbits/sha3.h
  6. +5 −1 cryptohash.cabal
View
5 Bench.hs
@@ -11,6 +11,7 @@ import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA384 as SHA384
import qualified Crypto.Hash.SHA512 as SHA512
import qualified Crypto.Hash.SHA512t as SHA512t
+import qualified Crypto.Hash.SHA3 as SHA3
import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
import qualified Crypto.Hash.Tiger as Tiger
import qualified Crypto.Hash.Skein256 as Skein256
@@ -29,6 +30,10 @@ allHashs =
, ("SHA2-384",SHA384.hash, hashmany (SHA384.init,SHA384.update,SHA384.finalize))
, ("SHA2-512",SHA512.hash, hashmany (SHA512.init,SHA512.update,SHA512.finalize))
, ("SHA2-512t-512",SHA512t.hash 512, hashmany (SHA512t.init 512,SHA512t.update,SHA512t.finalize))
+ , ("SHA3-224",SHA3.hash 224, hashmany (SHA3.init 224,SHA3.update,SHA3.finalize))
+ , ("SHA3-256",SHA3.hash 256, hashmany (SHA3.init 256,SHA3.update,SHA3.finalize))
+ , ("SHA3-384",SHA3.hash 384, hashmany (SHA3.init 384,SHA3.update,SHA3.finalize))
+ , ("SHA3-512",SHA3.hash 512, hashmany (SHA3.init 512,SHA3.update,SHA3.finalize))
, ("RIPEMD160",RIPEMD160.hash, hashmany (RIPEMD160.init,RIPEMD160.update,RIPEMD160.finalize))
, ("Tiger",Tiger.hash, hashmany (Tiger.init,Tiger.update,Tiger.finalize))
, ("Skein256-256",Skein256.hash 256, hashmany (Skein256.init 256,Skein256.update,Skein256.finalize))
View
122 Crypto/Hash/SHA3.hs
@@ -0,0 +1,122 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- |
+-- Module : Crypto.Hash.SHA3
+-- License : BSD-style
+-- Maintainer : Vincent Hanquez <vincent@snarc.org>
+-- Stability : experimental
+-- Portability : unknown
+--
+-- A module containing SHA3 bindings
+--
+module Crypto.Hash.SHA3
+ ( Ctx(..)
+
+ -- * Incremental hashing Functions
+ , init -- :: Int -> Ctx
+ , update -- :: Ctx -> ByteString -> Ctx
+ , finalize -- :: Ctx -> ByteString
+
+ -- * Single Pass hashing
+ , hash -- :: Int -> ByteString -> ByteString
+ , hashlazy -- :: Int -> ByteString -> ByteString
+ ) where
+
+import Prelude hiding (init)
+import System.IO.Unsafe (unsafePerformIO)
+import Foreign.Ptr
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Storable
+import Foreign.Marshal.Alloc
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString (ByteString)
+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)
+import Data.ByteString.Internal (create, toForeignPtr)
+import Data.Word
+
+data Ctx = Ctx !ByteString
+
+{-# INLINE sizeCtx #-}
+sizeCtx :: Int
+sizeCtx = 360
+
+{- return the number of bytes of output for the digest -}
+peekHashlen :: Ptr Ctx -> IO Int
+peekHashlen ptr = peek iptr >>= \v -> return $! fromIntegral v
+ where iptr :: Ptr Word32
+ iptr = castPtr ptr
+
+{-# INLINE withByteStringPtr #-}
+withByteStringPtr :: ByteString -> (Ptr Word8 -> IO a) -> IO a
+withByteStringPtr b f =
+ withForeignPtr fptr $ \ptr -> f (ptr `plusPtr` off)
+ where (fptr, off, _) = toForeignPtr b
+
+{-# INLINE memcopy64 #-}
+memcopy64 :: Ptr Word64 -> Ptr Word64 -> IO ()
+memcopy64 dst src = mapM_ peekAndPoke [0..44]
+ where peekAndPoke i = peekElemOff src i >>= pokeElemOff dst i
+
+withCtxCopy :: Ctx -> (Ptr Ctx -> IO ()) -> IO Ctx
+withCtxCopy (Ctx ctxB) f = Ctx `fmap` createCtx
+ where createCtx = create sizeCtx $ \dstPtr ->
+ withByteStringPtr ctxB $ \srcPtr -> do
+ memcopy64 (castPtr dstPtr) (castPtr srcPtr)
+ f (castPtr dstPtr)
+
+withCtxThrow :: Ctx -> (Ptr Ctx -> IO a) -> IO a
+withCtxThrow (Ctx ctxB) f =
+ allocaBytes sizeCtx $ \dstPtr ->
+ withByteStringPtr ctxB $ \srcPtr -> do
+ memcopy64 (castPtr dstPtr) (castPtr srcPtr)
+ f (castPtr dstPtr)
+
+withCtxNew :: (Ptr Ctx -> IO ()) -> IO Ctx
+withCtxNew f = Ctx `fmap` create sizeCtx (f . castPtr)
+
+withCtxNewThrow :: (Ptr Ctx -> IO a) -> IO a
+withCtxNewThrow f = allocaBytes sizeCtx (f . castPtr)
+
+foreign import ccall unsafe "sha3.h sha3_init"
+ c_sha3_init :: Ptr Ctx -> Word32 -> IO ()
+
+foreign import ccall "sha3.h sha3_update"
+ c_sha3_update :: Ptr Ctx -> Ptr Word8 -> Word32 -> IO ()
+
+foreign import ccall unsafe "sha3.h sha3_finalize"
+ c_sha3_finalize :: Ptr Ctx -> Ptr Word8 -> IO ()
+
+updateInternalIO :: Ptr Ctx -> ByteString -> IO ()
+updateInternalIO ptr d =
+ unsafeUseAsCStringLen d (\(cs, len) -> c_sha3_update ptr (castPtr cs) (fromIntegral len))
+
+finalizeInternalIO :: Ptr Ctx -> IO ByteString
+finalizeInternalIO ptr =
+ peekHashlen ptr >>= \digestSize -> create digestSize (c_sha3_finalize ptr)
+
+{-# NOINLINE init #-}
+-- | init a context
+init :: Int -> Ctx
+init hashlen = unsafePerformIO $ withCtxNew (\ctx -> c_sha3_init ctx (fromIntegral hashlen))
+
+{-# NOINLINE update #-}
+-- | update a context with a bytestring
+update :: Ctx -> ByteString -> Ctx
+update ctx d = unsafePerformIO $ withCtxCopy ctx $ \ptr -> updateInternalIO ptr d
+
+{-# NOINLINE finalize #-}
+-- | finalize the context into a digest bytestring
+finalize :: Ctx -> ByteString
+finalize ctx = unsafePerformIO $ withCtxThrow ctx finalizeInternalIO
+
+{-# NOINLINE hash #-}
+-- | hash a strict bytestring into a digest bytestring
+hash :: Int -> ByteString -> ByteString
+hash hashlen d = unsafePerformIO $ withCtxNewThrow $ \ptr -> do
+ c_sha3_init ptr (fromIntegral hashlen) >> updateInternalIO ptr d >> finalizeInternalIO ptr
+
+{-# NOINLINE hashlazy #-}
+-- | hash a lazy bytestring into a digest bytestring
+hashlazy :: Int -> L.ByteString -> ByteString
+hashlazy hashlen l = unsafePerformIO $ withCtxNewThrow $ \ptr -> do
+ c_sha3_init ptr (fromIntegral hashlen) >> mapM_ (updateInternalIO ptr) (L.toChunks l) >> finalizeInternalIO ptr
View
19 Tests.hs
@@ -12,6 +12,7 @@ import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA384 as SHA384
import qualified Crypto.Hash.SHA512 as SHA512
import qualified Crypto.Hash.SHA512t as SHA512t
+import qualified Crypto.Hash.SHA3 as SHA3
import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
import qualified Crypto.Hash.Tiger as Tiger
import qualified Crypto.Hash.Skein256 as Skein256
@@ -43,6 +44,8 @@ sha512Hash = HashFct { fctHash = SHA512.hash, fctInc = hashinc SHA512.init SHA51
sha512_224Hash = HashFct { fctHash = SHA512t.hash 224, fctInc = hashinc (SHA512t.init 224) SHA512t.update SHA512t.finalize }
sha512_256Hash = HashFct { fctHash = SHA512t.hash 256, fctInc = hashinc (SHA512t.init 256) SHA512t.update SHA512t.finalize }
+sha3Hash i = HashFct { fctHash = SHA3.hash i, fctInc = hashinc (SHA3.init i) SHA3.update SHA3.finalize }
+
ripemd160Hash = HashFct { fctHash = RIPEMD160.hash, fctInc = hashinc RIPEMD160.init RIPEMD160.update RIPEMD160.finalize }
tigerHash = HashFct { fctHash = Tiger.hash, fctInc = hashinc Tiger.init Tiger.update Tiger.finalize }
@@ -130,6 +133,22 @@ results = [
"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3",
"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35",
"dce81fc695cfea3d7e1446509238daf89f24cc61896f2d265927daa70f2108f8902f0dfd68be085d5abb9fcd2e482c1dc24f2fabf81f40b73495cad44d7360d3"])
+ , ("SHA3-224", sha3Hash 224, [
+ "f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd",
+ "310aee6b30c47350576ac2873fa89fd190cdc488442f3ef654cf23fe",
+ "0b27ff3b732133287f6831e2af47cf342b7ef1f3fcdee248811090cd" ])
+ , ("SHA3-256", sha3Hash 256, [
+ "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
+ "ed6c07f044d7573cc53bf1276f8cba3dac497919597a45b4599c8f73e22aa334" ])
+ , ("SHA3-384", sha3Hash 384, [
+ "2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff",
+ "283990fa9d5fb731d786c5bbee94ea4db4910f18c62c03d173fc0a5e494422e8a0b3da7574dae7fa0baf005e504063b3",
+ "1cc515e1812491058d8b8b226fd85045e746b4937a58b0111b6b7a39dd431b6295bd6b6d05e01e225586b4dab3cbb87a" ])
+ , ("SHA3-512", sha3Hash 512, [
+ "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e",
+ "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609",
+ "10f8caabb5b179861da5e447d34b84d604e3eb81830880e1c2135ffc94580a47cb21f6243ec0053d58b1124d13af2090033659075ee718e0f111bb3f69fb24cf" ])
]
hexalise s = concatMap (\c -> [ hex $ c `div` 16, hex $ c `mod` 16 ]) s
View
150 cbits/sha3.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include "bitfn.h"
+#include "sha3.h"
+
+#define KECCAK_NB_ROUNDS 24
+
+/* rounds constants */
+static const uint64_t keccak_rndc[24] =
+{
+ 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
+ 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
+ 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
+ 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
+ 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
+ 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
+ 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
+ 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL,
+};
+
+/* triangular numbers constants */
+static const int keccak_rotc[24] =
+ { 1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44 };
+
+static const int keccak_piln[24] =
+ { 10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1 };
+
+static inline void sha3_do_chunk(uint64_t state[25], uint64_t buf[], int bufsz)
+{
+ int i, j, r;
+ uint64_t tmp, bc[5];
+
+ /* merge buf with state */
+ for (i = 0; i < bufsz; i++)
+ state[i] ^= buf[i];
+
+ /* run keccak rounds */
+ for (r = 0; r < KECCAK_NB_ROUNDS; r++) {
+ /* compute the parity of each columns */
+ for (i = 0; i < 5; i++)
+ bc[i] = state[i] ^ state[i+5] ^ state[i+10] ^ state[i+15] ^ state[i+20];
+
+ for (i = 0; i < 5; i++) {
+ tmp = bc[(i + 4) % 5] ^ rol64(bc[(i + 1) % 5], 1);
+ for (j = 0; j < 25; j += 5)
+ state[j + i] ^= tmp;
+ }
+
+ /* rho pi */
+ tmp = state[1];
+ for (i = 0; i < 24; i++) {
+ j = keccak_piln[i];
+ bc[0] = state[j];
+ state[j] = rol64(tmp, keccak_rotc[i]);
+ tmp = bc[0];
+ }
+
+ /* bitwise combine along rows using a = a xor (not b and c) */
+ for (j = 0; j < 25; j += 5) {
+ for (i = 0; i < 5; i++)
+ bc[i] = state[j + i];
+ #define andn(b,c) (~(b) & (c))
+ state[j + 0] ^= andn(bc[1], bc[2]);
+ state[j + 1] ^= andn(bc[2], bc[3]);
+ state[j + 2] ^= andn(bc[3], bc[4]);
+ state[j + 3] ^= andn(bc[4], bc[0]);
+ state[j + 4] ^= andn(bc[0], bc[1]);
+ #undef andn
+ }
+
+ /* xor the round constant */
+ state[0] ^= keccak_rndc[r];
+ }
+}
+
+void sha3_init(struct sha3_ctx *ctx, uint32_t hashlen)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->hashlen = hashlen / 8;
+ ctx->bufsz = 200 - 2 * ctx->hashlen;
+}
+
+void sha3_update(struct sha3_ctx *ctx, uint8_t *data, uint32_t len)
+{
+ uint32_t to_fill;
+
+ to_fill = ctx->bufsz - ctx->bufindex;
+
+ if (ctx->bufindex == ctx->bufsz) {
+ sha3_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+ ctx->bufindex = 0;
+ }
+
+ /* process partial buffer if there's enough data to make a block */
+ if (ctx->bufindex && len >= to_fill) {
+ memcpy(ctx->buf + ctx->bufindex, data, to_fill);
+ sha3_do_chunk(ctx->state, (uint64_t *) ctx->buf, ctx->bufsz / 8);
+ len -= to_fill;
+ data += to_fill;
+ ctx->bufindex = 0;
+ }
+
+ /* process as much ctx->bufsz-block as possible except the last one in case we finalize */
+ for (; len > ctx->bufsz; len -= ctx->bufsz, data += ctx->bufsz)
+ sha3_do_chunk(ctx->state, (uint64_t *) data, ctx->bufsz / 8);
+
+ /* append data into buf */
+ if (len) {
+ memcpy(ctx->buf + ctx->bufindex, data, len);
+ ctx->bufindex += len;
+ }
+}
+
+void sha3_finalize(struct sha3_ctx *ctx, uint8_t *out)
+{
+ /* add the 10*1 padding */
+ ctx->buf[ctx->bufindex++] = 1;
+ 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 */
+ memcpy(out, ctx->state, ctx->hashlen);
+}
View
45 cbits/sha3.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Vincent Hanquez <vincent@snarc.org>
+ *
+ * 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 CRYPTOHASH_SHA3_H
+#define CRYPTOHASH_SHA3_H
+
+#include <stdint.h>
+
+struct sha3_ctx
+{
+ uint32_t hashlen; /* in bytes */
+ uint32_t bufindex;
+ uint64_t state[25];
+ uint32_t bufsz;
+ uint32_t _padding;
+ uint8_t buf[144]; /* minimum SHA3-224, otherwise buffer need increases */
+};
+
+#define SHA3_CTX_SIZE sizeof(struct sha3_ctx)
+
+void sha3_init(struct sha3_ctx *ctx, uint32_t hashlen);
+void sha3_update(struct sha3_ctx *ctx, uint8_t *data, uint32_t len);
+void sha3_finalize(struct sha3_ctx *ctx, uint8_t *out);
+
+#endif
View
6 cryptohash.cabal
@@ -20,7 +20,7 @@ data-files: README.md
extra-source-files:
cbits/bitfn.h cbits/md2.h cbits/md4.h cbits/md5.h
- cbits/ripemd.h cbits/sha1.h cbits/sha256.h cbits/sha512.h
+ cbits/ripemd.h cbits/sha1.h cbits/sha256.h cbits/sha512.h cbits/sha3.h
cbits/skein.h cbits/skein256.h cbits/skein512.h
cbits/tiger.h cbits/whirlpool.h
@@ -50,6 +50,7 @@ Library
Crypto.Hash.SHA384
Crypto.Hash.SHA512
Crypto.Hash.SHA512t
+ Crypto.Hash.SHA3
Crypto.Hash.MD2
Crypto.Hash.MD4
Crypto.Hash.MD5
@@ -63,6 +64,7 @@ Library
C-sources: cbits/sha1.c
cbits/sha256.c
cbits/sha512.c
+ cbits/sha3.c
cbits/md2.c
cbits/md4.c
cbits/md5.c
@@ -79,6 +81,7 @@ Executable Tests
C-sources: cbits/sha1.c
cbits/sha256.c
cbits/sha512.c
+ cbits/sha3.c
cbits/md2.c
cbits/md4.c
cbits/md5.c
@@ -102,6 +105,7 @@ Executable Bench
C-sources: cbits/sha1.c
cbits/sha256.c
cbits/sha512.c
+ cbits/sha3.c
cbits/md2.c
cbits/md4.c
cbits/md5.c

0 comments on commit 15b268d

Please sign in to comment.
Something went wrong with that request. Please try again.