diff options
-rw-r--r-- | src/BTLS/Result.hs | 1 | ||||
-rw-r--r-- | src/BTLS/Types.hs | 6 | ||||
-rw-r--r-- | src/Codec/Crypto/HKDF.hs | 65 | ||||
-rw-r--r-- | src/Data/Digest.hs | 60 | ||||
-rw-r--r-- | src/Data/HMAC.hs | 39 | ||||
-rw-r--r-- | src/System/Random/Crypto.hs | 9 | ||||
-rw-r--r-- | tests/Data/HMACTests.hs | 6 |
7 files changed, 160 insertions, 26 deletions
diff --git a/src/BTLS/Result.hs b/src/BTLS/Result.hs index 63626bd..4f0238a 100644 --- a/src/BTLS/Result.hs +++ b/src/BTLS/Result.hs @@ -40,6 +40,7 @@ requireSuccess r = when (r /= 1) $ ioError (userError "BoringSSL failure") type Result = Either [Error] +-- | An error which occurred during processing. data Error = Error { err :: Err , file :: FilePath diff --git a/src/BTLS/Types.hs b/src/BTLS/Types.hs index 06b5173..44b29bc 100644 --- a/src/BTLS/Types.hs +++ b/src/BTLS/Types.hs @@ -31,7 +31,10 @@ newtype Algorithm = Algorithm (Ptr EVPMD) newtype AssociatedData = AssociatedData ByteString deriving (Eq, Ord, Show) --- | The result of a hash operation. +-- | The result of a hash operation. Equality comparisons on this type are +-- variable-time. +-- +-- The 'Show' instance for this type displays the digest as a hexadecimal string. newtype Digest = Digest ByteString deriving (Eq, Ord) @@ -46,6 +49,7 @@ instance Show Digest where newtype Salt = Salt ByteString deriving (Eq, Ord, Show) +-- | A special value used to request that no salt be used. noSalt :: Salt noSalt = Salt ByteString.empty diff --git a/src/Codec/Crypto/HKDF.hs b/src/Codec/Crypto/HKDF.hs index f40f707..bc1ea61 100644 --- a/src/Codec/Crypto/HKDF.hs +++ b/src/Codec/Crypto/HKDF.hs @@ -12,9 +12,44 @@ -- License for the specific language governing permissions and limitations under -- the License. +{-| + Module: Codec.Crypto.HKDF + Description: Hash-based key derivation + Copyright: 2018 Google LLC + License: Apache License, version 2.0 + + The hash-based key derivation function (HKDF), as specified in + [RFC 5869](https://tools.ietf.org/html/rfc5869). +-} module Codec.Crypto.HKDF - ( AssociatedData(AssociatedData), Salt(Salt), SecretKey(SecretKey), noSalt - , hkdf, extract, expand + ( -- * Computing keys + SecretKey(SecretKey) + , hkdf + , extract + , expand + + -- * Cryptographic hash algorithms + , Algorithm + , sha1 + + -- ** SHA-2 family + -- | The SHA-2 family of hash functions is defined in + -- [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final). + , sha224, sha256, sha384, sha512 + + -- * Salt + + -- | You may salt the hash used to generate the key. If you do not wish to + -- do so, specify 'noSalt' as the salt. + , Salt(Salt), noSalt + + -- * Associated data + -- | You may mix in arbitrary data when generating a key. If you do not wish + -- to do so, specify the empty string as the associated data. + , AssociatedData(AssociatedData) + + -- * Legacy functions + , md5 ) where import Foreign (allocaArray) @@ -27,12 +62,23 @@ import BTLS.Types ( Algorithm(Algorithm), AssociatedData(AssociatedData), Salt(Salt) , SecretKey(SecretKey), noSalt ) +import Data.Digest (md5, sha1, sha224, sha256, sha384, sha512) --- | Computes an HKDF as specified by RFC 5869. -hkdf :: Algorithm -> Salt -> AssociatedData -> Int -> SecretKey -> SecretKey +-- | Computes an HKDF. It is defined by +-- +-- prop> hkdf md salt info len = expand md info len . extract md salt +-- +-- but may be faster than calling the two functions individually. +hkdf :: + Algorithm + -> Salt + -> AssociatedData + -> Int -- ^ The length of the derived key, in bytes. + -> SecretKey + -> SecretKey hkdf md salt info outLen = expand md info outLen . extract md salt --- | Computes an HKDF pseudorandom key (PRK) as specified by RFC 5869. +-- | Computes an HKDF pseudorandom key (PRK). extract :: Algorithm -> Salt -> SecretKey -> SecretKey extract (Algorithm md) (Salt salt) (SecretKey secret) = SecretKey $ @@ -40,8 +86,13 @@ extract (Algorithm md) (Salt salt) (SecretKey secret) = onBufferOfMaxSize evpMaxMDSize $ \pOutKey pOutLen -> do hkdfExtract pOutKey pOutLen md secret salt --- | Computes HKDF output key material (OKM) as specified by RFC 5869. -expand :: Algorithm -> AssociatedData -> Int -> SecretKey -> SecretKey +-- | Computes HKDF output key material (OKM). +expand :: + Algorithm + -> AssociatedData + -> Int -- ^ The length of the OKM, in bytes. + -> SecretKey + -> SecretKey expand (Algorithm md) (AssociatedData info) outLen (SecretKey secret) = SecretKey $ unsafeLocalState $ diff --git a/src/Data/Digest.hs b/src/Data/Digest.hs index 336b38d..a17c438 100644 --- a/src/Data/Digest.hs +++ b/src/Data/Digest.hs @@ -12,15 +12,33 @@ -- License for the specific language governing permissions and limitations under -- the License. +{-| + Module: Data.Digest + Description: Cryptographic hash functions + Copyright: 2017 Google LLC + License: Apache License, version 2.0 + + Cryptographic hash functions. +-} module Data.Digest - ( Algorithm - , Digest + ( -- * Computing digests + Digest , hash + + -- * Digest algorithms + , Algorithm + + -- ** SHA-2 family + -- | The SHA-2 family of hash functions is defined in + -- [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final). + , sha224, sha256, sha384, sha512 + + -- * Legacy functions , md5 , sha1 - , sha224, sha256, sha384, sha512 ) where +import qualified Data.ByteString.Lazy as Lazy (ByteString) import qualified Data.ByteString.Lazy as ByteString.Lazy import Foreign (withForeignPtr) import Foreign.Marshal.Unsafe (unsafeLocalState) @@ -30,18 +48,44 @@ import BTLS.BoringSSL.Digest import BTLS.Buffer (onBufferOfMaxSize) import BTLS.Types (Algorithm(Algorithm), Digest(Digest)) -type LazyByteString = ByteString.Lazy.ByteString +-- | Message Digest 5, a 128-bit digest defined in +-- [RFC 1321](https://tools.ietf.org/html/rfc1321). This algorithm is +-- cryptographically broken; do not use it except to interface with legacy +-- applications. +md5 :: Algorithm +md5 = Algorithm evpMD5 + +-- | Secure Hash Algorithm 1, a 160-bit digest defined in +-- [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final). +-- Hashing with this algorithm is cryptographically broken, although +-- constructing HMACs with it is safe. +sha1 :: Algorithm +sha1 = Algorithm evpSHA1 -md5, sha1, sha224, sha256, sha384, sha512 :: Algorithm -md5 = Algorithm evpMD5 -sha1 = Algorithm evpSHA1 +-- | The SHA224 digest, a 224-bit digest and Secure Hash Algorithm 2 family +-- member. +sha224 :: Algorithm sha224 = Algorithm evpSHA224 + +-- | The SHA256 digest, a 256-bit digest and Secure Hash Algorithm 2 family +-- member. Prefer this algorithm on 32-bit CPUs; it will run faster than +-- 'sha384' or 'sha512'. +sha256 :: Algorithm sha256 = Algorithm evpSHA256 + +-- | The SHA384 digest, a 384-bit digest and Secure Hash Algorithm 2 family +-- member. +sha384 :: Algorithm sha384 = Algorithm evpSHA384 + +-- | The SHA512 digest, a 512-bit digest and Secure Hash Algorithm 2 family +-- member. Prefer this algorithm on 64-bit CPUs; it will run faster than +-- 'sha224' or 'sha256'. +sha512 :: Algorithm sha512 = Algorithm evpSHA512 -- | Hashes according to the given 'Algorithm'. -hash :: Algorithm -> LazyByteString -> Digest +hash :: Algorithm -> Lazy.ByteString -> Digest hash (Algorithm md) bytes = unsafeLocalState $ do ctxFP <- mallocEVPMDCtx diff --git a/src/Data/HMAC.hs b/src/Data/HMAC.hs index bf1bef8..fb67817 100644 --- a/src/Data/HMAC.hs +++ b/src/Data/HMAC.hs @@ -12,15 +12,43 @@ -- License for the specific language governing permissions and limitations under -- the License. +{-| + Module: Data.HMAC + Description: Hash-based message authentication codes + Copyright: 2018 Google LLC + License: Apache License, version 2.0 + + Hash-based message authentication codes (HMACs). An HMAC guarantees + authenticity but not confidentiality. +-} module Data.HMAC - ( SecretKey(SecretKey) - , HMAC, Result + ( -- * Computing HMACs + HMAC , hmac + + -- * Cryptographic hash algorithms + , Algorithm + , sha1 + + -- ** SHA-2 family + -- | The SHA-2 family of hash functions is defined in + -- [FIPS 180-4](https://csrc.nist.gov/publications/detail/fips/180/4/final). + , sha224, sha256, sha384, sha512 + + -- * Keys + , SecretKey(SecretKey) + + -- * Error handling + , Error + + -- * Legacy functions + , md5 ) where import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (runExceptT) import Data.ByteString (ByteString) +import qualified Data.ByteString.Lazy as Lazy (ByteString) import qualified Data.ByteString.Lazy as ByteString.Lazy import qualified Data.ByteString.Unsafe as ByteString import Foreign (withForeignPtr) @@ -31,10 +59,9 @@ import BTLS.BoringSSL.Digest (evpMaxMDSize) import BTLS.BoringSSL.HMAC import BTLS.BoringSSL.Mem (cryptoMemcmp) import BTLS.Buffer (onBufferOfMaxSize) -import BTLS.Result (Result, check) +import BTLS.Result (Error, check) import BTLS.Types (Algorithm(Algorithm), Digest(Digest), SecretKey(SecretKey)) - -type LazyByteString = ByteString.Lazy.ByteString +import Data.Digest (md5, sha1, sha224, sha256, sha384, sha512) -- | A hash-based message authentication code. Equality comparisons on this type -- are constant-time. @@ -51,7 +78,7 @@ instance Show HMAC where show (HMAC m) = show (Digest m) -- | Creates an HMAC according to the given 'Algorithm'. -hmac :: Algorithm -> SecretKey -> LazyByteString -> Result HMAC +hmac :: Algorithm -> SecretKey -> Lazy.ByteString -> Either [Error] HMAC hmac (Algorithm md) (SecretKey key) bytes = unsafeLocalState $ do ctxFP <- mallocHMACCtx diff --git a/src/System/Random/Crypto.hs b/src/System/Random/Crypto.hs index ed5706d..464555e 100644 --- a/src/System/Random/Crypto.hs +++ b/src/System/Random/Crypto.hs @@ -12,6 +12,12 @@ -- License for the specific language governing permissions and limitations under -- the License. +{-| + Module: System.Random.Crypto + Description: Cryptographically secure pseudorandom number generator + Copyright: 2018 Google LLC + License: Apache License, version 2.0 +-} module System.Random.Crypto ( randomBytes ) where @@ -22,7 +28,8 @@ import Foreign (allocaArray) import BTLS.BoringSSL.Rand (randBytes) import BTLS.Buffer (packCUStringLen) --- | Generates a cryptographically random buffer of the specified size. +-- | Generates a cryptographically random buffer of the specified size (in +-- bytes). randomBytes :: Int -> IO ByteString randomBytes len = allocaArray len $ \pBuf -> do diff --git a/tests/Data/HMACTests.hs b/tests/Data/HMACTests.hs index ea786c0..bcdd7a6 100644 --- a/tests/Data/HMACTests.hs +++ b/tests/Data/HMACTests.hs @@ -23,7 +23,7 @@ import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit ((@?=), testCase) import Data.Digest (md5, sha1, sha224, sha256, sha384, sha512) -import Data.HMAC (Result, SecretKey(SecretKey), hmac) +import Data.HMAC (Error, SecretKey(SecretKey), hmac) type LazyByteString = ByteString.Lazy.ByteString @@ -35,7 +35,7 @@ tests = testGroup "Data.HMAC" ] tableTestCase :: - (SecretKey -> LazyByteString -> Result String) + (SecretKey -> LazyByteString -> Either [Error] String) -> (SecretKey, LazyByteString, String) -> TestTree tableTestCase f (key, input, output) = @@ -176,7 +176,7 @@ testRFC4231 = testGroup "RFC 4231" $ truncatedRFC4231Test = let key = SecretKey (ByteString.replicate 20 0x0c) input = "Test With Truncation" :: LazyByteString - t f = take 32 <$> f key input :: Result String + t f = take 32 <$> f key input :: Either [Error] String in testGroup (abbreviate input) [ testCase "SHA-224" (t hmacSha224 @?= Right "0e2aea68a90c8d37c988bcdb9fca6fa8") , testCase "SHA-256" (t hmacSha256 @?= Right "a3b6167473100ee06e0c796c2955552b") |