Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 2c3f31f01c3c88fbe2f729580326b8525b7aa164 @vincenthz committed Jun 24, 2012
Showing with 217 additions and 0 deletions.
  1. +86 −0 Crypto/MAC/SipHash.hs
  2. +27 −0 LICENSE
  3. +31 −0 README.md
  4. +2 −0 Setup.hs
  5. +37 −0 Tests/Tests.hs
  6. +34 −0 siphash.cabal
@@ -0,0 +1,86 @@
+-- |
+-- Module : Crypto.MAC.SipHash
+-- License : BSD-style
+-- Maintainer : Vincent Hanquez <vincent@snarc.org>
+-- Stability : experimental
+-- Portability : good
+--
+-- provide the SipHash algorithm.
+-- reference: http://131002.net/siphash/siphash.pdf
+--
+module Crypto.MAC.SipHash
+ ( SipKey(..)
+ , SipHash(..)
+ , hashWith
+ , hash
+ ) where
+
+import Data.Word
+import Data.Bits
+import Data.List (foldl')
+import Data.Serialize.Get
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Control.Monad (foldM,replicateM)
+
+-- | SigHash Key
+data SipKey = SipKey {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
+
+-- | Siphash tag value
+newtype SipHash = SipHash Word64
+ deriving (Show,Eq)
+
+data InternalState = InternalState {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
+
+-- | same as @hash, except also specifies the number of sipround iterations for compression and digest.
+hashWith :: Int -> Int -> SipKey -> ByteString -> SipHash
+hashWith c d key b = either error (finish d) $ runGet runHash b
+ where len = B.length b
+ (nb,last) = len `divMod` 8
+
+ runHash = foldM (const . getBlock) (initSip key)[0..(nb-1)] >>= lastBlock
+ getBlock st = process c st `fmap` getWord64le
+ lastBlock st = do
+ let lengthBlock = fromIntegral (len `mod` 256) `shiftL` 56
+ z <- (fst . foldl shiftAndAdd (0,0)) `fmap` replicateM last getWord8
+ return $ process c st (lengthBlock .|. z)
+
+ shiftAndAdd :: (Word64,Int) -> Word8 -> (Word64,Int)
+ shiftAndAdd (acc,pos) v = (acc .|. ((fromIntegral v) `shiftL` pos), pos+8)
+
+-- | produce a siphash with a key and a bytestring.
+hash :: SipKey -> ByteString -> SipHash
+hash = hashWith 2 4
+
+initSip (SipKey k0 k1) = InternalState (k0 `xor` 0x736f6d6570736575)
+ (k1 `xor` 0x646f72616e646f6d)
+ (k0 `xor` 0x6c7967656e657261)
+ (k1 `xor` 0x7465646279746573)
+
+doRound (InternalState v0 v1 v2 v3) = do
+ let v0' = v0 + v1
+ v2' = v2 + v3
+ v1' = v1 `rotateL` 13
+ v3' = v3 `rotateL` 16
+ v1'' = v1' `xor` v0'
+ v3'' = v3' `xor` v2'
+ v0'' = v0' `rotateL` 32
+ v2'' = v2' + v1''
+ v0''' = v0'' + v3''
+ v1''' = v1'' `rotateL` 17
+ v3''' = v3'' `rotateL` 21
+ v1'''' = v1''' `xor` v2''
+ v3'''' = v3''' `xor` v0'''
+ v2''' = v2'' `rotateL` 32
+ in InternalState v0''' v1'''' v2''' v3''''
+
+runRounds n st = foldl' (const . doRound) st [0..(n-1)]
+
+process c istate m = newState
+ where newState = postInject $ runRounds c $ preInject $ istate
+ preInject (InternalState v0 v1 v2 v3) = InternalState v0 v1 v2 (v3 `xor` m)
+ postInject (InternalState v0 v1 v2 v3) = InternalState (v0 `xor` m) v1 v2 v3
+
+finish d istate = getDigest $ runRounds d $ preInject istate
+ where getDigest (InternalState v0 v1 v2 v3) = SipHash (v0 `xor` v1 `xor` v2 `xor` v3)
+ preInject (InternalState v0 v1 v2 v3) = InternalState v0 v1 (v2 `xor` 0xff) v3
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 Vincent Hanquez <vincent@snarc.org>
+
+All rights reserved.
+
+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.
+3. Neither the name of the author nor the names of his contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 AUTHORS OR CONTRIBUTORS 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.
@@ -0,0 +1,31 @@
+Haskell Siphash
+===============
+
+Simple module to compute the [SipHash](http://131002.net/siphash/siphash.pdf)
+algorithm.
+
+Install
+-------
+
+ cabal install sighash
+
+Usage
+-----
+
+sighash-2-4:
+
+ import Crypto.MAC.SigHash (hash)
@nagisa

nagisa Dec 14, 2012

Contributor

Did you mean Sip instead of Sig here as well as everywhere else in the file?

@vincenthz

vincenthz Dec 15, 2012

Owner

yes definitely. good spotting ! i'm thinking, fingers are too trained to type Sig....

+ import qualified Data.ByteString.Char8 as B
+
+ k0 = 0xaaaaaaaaaaaaaaaa
+ k1 = 0xbbbbbbbbbbbbbbbb
+ tag = hash (SigKey k0 k1) (B.pack "my text to hash")
+
+sighash-c-d:
+
+ import Crypto.MAC.SigHash (hash)
+ import qualified Data.ByteString.Char8 as B
+
+ k0 = 0xaaaaaaaaaaaaaaaa
+ k1 = 0xbbbbbbbbbbbbbbbb
+ tag = hashWith nbCompressionRounds nbDigestRounds (SigKey k0 k1) (B.pack "my text to hash")
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
@@ -0,0 +1,37 @@
+{-# LANGUAGE ViewPatterns #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import Control.Applicative
+import Control.Monad
+
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B
+import Crypto.MAC.SipHash
+
+assertEq expected got
+ | expected == got = True
+ | otherwise = error ("expected: " ++ show expected ++ " got: " ++ show got)
+
+vectors =
+ [ ( SipKey 0x0706050403020100 0x0f0e0d0c0b0a0908
+ , "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
+ , SipHash 0xa129ca6149be45e5
+ )
+ ]
+
+katTests v = map (testProperty "kat" . makeTest) v
+ where makeTest (key,msg,tag) = assertEq tag $ hash key msg
+
+tests =
+ [ testGroup "KAT" $ katTests vectors
+ ]
+
+main = defaultMain tests
@@ -0,0 +1,34 @@
+Name: siphash
+Version: 1.0.0
+Description:
+ Haskell implementation of siphash. [http://131002.net/siphash/siphash.pdf]
+License: BSD3
+License-file: LICENSE
+Copyright: Vincent Hanquez <vincent@snarc.org>
+Author: Vincent Hanquez <vincent@snarc.org>
+Maintainer: Vincent Hanquez <vincent@snarc.org>
+Synopsis: siphash: a fast short input PRF
+Category: Data, Cryptography
+Build-Type: Simple
+Cabal-Version: >=1.8
+Homepage: http://github.com/vincenthz/hs-siphash
+data-files: README.md
+
+Library
+ Build-Depends: base >= 4 && < 6, bytestring, cereal
+ Exposed-modules: Crypto.MAC.SipHash
+
+Test-Suite test-siphash
+ type: exitcode-stdio-1.0
+ hs-source-dirs: Tests
+ Main-Is: Tests.hs
+ Build-depends: base >= 4 && < 5
+ , siphash
+ , bytestring
+ , QuickCheck >= 2
+ , test-framework >= 0.3.3 && < 0.7
+ , test-framework-quickcheck2 >= 0.2.9 && < 0.3
+
+source-repository head
+ type: git
+ location: git://github.com/vincenthz/hs-siphash

0 comments on commit 2c3f31f

Please sign in to comment.