We don't need to give the hash algorithm as a separate argument since it is already available from the digest value itself.
134 lines
4.3 KiB
Haskell
134 lines
4.3 KiB
Haskell
-- | /WARNING:/ Signature operations may leak the private key. Signature verification
|
|
-- should be safe.
|
|
{-# LANGUAGE DeriveDataTypeable #-}
|
|
module Crypto.PubKey.ECC.ECDSA
|
|
( Signature(..)
|
|
, PublicPoint
|
|
, PublicKey(..)
|
|
, PrivateNumber
|
|
, PrivateKey(..)
|
|
, KeyPair(..)
|
|
, toPublicKey
|
|
, toPrivateKey
|
|
, signWith
|
|
, signDigestWith
|
|
, sign
|
|
, signDigest
|
|
, verify
|
|
, verifyDigest
|
|
) where
|
|
|
|
import Control.Monad
|
|
import Data.Data
|
|
|
|
import Crypto.Hash
|
|
import Crypto.Internal.ByteArray (ByteArrayAccess)
|
|
import Crypto.Number.ModArithmetic (inverse)
|
|
import Crypto.Number.Generate
|
|
import Crypto.PubKey.ECC.Types
|
|
import Crypto.PubKey.ECC.Prim
|
|
import Crypto.PubKey.Internal (dsaTruncHashDigest)
|
|
import Crypto.Random.Types
|
|
|
|
-- | Represent a ECDSA signature namely R and S.
|
|
data Signature = Signature
|
|
{ sign_r :: Integer -- ^ ECDSA r
|
|
, sign_s :: Integer -- ^ ECDSA s
|
|
} deriving (Show,Read,Eq,Data)
|
|
|
|
-- | ECDSA Private Key.
|
|
data PrivateKey = PrivateKey
|
|
{ private_curve :: Curve
|
|
, private_d :: PrivateNumber
|
|
} deriving (Show,Read,Eq,Data)
|
|
|
|
-- | ECDSA Public Key.
|
|
data PublicKey = PublicKey
|
|
{ public_curve :: Curve
|
|
, public_q :: PublicPoint
|
|
} deriving (Show,Read,Eq,Data)
|
|
|
|
-- | ECDSA Key Pair.
|
|
data KeyPair = KeyPair Curve PublicPoint PrivateNumber
|
|
deriving (Show,Read,Eq,Data)
|
|
|
|
-- | Public key of a ECDSA Key pair.
|
|
toPublicKey :: KeyPair -> PublicKey
|
|
toPublicKey (KeyPair curve pub _) = PublicKey curve pub
|
|
|
|
-- | Private key of a ECDSA Key pair.
|
|
toPrivateKey :: KeyPair -> PrivateKey
|
|
toPrivateKey (KeyPair curve _ priv) = PrivateKey curve priv
|
|
|
|
-- | Sign digest using the private key and an explicit k number.
|
|
--
|
|
-- /WARNING:/ Vulnerable to timing attacks.
|
|
signDigestWith :: HashAlgorithm hash
|
|
=> 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
|
|
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 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 = signDigestWith k pk (hashWith hashAlg msg)
|
|
|
|
-- | Sign digest using the private key.
|
|
--
|
|
-- /WARNING:/ Vulnerable to timing attacks.
|
|
signDigest :: (HashAlgorithm hash, MonadRandom m)
|
|
=> PrivateKey -> Digest hash -> m Signature
|
|
signDigest pk digest = do
|
|
k <- generateBetween 1 (n - 1)
|
|
case signDigestWith k pk digest of
|
|
Nothing -> signDigest pk 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 = signDigest pk (hashWith hashAlg msg)
|
|
|
|
-- | Verify a digest using the public key.
|
|
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 digest n
|
|
u1 = z * w `mod` n
|
|
u2 = r * w `mod` n
|
|
x = pointAddTwoMuls curve u1 g u2 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
|
|
|
|
-- | Verify a bytestring using the public key.
|
|
verify :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> PublicKey -> Signature -> msg -> Bool
|
|
verify hashAlg pk sig msg = verifyDigest pk sig (hashWith hashAlg msg)
|