hkdf (empty) → 0.0.1.1
raw patch · 6 files changed
+253/−0 lines, 6 filesdep +basedep +byteabledep +bytestringsetup-changed
Dependencies added: base, byteable, bytestring, cryptohash, hkdf, hspec
Files
- LICENSE +28/−0
- README.md +20/−0
- Setup.hs +2/−0
- hkdf.cabal +47/−0
- src/Crypto/HKDF.hs +62/−0
- test/Spec.hs +94/−0
+ LICENSE view
@@ -0,0 +1,28 @@+Copyright (c) 2015, Jiri Marsicek+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+ list of conditions and the following disclaimer.++* 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.++* Neither the name of hkdf nor the names of its+ contributors may be used to endorse or promote products derived from+ this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.+
+ README.md view
@@ -0,0 +1,20 @@+# haskell hkdf+Implementation of HKDF (RFC 5869)++## Description++This is Haskell implementation of [RFC 5869](https://tools.ietf.org/html/rfc5869) (HMAC-based Extract-and-Expand Key Derivation Function (HKDF)).++Implementation is based on [cryptohash](https://hackage.haskell.org/package/cryptohash).++## Installation++Run either `cabal install` or `stack install`. This is library, thus there is no executable available.++## Running tests++Tests can be run via `stack test`.++## Discalmer++Any feedback is very welcomed.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hkdf.cabal view
@@ -0,0 +1,47 @@+name: hkdf+version: 0.0.1.1+synopsis: Implementation of HKDF (RFC 5869)+description: Please see README.md+homepage: http://github.com/j1r1k/hkdf+license: BSD3+license-file: LICENSE+author: Jiri Marsicek <jiri.marsicek@gmail.com>+maintainer: Jiri Marsicek <jiri.marsicek@gmail.com>+copyright: Jiri Marsicek <jiri.marsicek@gmail.com>+category: Cryptography,Web+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Crypto.HKDF+ build-depends: base >= 4.7 && < 5+ , byteable >= 0.1+ , bytestring >= 0.9+ , cryptohash >= 0.10+ ghc-options: -Wall+ default-language: Haskell2010+++test-suite hkdf-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base >= 4.7 && < 5+ , byteable >= 0.1+ , bytestring >= 0.9+ , cryptohash >= 0.10+ , hkdf+ , hspec+ ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall+ default-language: Haskell2010++source-repository this+ type: git+ location: https://github.com/j1r1k/hkdf+ tag: 0.0.1.1++source-repository head+ type: git+ location: https://github.com/j1r1k/hkdf
+ src/Crypto/HKDF.hs view
@@ -0,0 +1,62 @@+-- |+-- Module: Crypto.HKDF+-- Maintainer: Jiri Marsicek <jiri.marsicek@gmail.com>+--+-- This module provides implementation of HKDF function defined in+-- RFC-5869 (<http://www.ietf.org/rfc/rfc5869.txt>),+-- It is using "HashAlgorithm" from "cryptohash" as underlying implementation+--+module Crypto.HKDF+ ( hkdfExtract+ , hkdfExpand+ , hkdf+ ) where++import Crypto.Hash (HashAlgorithm)+import Crypto.MAC (HMAC, hmacAlg)+import Data.Byteable (toBytes)+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS (concat, empty, length, take)+import qualified Data.ByteString.Char8 as C8 (singleton)+import Data.Char (chr)++-- | Extract function. +--+-- Synonym to 'hmacAlg'+hkdfExtract :: (HashAlgorithm a) => a -- ^ hash algorithm+ -> ByteString -- ^ optional salt value (a non-secret random value)+ -> ByteString -- ^ input keying material+ -> HMAC a -- ^ a pseudorandom key+hkdfExtract = hmacAlg++-- | Expand function.+--+-- "Nothing" is returned in case (length of output > 255 * hash length)+hkdfExpand :: (HashAlgorithm a) => a -- ^ hash algorithm+ -> ByteString -- ^ pseudorandom key+ -> ByteString -- ^ info+ -> Int -- ^ length of output keying material in octets+ -> Maybe ByteString -- ^ output keying material+hkdfExpand alg prk info l+ | l <= 255 * chunkSize = Just $ BS.take l $ BS.concat $ take (l `div` chunkSize + 2) hkdfChunks+ | otherwise = Nothing+ where hkdfChunks = map fst $ iterate (hkdfSingle alg prk info) (BS.empty, 1)+ chunkSize = BS.length $ hkdfChunks !! 1++type HKDFIteration = (ByteString, Int)++hkdfSingle :: (HashAlgorithm a) => a -- ^ hash algorithm+ -> ByteString -- ^ pseudorandom key+ -> ByteString -- ^ info+ -> HKDFIteration -- ^ output of previous iteration+ -> HKDFIteration -- ^ output of current iteration+hkdfSingle alg prk info (prev, n) = (toBytes $ hmacAlg alg prk $ BS.concat [prev, info, C8.singleton $ chr n], n + 1)++-- | Function combining extract and expand functions.+hkdf :: (HashAlgorithm a) => a -- ^ hash algorithm+ -> ByteString -- ^ optional salt value (a non-secret random value)+ -> ByteString -- ^ input keying material+ -> ByteString -- ^ info+ -> Int -- ^ length of output keying material in octets+ -> Maybe ByteString -- ^ output keying material+hkdf alg salt ikm = hkdfExpand alg (toBytes $ hkdfExtract alg salt ikm)
+ test/Spec.hs view
@@ -0,0 +1,94 @@+import Data.Byteable (toBytes)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C8+import Data.Char (chr)+import Data.Maybe (fromMaybe)+import Crypto.Hash+import Crypto.HKDF+import Test.Hspec+import Text.Printf++hex :: BS.ByteString -> String+hex = concatMap (printf "%02x") . BS.unpack++convertOctets :: [Int] -> BS.ByteString+convertOctets = C8.pack . map chr++testCase :: (HashAlgorithm a) => a+ -> [Int] -- ^ ikm octets+ -> [Int] -- ^ salt octets+ -> [Int] -- ^ info octets+ -> Int -- ^ L+ -> (BS.ByteString, BS.ByteString) -- ^ (PRK, OKM)+testCase alg ikm salt info l = (prk, okm)+ where prk = toBytes $ hkdfExtract alg (convertOctets salt) (convertOctets ikm)+ okm = fromMaybe BS.empty $ hkdfExpand alg prk (convertOctets info) l++testCase1 :: (BS.ByteString, BS.ByteString)+testCase1 = testCase SHA256+ (replicate 22 0x0b) + [0x00 .. 0x0c] + [0xf0 .. 0xf9] + 42++testCase2 :: (BS.ByteString, BS.ByteString)+testCase2 = testCase SHA256+ [0x00 .. 0x4f]+ [0x60 .. 0xaf]+ [0xb0 .. 0xff]+ 82++testCase3 :: (BS.ByteString, BS.ByteString)+testCase3 = testCase SHA256+ (replicate 22 0x0b)+ []+ []+ 42++testCase4 :: (BS.ByteString, BS.ByteString)+testCase4 = testCase SHA1+ (replicate 11 0x0b)+ [0x00 .. 0x0c]+ [0xf0 .. 0xf9]+ 42++testCase5 :: (BS.ByteString, BS.ByteString)+testCase5 = testCase SHA1+ [0x00 .. 0x4f]+ [0x60 .. 0xaf]+ [0xb0 .. 0xff]+ 82++testCase6 :: (BS.ByteString, BS.ByteString)+testCase6 = testCase SHA1+ (replicate 22 0x0b)+ []+ []+ 42++testCase7 :: (BS.ByteString, BS.ByteString)+testCase7 = testCase SHA1+ (replicate 22 0x0c)+ []+ []+ 42++main :: IO ()+main = hspec $ do+ describe "hkdf-export" $ do+ it "should work for test case 1" $ hex (fst testCase1) `shouldBe` "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"+ it "should work for test case 2" $ hex (fst testCase2) `shouldBe` "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244"+ it "should work for test case 3" $ hex (fst testCase3) `shouldBe` "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04"+ it "should work for test case 4" $ hex (fst testCase4) `shouldBe` "9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243"+ it "should work for test case 5" $ hex (fst testCase5) `shouldBe` "8adae09a2a307059478d309b26c4115a224cfaf6"+ it "should work for test case 6" $ hex (fst testCase6) `shouldBe` "da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"+ it "should work for test case 7" $ hex (fst testCase7) `shouldBe` "2adccada18779e7c2077ad2eb19d3f3e731385dd"++ describe "hkdf-expand" $ do+ it "should work for test case 1" $ hex (snd testCase1) `shouldBe` "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"+ it "should work for test case 2" $ hex (snd testCase2) `shouldBe` "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87"+ it "should work for test case 3" $ hex (snd testCase3) `shouldBe` "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8"+ it "should work for test case 4" $ hex (snd testCase4) `shouldBe` "085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896"+ it "should work for test case 5" $ hex (snd testCase5) `shouldBe` "0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4"+ it "should work for test case 6" $ hex (snd testCase6) `shouldBe` "0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918"+ it "should work for test case 7" $ hex (snd testCase7) `shouldBe` "2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48"