crypto-pubkey 0.2.2 → 0.2.3
raw patch · 12 files changed
+336/−49 lines, 12 filesdep +deepseqdep ~crypto-pubkey-types
Dependencies added: deepseq
Dependency ranges changed: crypto-pubkey-types
Files
- Benchs/Bench.hs +15/−0
- Benchs/PregenKeys.hs +19/−0
- Crypto/PubKey/DH.hs +3/−3
- Crypto/PubKey/ECC/ECDSA.hs +81/−0
- Crypto/PubKey/ECC/Generate.hs +29/−0
- Crypto/PubKey/ECC/Prim.hs +68/−0
- Tests/KAT.hs +2/−0
- Tests/KAT/ECDSA.hs +66/−0
- Tests/PregenKeys.hs +19/−0
- Tests/T.hs +0/−44
- Tests/Tests.hs +26/−0
- crypto-pubkey.cabal +8/−2
Benchs/Bench.hs view
@@ -10,13 +10,18 @@ import Crypto.PubKey.RSA.OAEP as OAEP import Crypto.PubKey.RSA.PSS as PSS import Crypto.PubKey.HashDescr+import Crypto.PubKey.ECC.ECDSA as ECDSA import Crypto.Random+import Control.DeepSeq import qualified Data.ByteString as B right (Right r) = r right (Left _) = error "left received" +instance NFData Signature where+ rnf (Signature r s) = rnf r `seq` rnf s+ main = do rng <- cprgCreate `fmap` createEntropyPool :: IO SystemRNG let !bs = B.replicate 32 0@@ -28,6 +33,8 @@ !blinder = fst $ generateBlinder rng (RSA.public_n rsaPublickey) oaepParams = OAEP.defaultOAEPParams SHA1.hash pssParams = PSS.defaultPSSParamsSHA1+ ecdsaSignatureP = fst $ ECDSA.sign rng ecdsaPrivatekeyP SHA1.hash bs+ ecdsaSignatureB = fst $ ECDSA.sign rng ecdsaPrivatekeyB SHA1.hash bs defaultMain [ bgroup "RSA PKCS15" [ bench "encryption" $ nf (right . fst . PKCS15.encrypt rng rsaPublickey) bs@@ -62,5 +69,13 @@ , bench "fast+blinding" $ nf (right . fst . PSS.sign rng (Just blinder) pssParams rsaPrivatekey) bs ] , bench "verify" $ nf (PSS.verify pssParams rsaPublickey bs) signedMsgPSS+ ]+ , bgroup "ECDSA secp160r1"+ [ bench "sign" $ nf (fst . ECDSA.sign rng ecdsaPrivatekeyP SHA1.hash) bs+ , bench "verify" $ nf (ECDSA.verify SHA1.hash ecdsaPublickeyP ecdsaSignatureP) bs+ ]+ , bgroup "ECDSA sect163k1"+ [ bench "sign" $ nf (fst . ECDSA.sign rng ecdsaPrivatekeyB SHA1.hash) bs+ , bench "verify" $ nf (ECDSA.verify SHA1.hash ecdsaPublickeyB ecdsaSignatureB) bs ] ]
Benchs/PregenKeys.hs view
@@ -3,6 +3,8 @@ import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA import qualified Crypto.PubKey.DH as DH+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.Types.PubKey.ECC as ECC rsaPrivatekey = RSA.PrivateKey { RSA.private_pub = rsaPublickey@@ -36,3 +38,20 @@ , DSA.public_y = 0x4fa505e86e32922f1fa1702a120abdba088bb4be801d4c44f7fc6b9094d85cd52c429cbc2b39514e30909b31e2e2e0752b0fc05c1a7d9c05c3e52e49e6edef4c } +ecdsaCurveP = ECC.getCurveByName ECC.SEC_p160r1++ecdsaPrivatekeyP = ECDSA.PrivateKey ecdsaCurveP 971761939728640320549601132085879836204587084162++ecdsaPublickeyP = ECDSA.PublicKey+ ecdsaCurveP+ (ECC.Point 466448783855397898016055842232266600516272889280+ 1110706324081757720403272427311003102474457754220)++ecdsaCurveB = ECC.getCurveByName ECC.SEC_t163k1++ecdsaPrivatekeyB = ECDSA.PrivateKey ecdsaCurveB 5321230001203043918714616464614664646674949479949++ecdsaPublickeyB = ECDSA.PublicKey+ ecdsaCurveB+ (ECC.Point 0x37d529fa37e42195f10111127ffb2bb38644806bc+ 0x447026eee8b34157f3eb51be5185d2be0249ed776)
Crypto/PubKey/DH.hs view
@@ -20,7 +20,7 @@ import Crypto.Number.ModArithmetic (expSafe) import Crypto.Number.Prime (generateSafePrime)-import Crypto.Number.Generate (generateOfSize)+import Crypto.Number.Generate (generateMax) import Crypto.Types.PubKey.DH import Crypto.Random.API import Control.Arrow (first)@@ -33,8 +33,8 @@ -- | generate a private number with no specific property -- this number is usually called X in DH text.-generatePrivate :: CPRG g => g -> Int -> (PrivateNumber, g)-generatePrivate rng bits = first PrivateNumber $ generateOfSize rng bits+generatePrivate :: CPRG g => g -> Params -> (PrivateNumber, g)+generatePrivate rng (Params p _) = first PrivateNumber $ generateMax rng p -- | generate a public number that is for the other party benefits. -- this number is usually called Y in DH text.
+ Crypto/PubKey/ECC/ECDSA.hs view
@@ -0,0 +1,81 @@+-- | /WARNING:/ Signature operations may leak the private key. Signature verification+-- should be safe.+module Crypto.PubKey.ECC.ECDSA+ ( module Crypto.Types.PubKey.ECDSA+ , signWith+ , sign+ , verify+ ) where++import Control.Monad+import Crypto.Random.API+import Data.Bits (shiftR)+import Data.ByteString (ByteString)+import Crypto.Number.ModArithmetic (inverse)+import Crypto.Number.Serialize+import Crypto.Number.Generate+import Crypto.Types.PubKey.ECDSA+import Crypto.Types.PubKey.ECC+import Crypto.PubKey.HashDescr+import Crypto.PubKey.ECC.Prim++-- | Sign message using the private key and an explicit k number.+--+-- /WARNING:/ Vulnerable to timing attacks.+signWith :: Integer -- ^ k random number+ -> PrivateKey -- ^ private key+ -> HashFunction -- ^ hash function+ -> ByteString -- ^ message to sign+ -> Maybe Signature+signWith k (PrivateKey curve d) hash msg = do+ let z = tHash hash msg n+ CurveCommon _ _ g n _ = common_curve curve+ let point = pointMul curve k g+ r <- case point of+ PointO -> Nothing+ Point x _ -> return $ x `mod` n+ kInv <- inverse k n+ let s = kInv * (z + r * d) `mod` n+ when (r == 0 || s == 0) Nothing+ return $ Signature r s++-- | Sign message using the private key.+--+-- /WARNING:/ Vulnerable to timing attacks.+sign :: CPRG g => g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)+sign rng pk hash msg =+ case signWith k pk hash msg of+ Nothing -> sign rng' pk hash msg+ Just sig -> (sig, rng')+ where n = ecc_n . common_curve $ private_curve pk+ (k, rng') = generateBetween rng 1 (n - 1)++-- | Verify a bytestring using the public key.+verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool+verify _ (PublicKey _ PointO) _ _ = False+verify hash pk@(PublicKey curve q) (Signature r s) msg+ | r < 1 || r >= n || s < 1 || s >= n = False+ | otherwise = maybe False (r ==) $ do+ w <- inverse s n+ let z = tHash hash msg n+ u1 = z * w `mod` n+ u2 = r * w `mod` n+ -- TODO: Use Shamir's trick+ g' = pointMul curve u1 g+ q' = pointMul curve u2 q+ x = pointAdd curve g' q'+ case x of+ PointO -> Nothing+ Point x1 _ -> return $ x1 `mod` n+ where n = ecc_n cc+ g = ecc_g cc+ cc = common_curve $ public_curve pk++-- | Truncate and hash.+tHash :: HashFunction -> ByteString -> Integer -> Integer+tHash hash m n+ | d > 0 = shiftR e d+ | otherwise = e+ where e = os2ip $ hash m+ d = log2 e - log2 n+ log2 = ceiling . logBase (2 :: Double) . fromIntegral
+ Crypto/PubKey/ECC/Generate.hs view
@@ -0,0 +1,29 @@+-- | Signature generation.+module Crypto.PubKey.ECC.Generate where++import Crypto.Random (CPRG)+import Crypto.Types.PubKey.ECC+import Crypto.Types.PubKey.ECDSA+import Crypto.Number.Generate+import Crypto.PubKey.ECC.Prim++-- | Generate Q given d.+--+-- /WARNING:/ Vulnerable to timing attacks.+generateQ :: Curve+ -> Integer+ -> Point+generateQ curve d = pointMul curve d g+ where g = ecc_g $ common_curve curve++-- | Generate a pair of (private, public) key.+--+-- /WARNING:/ Vulnerable to timing attacks.+generate :: CPRG g+ => g -- ^ CPRG+ -> Curve -- ^ Elliptic Curve+ -> ((PublicKey, PrivateKey), g)+generate rng curve = ((PublicKey curve q, PrivateKey curve d), rng')+ where (d, rng') = generateBetween rng 1 (n - 1)+ q = generateQ curve d+ n = ecc_n $ common_curve curve
+ Crypto/PubKey/ECC/Prim.hs view
@@ -0,0 +1,68 @@+-- | Elliptic Curve Arithmetic.+--+-- /WARNING:/ These functions are vulnerable to timing attacks.+module Crypto.PubKey.ECC.Prim+ ( pointAdd+ , pointDouble+ , pointMul+ ) where++import Data.Maybe+import Crypto.Number.ModArithmetic+import Crypto.Number.F2m+import Crypto.Types.PubKey.ECC++-- | Elliptic Curve point addition.+--+-- /WARNING:/ Vulnerable to timing attacks.+pointAdd :: Curve -> Point -> Point -> Point+pointAdd _ PointO PointO = PointO+pointAdd _ PointO q = q+pointAdd _ p PointO = p+pointAdd c@(CurveFP (CurvePrime pr _)) p@(Point xp yp) q@(Point xq yq)+ | p == Point xq (-yq) = PointO+ | p == q = pointDouble c p+ | otherwise = let s = divmod (yp - yq) (xp - xq) pr+ xr = (s ^ (2::Int) - xp - xq) `mod` pr+ yr = (s * (xp - xr) - yp) `mod` pr+ in Point xr yr+pointAdd c@(CurveF2m (CurveBinary fx cc)) p@(Point xp yp) q@(Point xq yq)+ | p == Point xq (xq `addF2m` yq) = PointO+ | p == q = pointDouble c p+ | otherwise = fromMaybe PointO $ do+ s <- divF2m fx (yp `addF2m` yq) (xp `addF2m` xq)+ let xr = mulF2m fx s s `addF2m` s `addF2m` xp `addF2m` xq `addF2m` a+ yr = mulF2m fx s (xp `addF2m` xr) `addF2m` xr `addF2m` yp+ return $ Point xr yr+ where a = ecc_a cc++-- | Elliptic Curve point doubling.+--+-- /WARNING:/ Vulnerable to timing attacks.+pointDouble :: Curve -> Point -> Point+pointDouble _ PointO = PointO+pointDouble (CurveFP (CurvePrime pr cc)) (Point xp yp) =+ let l = divmod (3 * xp ^ (2::Int) + a) (2 * yp) pr+ xr = (l ^ (2::Int) - 2 * xp) `mod` pr+ yr = (l * (xp - xr) - yp) `mod` pr+ in Point xr yr+ where a = ecc_a cc+pointDouble (CurveF2m (CurveBinary fx cc)) (Point xp yp) = fromMaybe PointO $ do+ s <- return . addF2m xp =<< divF2m fx yp xp+ let xr = mulF2m fx s s `addF2m` s `addF2m` a+ yr = mulF2m fx xp xp `addF2m` mulF2m fx xr (s `addF2m` 1)+ return $ Point xr yr+ where a = ecc_a cc++-- | Elliptic curve point multiplication (double and add algorithm).+--+-- /WARNING:/ Vulnerable to timing attacks.+pointMul :: Curve -> Integer -> Point -> Point+pointMul c n p+ | n == 1 = p+ | odd n = pointAdd c p (pointMul c (n - 1) p)+ | otherwise = pointMul c (n `div` 2) (pointDouble c p)++-- | div and mod+divmod :: Integer -> Integer -> Integer -> Integer+divmod y x m = y * fromJust (inverse (x `mod` m) m) `mod` m
Tests/KAT.hs view
@@ -19,6 +19,7 @@ import KAT.OAEP import KAT.PSS import KAT.DSA+import KAT.ECDSA data VectorMgf = VectorMgf { seed :: ByteString , dbMask :: ByteString@@ -39,4 +40,5 @@ , pssTests , oaepTests , dsaTests+ , ecdsaTests ]
+ Tests/KAT/ECDSA.hs view
@@ -0,0 +1,66 @@+-- Test vectors are taken from GEC2: www.secg.org/collateral/gec2.pdf+{-# LANGUAGE OverloadedStrings #-}+module KAT.ECDSA (ecdsaTests) where++import Data.ByteString (ByteString)++import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.Types.PubKey.ECC as ECC+import qualified Crypto.Hash.SHA1 as SHA1++import Test.HUnit+import Test.Framework (testGroup)+import Test.Framework.Providers.HUnit (testCase)++data VectorECDSA = VectorECDSA+ { curve :: ECC.Curve+ , msg :: ByteString+ , d :: Integer+ , q :: ECC.Point+ , k :: Integer+ , r :: Integer+ , s :: Integer+ }++vectorsSHA1 =+ [ VectorECDSA+ { curve = ECC.getCurveByName ECC.SEC_p160r1+ , msg = "abc"+ , d = 971761939728640320549601132085879836204587084162+ , q = ECC.Point 466448783855397898016055842232266600516272889280+ 1110706324081757720403272427311003102474457754220+ , k = 702232148019446860144825009548118511996283736794+ , r = 1176954224688105769566774212902092897866168635793+ , s = 299742580584132926933316745664091704165278518100+ }+ , VectorECDSA+ { curve = ECC.getCurveByName ECC.SEC_t163k1+ , msg = "abc"+ , d = 5321230001203043918714616464614664646674949479949+ , q = ECC.Point 0x037d529fa37e42195f10111127ffb2bb38644806bc+ 0x0447026eee8b34157f3eb51be5185d2be0249ed776+ , k = 936523985789236956265265265235675811949404040044+ , r = 875196600601491789979810028167552198674202899628+ , s = 1935199835333115956886966454901154618180070051199+ }+ ]++vectorToPrivate :: VectorECDSA -> ECDSA.PrivateKey+vectorToPrivate vector = ECDSA.PrivateKey (curve vector) (d vector)++vectorToPublic :: VectorECDSA -> ECDSA.PublicKey+vectorToPublic vector = ECDSA.PublicKey (curve vector) (q vector)++doSignatureTest (i, vector) = testCase (show i) (expected @=? actual)+ where expected = Just $ ECDSA.Signature (r vector) (s vector)+ actual = ECDSA.signWith (k vector) (vectorToPrivate vector) SHA1.hash (msg vector)++doVerifyTest (i, vector) = testCase (show i) (True @=? actual)+ where actual = ECDSA.verify SHA1.hash (vectorToPublic vector) (ECDSA.Signature (r vector) (s vector)) (msg vector)++ecdsaTests = testGroup "DSA"+ [ testGroup "SHA1"+ [ testGroup "signature" $ map doSignatureTest (zip [0..] vectorsSHA1)+ , testGroup "verify" $ map doVerifyTest (zip [0..] vectorsSHA1)+ ]+ ]
Tests/PregenKeys.hs view
@@ -2,6 +2,8 @@ import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.Types.PubKey.ECC as ECC import qualified Crypto.PubKey.DH as DH rsaPrivatekey = RSA.PrivateKey@@ -36,3 +38,20 @@ , DSA.public_y = 0x4fa505e86e32922f1fa1702a120abdba088bb4be801d4c44f7fc6b9094d85cd52c429cbc2b39514e30909b31e2e2e0752b0fc05c1a7d9c05c3e52e49e6edef4c } +ecdsaCurveP = ECC.getCurveByName ECC.SEC_p160r1++ecdsaPrivatekeyP = ECDSA.PrivateKey ecdsaCurveP 971761939728640320549601132085879836204587084162++ecdsaPublickeyP = ECDSA.PublicKey+ ecdsaCurveP+ (ECC.Point 466448783855397898016055842232266600516272889280+ 1110706324081757720403272427311003102474457754220)++ecdsaCurveB = ECC.getCurveByName ECC.SEC_t163k1++ecdsaPrivatekeyB = ECDSA.PrivateKey ecdsaCurveB 5321230001203043918714616464614664646674949479949++ecdsaPublickeyB = ECDSA.PublicKey+ ecdsaCurveB+ (ECC.Point 0x37d529fa37e42195f10111127ffb2bb38644806bc+ 0x447026eee8b34157f3eb51be5185d2be0249ed776)
− Tests/T.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE BangPatterns #-}-module Main where--import PregenKeys-import Control.Applicative-import qualified Crypto.Hash.SHA1 as SHA1-import qualified Crypto.PubKey.RSA as RSA-import qualified Crypto.PubKey.RSA.OAEP as RSAOAEP-import qualified Crypto.PubKey.RSA.PKCS15 as RSAPKCS15-import qualified Data.ByteString as B--import Crypto.Random--priv = rsaPrivatekey-pub = rsaPublickey-oaepParams = RSAOAEP.defaultOAEPParams SHA1.hash--doEncrypt = False--nbEncrypt = 944242-nbDecrypt = 64325--main = do- system <- cprgCreate <$> createEntropyPool :: IO SystemRNG- if doEncrypt- then do- putStrLn $ show $ justEncrypt system nbEncrypt B.empty- else do- let t = either (error . show) id $ fst $ RSAPKCS15.encrypt system pub msg- --let t = either (error . show) id $ RSAOAEP.encryptWithSeed (B.replicate 20 0) oaepParams pub msg- - putStrLn $ show $ decryptEncrypt nbDecrypt (t, msg)- where msg = B.replicate 10 4- decryptEncrypt 0 (!bEnc, !b) = b- decryptEncrypt n (!bEnc, !b) =- let bDec = either (error . show) id $ RSAPKCS15.decrypt Nothing priv bEnc in- --let bDec = either (error . show) id $ RSAOAEP.decrypt Nothing oaepParams priv bEnc in- decryptEncrypt (n-1) (bEnc, bDec)-- justEncrypt _ 0 (!bEnc) = bEnc- justEncrypt rng n (!bEnc) =- let bEnc2 = either (error . show) id $ fst $ RSAPKCS15.encrypt rng pub msg in- justEncrypt rng (n-1) bEnc2 -
Tests/Tests.hs view
@@ -22,6 +22,9 @@ import qualified Crypto.PubKey.RSA.PKCS15 as RSAPKCS15 import qualified Crypto.PubKey.RSA.OAEP as RSAOAEP import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import Crypto.Types.PubKey.ECC+import Crypto.PubKey.ECC.Generate import qualified Crypto.PubKey.DH as DH import Crypto.Number.Serialize (i2osp) import Crypto.PubKey.HashDescr@@ -116,6 +119,26 @@ prop_dsa_valid (RSAMessage _ msg) = DSA.verify (SHA1.hash) dsaPublickey signature msg where (signature, rng') = DSA.sign rng dsaPrivatekey (SHA1.hash) msg +prop_ecdsa_prime_valid (RSAMessage _ msg) = ECDSA.verify SHA1.hash ecdsaPublickeyP signature msg+ where (signature, rng') = ECDSA.sign rng ecdsaPrivatekeyP SHA1.hash msg++prop_ecdsa_binary_valid (RSAMessage _ msg) = ECDSA.verify SHA1.hash ecdsaPublickeyB signature msg+ where (signature, rng') = ECDSA.sign rng ecdsaPrivatekeyB SHA1.hash msg++prop_ecdsa_curve_valid keypair = ECDSA.verify SHA1.hash pubkey signature "test"+ where (signature, rng') = ECDSA.sign rng privkey SHA1.hash "test"+ pubkey = ECDSA.toPublicKey keypair+ privkey = ECDSA.toPrivateKey keypair++instance Arbitrary ECDSA.KeyPair where+ arbitrary = do curve <- arbitrary+ d <- getPositive <$> (arbitrary :: Gen (Positive Integer))+ let q = generateQ curve d+ return $ ECDSA.KeyPair curve q d++instance Arbitrary Curve where+ arbitrary = elements $ map getCurveByName $ enumFrom SEC_p112r1+ instance Arbitrary DH.PrivateNumber where arbitrary = fromIntegral <$> (suchThat (arbitrary :: Gen Integer) (\x -> x >= 1)) @@ -143,6 +166,9 @@ [ testProperty "RSA(PKCS15) (slow)" prop_rsa_sign_slow_valid , testProperty "RSA(PKCS15) (fast)" prop_rsa_sign_fast_valid , testProperty "DSA" prop_dsa_valid+ , testProperty "ECDSA Prime" prop_ecdsa_prime_valid+ , testProperty "ECDSA Binary" prop_ecdsa_binary_valid+ , testProperty "ECDSA Curve" prop_ecdsa_curve_valid ] asymOtherTests = testGroup "assymetric other tests"
crypto-pubkey.cabal view
@@ -1,5 +1,5 @@ Name: crypto-pubkey-Version: 0.2.2+Version: 0.2.3 Description: Public Key cryptography .@@ -29,7 +29,7 @@ , bytestring , byteable , crypto-random >= 0.0 && < 0.1- , crypto-pubkey-types >= 0.4 && < 0.5+ , crypto-pubkey-types >= 0.4.1 && < 0.5 , cryptohash >= 0.9.1 , crypto-numbers >= 0.2.2 Exposed-modules: Crypto.PubKey.RSA@@ -41,6 +41,9 @@ Crypto.PubKey.DH Crypto.PubKey.HashDescr Crypto.PubKey.MaskGenFunction+ Crypto.PubKey.ECC.Generate+ Crypto.PubKey.ECC.Prim+ Crypto.PubKey.ECC.ECDSA other-modules: Crypto.PubKey.ElGamal Crypto.PubKey.RSA.Types Crypto.PubKey.Internal@@ -55,6 +58,7 @@ , byteable , cryptohash , crypto-pubkey+ , crypto-pubkey-types , crypto-numbers , crypto-random , QuickCheck >= 2@@ -72,8 +76,10 @@ , cryptohash , crypto-random , crypto-pubkey+ , crypto-pubkey-types , criterion , mtl+ , deepseq source-repository head type: git