diff --git a/Crypto/ECC.hs b/Crypto/ECC.hs index 576247d..5629bc4 100644 --- a/Crypto/ECC.hs +++ b/Crypto/ECC.hs @@ -49,7 +49,7 @@ data KeyPair curve = KeyPair } newtype SharedSecret = SharedSecret ScrubbedBytes - deriving (Eq, ByteArrayAccess) + deriving (Eq, ByteArrayAccess, NFData) class EllipticCurve curve where -- | Point on an Elliptic Curve diff --git a/Crypto/ECC/Simple/Types.hs b/Crypto/ECC/Simple/Types.hs index 653f4c2..814c256 100644 --- a/Crypto/ECC/Simple/Types.hs +++ b/Crypto/ECC/Simple/Types.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | -- Module : Crypto.ECC.Simple.Types -- License : BSD-style @@ -98,7 +99,7 @@ data CurveType = -- | ECC Private Number newtype Scalar curve = Scalar Integer - deriving (Show,Read,Eq,Data,Typeable) + deriving (Show,Read,Eq,Data,Typeable,NFData) -- | Define a point on a curve. data Point curve = diff --git a/Crypto/PubKey/DH.hs b/Crypto/PubKey/DH.hs index d066944..152b1b5 100644 --- a/Crypto/PubKey/DH.hs +++ b/Crypto/PubKey/DH.hs @@ -35,17 +35,20 @@ data Params = Params , params_bits :: Int } deriving (Show,Read,Eq,Data,Typeable) +instance NFData Params where + rnf (Params p g bits) = rnf p `seq` rnf g `seq` bits `seq` () + -- | Represent Diffie Hellman public number Y. newtype PublicNumber = PublicNumber Integer - deriving (Show,Read,Eq,Enum,Real,Num,Ord) + deriving (Show,Read,Eq,Enum,Real,Num,Ord,NFData) -- | Represent Diffie Hellman private number X. newtype PrivateNumber = PrivateNumber Integer - deriving (Show,Read,Eq,Enum,Real,Num,Ord) + deriving (Show,Read,Eq,Enum,Real,Num,Ord,NFData) -- | Represent Diffie Hellman shared secret. newtype SharedKey = SharedKey ScrubbedBytes - deriving (Show,Eq,ByteArrayAccess) + deriving (Show,Eq,ByteArrayAccess,NFData) -- | generate params from a specific generator (2 or 5 are common values) -- we generate a safe prime (a prime number of the form 2p+1 where p is also prime) diff --git a/Crypto/PubKey/ECC/P256.hs b/Crypto/PubKey/ECC/P256.hs index f1d8c32..e227c51 100644 --- a/Crypto/PubKey/ECC/P256.hs +++ b/Crypto/PubKey/ECC/P256.hs @@ -58,11 +58,11 @@ import qualified Crypto.Number.Serialize as S (os2ip, i2ospOf) -- | A P256 scalar newtype Scalar = Scalar ScrubbedBytes - deriving (Show,Eq,ByteArrayAccess) + deriving (Show,Eq,ByteArrayAccess,NFData) -- | A P256 point newtype Point = Point Bytes - deriving (Show,Eq) + deriving (Show,Eq,NFData) scalarSize :: Int scalarSize = 32 diff --git a/benchs/Bench.hs b/benchs/Bench.hs index 0c37803..718f356 100644 --- a/benchs/Bench.hs +++ b/benchs/Bench.hs @@ -1,6 +1,6 @@ -{-# LANGUAGE PackageImports #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} module Main where import Criterion.Main @@ -12,36 +12,37 @@ import qualified Crypto.Cipher.ChaChaPoly1305 as CP import Crypto.Cipher.DES import Crypto.Cipher.Twofish import Crypto.Cipher.Types +import Crypto.ECC import Crypto.Error import Crypto.Hash import qualified Crypto.KDF.PBKDF2 as PBKDF2 +import Crypto.Number.Basic (numBits) +import Crypto.Number.Generate +import qualified Crypto.PubKey.DH as DH import qualified Crypto.PubKey.ECC.Types as ECC import qualified Crypto.PubKey.ECC.Prim as ECC import Crypto.Random +import Control.DeepSeq (NFData) import Data.ByteArray (ByteArray, Bytes) import qualified Data.ByteString as B -import System.IO.Unsafe (unsafePerformIO) - import Number.F2m data HashAlg = forall alg . HashAlgorithm alg => HashAlg alg benchHash = - [ bgroup "1KB" $ map (doHashBench oneKB) hashAlgs - , bgroup "1MB" $ map (doHashBench oneMB) hashAlgs + [ env oneKB $ \b -> bgroup "1KB" $ map (doHashBench b) hashAlgs + , env oneMB $ \b -> bgroup "1MB" $ map (doHashBench b) hashAlgs ] where doHashBench b (name, HashAlg alg) = bench name $ nf (hashWith alg) b - oneKB :: Bytes - oneKB = unsafePerformIO (getRandomBytes 1024) - {-# NOINLINE oneKB #-} + oneKB :: IO Bytes + oneKB = getRandomBytes 1024 - oneMB :: Bytes - oneMB = unsafePerformIO (getRandomBytes $ 1024 * 1024) - {-# NOINLINE oneMB #-} + oneMB :: IO Bytes + oneMB = getRandomBytes $ 1024 * 1024 hashAlgs = [ ("MD2", HashAlg MD2) @@ -181,11 +182,61 @@ benchECC = n1 = 0x2ba9daf2363b2819e69b34a39cf496c2458a9b2a21505ea9e7b7cbca42dc7435 n2 = 0xf054a7f60d10b8c2cf847ee90e9e029f8b0e971b09ca5f55c4d49921a11fadc1 +benchFFDH = map doFFDHBench primes + where + doFFDHBench (e, p) = + let bits = numBits p + params = DH.Params { DH.params_p = p, DH.params_g = 2, DH.params_bits = bits } + in env (generate e params) $ bench (show bits) . nf (run params) + + generate e params = do + aPriv <- DH.PrivateNumber `fmap` generatePriv e + bPriv <- DH.PrivateNumber `fmap` generatePriv e + return (aPriv, DH.calculatePublic params bPriv) + + generatePriv e = generateParams e (Just SetHighest) False + + run params (priv, pub) = DH.getShared params priv pub + + -- RFC 7919: prime p with minimal size of exponent + primes = [ (225, 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF) + , (275, 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF) + , (325, 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF) + , (375, 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF) + , (400, 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF) + ] + +data CurveDH = forall c . (EllipticCurveDH c, NFData (Scalar c), NFData (Point c)) => CurveDH c + +benchECDH = map doECDHBench curves + where + doECDHBench (name, CurveDH c) = + let proxy = Just c -- using Maybe as Proxy + in env (generate proxy) $ bench name . nf (run proxy) + + generate proxy = do + KeyPair _ aScalar <- curveGenerateKeyPair proxy + KeyPair bPoint _ <- curveGenerateKeyPair proxy + return (aScalar, bPoint) + + run proxy (s, p) = throwCryptoError (ecdh proxy s p) + + curves = [ ("P256R1", CurveDH Curve_P256R1) + , ("P384R1", CurveDH Curve_P384R1) + , ("P521R1", CurveDH Curve_P521R1) + , ("X25519", CurveDH Curve_X25519) + , ("X448", CurveDH Curve_X448) + ] + main = defaultMain [ bgroup "hash" benchHash , bgroup "block-cipher" benchBlockCipher , bgroup "AE" benchAE , bgroup "pbkdf2" benchPBKDF2 , bgroup "ECC" benchECC + , bgroup "DH" + [ bgroup "FFDH" benchFFDH + , bgroup "ECDH" benchECDH + ] , bgroup "F2m" benchF2m ] diff --git a/cryptonite.cabal b/cryptonite.cabal index c85f26f..df26fa0 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -431,6 +431,7 @@ Benchmark bench-cryptonite Other-modules: Number.F2m Build-Depends: base >= 3 && < 5 , bytestring + , deepseq , memory , criterion , random