    -- | /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.ModArithmetic (inverse)
    import Crypto.Number.Serialize
    import Crypto.Number.Generate
    import Crypto.PubKey.ECC.Types
    import Crypto.PubKey.ECC.Prim
    import Crypto.Hash

    -- | 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
                -- 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 :: (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 = log2 e - log2 n
            log2 = ceiling . logBase (2 :: Double) . fromIntegral