    -- | /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
        , sign
        , verify
        ) where

    import Control.Monad
    import Crypto.Random.Types
    import Data.Bits (shiftR)
    import Crypto.Internal.ByteArray (ByteArrayAccess)
    import Data.Data
    import Crypto.Number.Basic (numBits)
    import Crypto.Number.ModArithmetic (inverse)
    import Crypto.Number.Serialize
    import Crypto.Number.Generate
    import Crypto.PubKey.ECC.Types
    import Crypto.PubKey.ECC.Prim
    import Crypto.Hash
    import Crypto.Hash.Types (hashDigestSize)

    -- | 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,Typeable)

    -- | ECDSA Private Key.
    data PrivateKey = PrivateKey
        { private_curve :: Curve
        , private_d     :: PrivateNumber
        } deriving (Show,Read,Eq,Data,Typeable)

    -- | ECDSA Public Key.
    data PublicKey = PublicKey
        { public_curve :: Curve
        , public_q     :: PublicPoint
        } deriving (Show,Read,Eq,Data,Typeable)

    -- | ECDSA Key Pair.
    data KeyPair = KeyPair Curve PublicPoint PrivateNumber
        deriving (Show,Read,Eq,Data,Typeable)

    -- | 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 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 (PrivateKey curve d) hashAlg msg = do
        let z = tHash hashAlg 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 :: (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

    -- | 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
        | r < 1 || r >= n || s < 1 || s >= n = False
        | otherwise = maybe False (r ==) $ do
            w <- inverse s n
            let z  = tHash hashAlg msg 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

    -- | Truncate and hash.
    tHash :: (ByteArrayAccess msg, HashAlgorithm hash) => hash -> msg -> Integer -> Integer
    tHash hashAlg m n
        | d > 0 = shiftR e d
        | otherwise = e
      where e = os2ip $ hashWith hashAlg m
            d = hashDigestSize hashAlg * 8 - numBits n