From b55a93dfdc13b758cbd00770b70c4684fdd486e7 Mon Sep 17 00:00:00 2001 From: Baojun Wang Date: Mon, 4 Jun 2018 18:13:29 -0700 Subject: [PATCH 1/3] add ECDSA sign/verify digest APIs (rebased from commit 045793427e8d46594b0b2afedb314d027ec707ab) --- Crypto/PubKey/ECC/ECDSA.hs | 65 ++++++++++++++++++++++++++------------ Crypto/PubKey/Internal.hs | 9 ++++-- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/Crypto/PubKey/ECC/ECDSA.hs b/Crypto/PubKey/ECC/ECDSA.hs index 4cfc4b8..24c6e15 100644 --- a/Crypto/PubKey/ECC/ECDSA.hs +++ b/Crypto/PubKey/ECC/ECDSA.hs @@ -10,9 +10,12 @@ module Crypto.PubKey.ECC.ECDSA , KeyPair(..) , toPublicKey , toPrivateKey + , signWithDigest , signWith + , signDigest , sign , verify + , verifyDigest ) where import Control.Monad @@ -24,7 +27,7 @@ import Crypto.Number.ModArithmetic (inverse) import Crypto.Number.Generate import Crypto.PubKey.ECC.Types import Crypto.PubKey.ECC.Prim -import Crypto.PubKey.Internal (dsaTruncHash) +import Crypto.PubKey.Internal (dsaTruncHashDigest) import Crypto.Random.Types -- | Represent a ECDSA signature namely R and S. @@ -57,17 +60,17 @@ toPublicKey (KeyPair curve pub _) = PublicKey curve pub toPrivateKey :: KeyPair -> PrivateKey toPrivateKey (KeyPair curve _ priv) = PrivateKey curve priv --- | Sign message using the private key and an explicit k number. +-- | Sign digest using the private key and an explicit k number. -- -- /WARNING:/ Vulnerable to timing attacks. -signWith :: (ByteArrayAccess msg, HashAlgorithm hash) - => Integer -- ^ k random number - -> PrivateKey -- ^ private key - -> hash -- ^ hash function - -> msg -- ^ message to sign +signWithDigest :: HashAlgorithm hash + => Integer -- ^ k random number + -> PrivateKey -- ^ private key + -> hash -- ^ hash function + -> Digest hash -- ^ digest to sign -> Maybe Signature -signWith k (PrivateKey curve d) hashAlg msg = do - let z = dsaTruncHash hashAlg msg n +signWithDigest k (PrivateKey curve d) hashAlg digest = do + let z = dsaTruncHashDigest hashAlg digest n CurveCommon _ _ g n _ = common_curve curve let point = pointMul curve k g r <- case point of @@ -78,26 +81,44 @@ signWith k (PrivateKey curve d) hashAlg msg = do when (r == 0 || s == 0) Nothing return $ Signature r s +-- | Sign message using the private key and an explicit k number. +-- +-- /WARNING:/ Vulnerable to timing attacks. +signWith :: (ByteArrayAccess msg, HashAlgorithm hash) + => Integer -- ^ k random number + -> PrivateKey -- ^ private key + -> hash -- ^ hash function + -> msg -- ^ message to sign + -> Maybe Signature +signWith k pk hashAlg msg = signWithDigest k pk hashAlg (hashWith hashAlg msg) + +-- | Sign digst using the private key. +-- +-- /WARNING:/ Vulnerable to timing attacks. +signDigest :: (HashAlgorithm hash, MonadRandom m) + => PrivateKey -> hash -> Digest hash -> m Signature +signDigest pk hashAlg digest = do + k <- generateBetween 1 (n - 1) + case signWithDigest k pk hashAlg digest of + Nothing -> signDigest pk hashAlg digest + Just sig -> return sig + where n = ecc_n . common_curve $ private_curve pk + -- | Sign message using the private key. -- -- /WARNING:/ Vulnerable to timing attacks. sign :: (ByteArrayAccess msg, HashAlgorithm hash, MonadRandom m) => PrivateKey -> hash -> msg -> m Signature -sign pk hashAlg msg = do - k <- generateBetween 1 (n - 1) - case signWith k pk hashAlg msg of - Nothing -> sign pk hashAlg msg - Just sig -> return sig - where n = ecc_n . common_curve $ private_curve pk +sign pk hashAlg msg = signDigest pk hashAlg (hashWith hashAlg msg) --- | Verify a bytestring using the public key. -verify :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> PublicKey -> Signature -> msg -> Bool -verify _ (PublicKey _ PointO) _ _ = False -verify hashAlg pk@(PublicKey curve q) (Signature r s) msg +-- | Verify a digest using the public key. +verifyDigest :: HashAlgorithm hash => hash -> PublicKey -> Signature -> Digest hash -> Bool +verifyDigest _ (PublicKey _ PointO) _ _ = False +verifyDigest hashAlg pk@(PublicKey curve q) (Signature r s) digest | r < 1 || r >= n || s < 1 || s >= n = False | otherwise = maybe False (r ==) $ do w <- inverse s n - let z = dsaTruncHash hashAlg msg n + let z = dsaTruncHashDigest hashAlg digest n u1 = z * w `mod` n u2 = r * w `mod` n x = pointAddTwoMuls curve u1 g u2 q @@ -107,3 +128,7 @@ verify hashAlg pk@(PublicKey curve q) (Signature r s) msg where n = ecc_n cc g = ecc_g cc cc = common_curve $ public_curve pk + +-- | Verify a bytestring using the public key. +verify :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> PublicKey -> Signature -> msg -> Bool +verify hashAlg pk sig msg = verifyDigest hashAlg pk sig (hashWith hashAlg msg) diff --git a/Crypto/PubKey/Internal.hs b/Crypto/PubKey/Internal.hs index b1631cc..5e0629f 100644 --- a/Crypto/PubKey/Internal.hs +++ b/Crypto/PubKey/Internal.hs @@ -9,6 +9,7 @@ module Crypto.PubKey.Internal ( and' , (&&!) , dsaTruncHash + , dsaTruncHashDigest ) where import Data.Bits (shiftR) @@ -32,8 +33,12 @@ False &&! False = False -- | Truncate and hash for DSA and ECDSA. dsaTruncHash :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> msg -> Integer -> Integer -dsaTruncHash hashAlg m n +dsaTruncHash hashAlg = dsaTruncHashDigest hashAlg . hashWith hashAlg + +-- | Truncate a digest for DSA and ECDSA. +dsaTruncHashDigest :: HashAlgorithm hash => hash -> Digest hash -> Integer -> Integer +dsaTruncHashDigest hashAlg digest n | d > 0 = shiftR e d | otherwise = e - where e = os2ip $ hashWith hashAlg m + where e = os2ip digest d = hashDigestSize hashAlg * 8 - numBits n From 997cea369b7b520a91178a3fa3968267cb97bcd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Thu, 28 Feb 2019 21:14:18 +0100 Subject: [PATCH 2/3] Rename to signDigestWith --- Crypto/PubKey/ECC/ECDSA.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Crypto/PubKey/ECC/ECDSA.hs b/Crypto/PubKey/ECC/ECDSA.hs index 24c6e15..98b1ac4 100644 --- a/Crypto/PubKey/ECC/ECDSA.hs +++ b/Crypto/PubKey/ECC/ECDSA.hs @@ -10,10 +10,10 @@ module Crypto.PubKey.ECC.ECDSA , KeyPair(..) , toPublicKey , toPrivateKey - , signWithDigest , signWith - , signDigest + , signDigestWith , sign + , signDigest , verify , verifyDigest ) where @@ -63,13 +63,13 @@ toPrivateKey (KeyPair curve _ priv) = PrivateKey curve priv -- | Sign digest using the private key and an explicit k number. -- -- /WARNING:/ Vulnerable to timing attacks. -signWithDigest :: HashAlgorithm hash +signDigestWith :: HashAlgorithm hash => Integer -- ^ k random number -> PrivateKey -- ^ private key -> hash -- ^ hash function -> Digest hash -- ^ digest to sign -> Maybe Signature -signWithDigest k (PrivateKey curve d) hashAlg digest = do +signDigestWith k (PrivateKey curve d) hashAlg digest = do let z = dsaTruncHashDigest hashAlg digest n CurveCommon _ _ g n _ = common_curve curve let point = pointMul curve k g @@ -90,16 +90,16 @@ signWith :: (ByteArrayAccess msg, HashAlgorithm hash) -> hash -- ^ hash function -> msg -- ^ message to sign -> Maybe Signature -signWith k pk hashAlg msg = signWithDigest k pk hashAlg (hashWith hashAlg msg) +signWith k pk hashAlg msg = signDigestWith k pk hashAlg (hashWith hashAlg msg) --- | Sign digst using the private key. +-- | Sign digest using the private key. -- -- /WARNING:/ Vulnerable to timing attacks. signDigest :: (HashAlgorithm hash, MonadRandom m) => PrivateKey -> hash -> Digest hash -> m Signature signDigest pk hashAlg digest = do k <- generateBetween 1 (n - 1) - case signWithDigest k pk hashAlg digest of + case signDigestWith k pk hashAlg digest of Nothing -> signDigest pk hashAlg digest Just sig -> return sig where n = ecc_n . common_curve $ private_curve pk From 299140f884300dc465e75c3d4085d1bd69a0e9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Ch=C3=A9ron?= Date: Thu, 28 Feb 2019 21:26:00 +0100 Subject: [PATCH 3/3] Remove unnecessary hash arguments We don't need to give the hash algorithm as a separate argument since it is already available from the digest value itself. --- Crypto/PubKey/ECC/ECDSA.hs | 35 +++++++++++++++++------------------ Crypto/PubKey/Internal.hs | 11 +++++++---- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Crypto/PubKey/ECC/ECDSA.hs b/Crypto/PubKey/ECC/ECDSA.hs index 98b1ac4..175f75c 100644 --- a/Crypto/PubKey/ECC/ECDSA.hs +++ b/Crypto/PubKey/ECC/ECDSA.hs @@ -64,13 +64,12 @@ toPrivateKey (KeyPair curve _ priv) = PrivateKey curve priv -- -- /WARNING:/ Vulnerable to timing attacks. signDigestWith :: HashAlgorithm hash - => Integer -- ^ k random number - -> PrivateKey -- ^ private key - -> hash -- ^ hash function - -> Digest hash -- ^ digest to sign - -> Maybe Signature -signDigestWith k (PrivateKey curve d) hashAlg digest = do - let z = dsaTruncHashDigest hashAlg digest n + => Integer -- ^ k random number + -> PrivateKey -- ^ private key + -> Digest hash -- ^ digest to sign + -> Maybe Signature +signDigestWith k (PrivateKey curve d) digest = do + let z = dsaTruncHashDigest digest n CurveCommon _ _ g n _ = common_curve curve let point = pointMul curve k g r <- case point of @@ -90,17 +89,17 @@ signWith :: (ByteArrayAccess msg, HashAlgorithm hash) -> hash -- ^ hash function -> msg -- ^ message to sign -> Maybe Signature -signWith k pk hashAlg msg = signDigestWith k pk hashAlg (hashWith hashAlg msg) +signWith k pk hashAlg msg = signDigestWith k pk (hashWith hashAlg msg) -- | Sign digest using the private key. -- -- /WARNING:/ Vulnerable to timing attacks. signDigest :: (HashAlgorithm hash, MonadRandom m) - => PrivateKey -> hash -> Digest hash -> m Signature -signDigest pk hashAlg digest = do + => PrivateKey -> Digest hash -> m Signature +signDigest pk digest = do k <- generateBetween 1 (n - 1) - case signDigestWith k pk hashAlg digest of - Nothing -> signDigest pk hashAlg digest + case signDigestWith k pk digest of + Nothing -> signDigest pk digest Just sig -> return sig where n = ecc_n . common_curve $ private_curve pk @@ -109,16 +108,16 @@ signDigest pk hashAlg digest = do -- /WARNING:/ Vulnerable to timing attacks. sign :: (ByteArrayAccess msg, HashAlgorithm hash, MonadRandom m) => PrivateKey -> hash -> msg -> m Signature -sign pk hashAlg msg = signDigest pk hashAlg (hashWith hashAlg msg) +sign pk hashAlg msg = signDigest pk (hashWith hashAlg msg) -- | Verify a digest using the public key. -verifyDigest :: HashAlgorithm hash => hash -> PublicKey -> Signature -> Digest hash -> Bool -verifyDigest _ (PublicKey _ PointO) _ _ = False -verifyDigest hashAlg pk@(PublicKey curve q) (Signature r s) digest +verifyDigest :: HashAlgorithm hash => PublicKey -> Signature -> Digest hash -> Bool +verifyDigest (PublicKey _ PointO) _ _ = False +verifyDigest pk@(PublicKey curve q) (Signature r s) digest | r < 1 || r >= n || s < 1 || s >= n = False | otherwise = maybe False (r ==) $ do w <- inverse s n - let z = dsaTruncHashDigest hashAlg digest n + let z = dsaTruncHashDigest digest n u1 = z * w `mod` n u2 = r * w `mod` n x = pointAddTwoMuls curve u1 g u2 q @@ -131,4 +130,4 @@ verifyDigest hashAlg pk@(PublicKey curve q) (Signature r s) digest -- | Verify a bytestring using the public key. verify :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> PublicKey -> Signature -> msg -> Bool -verify hashAlg pk sig msg = verifyDigest hashAlg pk sig (hashWith hashAlg msg) +verify hashAlg pk sig msg = verifyDigest pk sig (hashWith hashAlg msg) diff --git a/Crypto/PubKey/Internal.hs b/Crypto/PubKey/Internal.hs index 5e0629f..3951d06 100644 --- a/Crypto/PubKey/Internal.hs +++ b/Crypto/PubKey/Internal.hs @@ -33,12 +33,15 @@ False &&! False = False -- | Truncate and hash for DSA and ECDSA. dsaTruncHash :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> msg -> Integer -> Integer -dsaTruncHash hashAlg = dsaTruncHashDigest hashAlg . hashWith hashAlg +dsaTruncHash hashAlg = dsaTruncHashDigest . hashWith hashAlg -- | Truncate a digest for DSA and ECDSA. -dsaTruncHashDigest :: HashAlgorithm hash => hash -> Digest hash -> Integer -> Integer -dsaTruncHashDigest hashAlg digest n +dsaTruncHashDigest :: HashAlgorithm hash => Digest hash -> Integer -> Integer +dsaTruncHashDigest digest n | d > 0 = shiftR e d | otherwise = e where e = os2ip digest - d = hashDigestSize hashAlg * 8 - numBits n + d = hashDigestSize (getHashAlg digest) * 8 - numBits n + +getHashAlg :: Digest hash -> hash +getHashAlg _ = undefined