-- |
-- Module      : Crypto.PubKey.DSA
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good
--
-- An implementation of the Digital Signature Algorithm (DSA)

module Crypto.PubKey.DSA
    ( Params(..)
    , Signature(..)
    , PublicKey(..)
    , PrivateKey(..)
    -- * generation
    , generatePrivate
    , calculatePublic
    -- * signature primitive
    , sign
    , signWith
    -- * verification primitive
    , verify
    ) where

import Crypto.Random
import Data.Maybe
import Data.ByteString (ByteString)
import Crypto.Number.ModArithmetic (expFast, expSafe, inverse)
import Crypto.Number.Serialize
import Crypto.Number.Generate
import Crypto.Types.PubKey.DSA
import Crypto.PubKey.HashDescr

-- | generate a private number with no specific property
-- this number is usually called X in DSA text.
generatePrivate :: CPRG g => g -> Params -> (PrivateNumber, g)
generatePrivate :: g -> Params -> (PrivateNumber, g)
generatePrivate rng :: g
rng (Params _ _ q :: PrivateNumber
q) = g -> PrivateNumber -> (PrivateNumber, g)
forall g. CPRG g => g -> PrivateNumber -> (PrivateNumber, g)
generateMax g
rng PrivateNumber
q

-- | Calculate the public number from the parameters and the private key
calculatePublic :: Params -> PrivateNumber -> PublicNumber
calculatePublic :: Params -> PrivateNumber -> PrivateNumber
calculatePublic (Params p :: PrivateNumber
p g :: PrivateNumber
g _) x :: PrivateNumber
x = PrivateNumber -> PrivateNumber -> PrivateNumber -> PrivateNumber
expSafe PrivateNumber
g PrivateNumber
x PrivateNumber
p

-- | sign message using the private key and an explicit k number.
signWith :: Integer         -- ^ k random number
         -> PrivateKey      -- ^ private key
         -> HashFunction    -- ^ hash function
         -> ByteString      -- ^ message to sign
         -> Maybe Signature
signWith :: PrivateNumber
-> PrivateKey -> HashFunction -> ByteString -> Maybe Signature
signWith k :: PrivateNumber
k pk :: PrivateKey
pk hash :: HashFunction
hash msg :: ByteString
msg
    | PrivateNumber
r PrivateNumber -> PrivateNumber -> Bool
forall a. Eq a => a -> a -> Bool
== 0 Bool -> Bool -> Bool
|| PrivateNumber
s PrivateNumber -> PrivateNumber -> Bool
forall a. Eq a => a -> a -> Bool
== 0  = Maybe Signature
forall a. Maybe a
Nothing
    | Bool
otherwise         = Signature -> Maybe Signature
forall a. a -> Maybe a
Just (Signature -> Maybe Signature) -> Signature -> Maybe Signature
forall a b. (a -> b) -> a -> b
$ PrivateNumber -> PrivateNumber -> Signature
Signature PrivateNumber
r PrivateNumber
s
    where -- parameters
          (Params p :: PrivateNumber
p g :: PrivateNumber
g q :: PrivateNumber
q) = PrivateKey -> Params
private_params PrivateKey
pk
          x :: PrivateNumber
x         = PrivateKey -> PrivateNumber
private_x PrivateKey
pk
          -- compute r,s
          kInv :: PrivateNumber
kInv      = Maybe PrivateNumber -> PrivateNumber
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe PrivateNumber -> PrivateNumber)
-> Maybe PrivateNumber -> PrivateNumber
forall a b. (a -> b) -> a -> b
$ PrivateNumber -> PrivateNumber -> Maybe PrivateNumber
inverse PrivateNumber
k PrivateNumber
q
          hm :: PrivateNumber
hm        = ByteString -> PrivateNumber
os2ip (ByteString -> PrivateNumber) -> ByteString -> PrivateNumber
forall a b. (a -> b) -> a -> b
$ HashFunction
hash ByteString
msg
          r :: PrivateNumber
r         = PrivateNumber -> PrivateNumber -> PrivateNumber -> PrivateNumber
expSafe PrivateNumber
g PrivateNumber
k PrivateNumber
p PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
q
          s :: PrivateNumber
s         = (PrivateNumber
kInv PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
* (PrivateNumber
hm PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
+ PrivateNumber
x PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
* PrivateNumber
r)) PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
q

-- | sign message using the private key.
sign :: CPRG g => g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)
sign :: g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)
sign rng :: g
rng pk :: PrivateKey
pk hash :: HashFunction
hash msg :: ByteString
msg =
    case PrivateNumber
-> PrivateKey -> HashFunction -> ByteString -> Maybe Signature
signWith PrivateNumber
k PrivateKey
pk HashFunction
hash ByteString
msg of
        Nothing  -> g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)
forall g.
CPRG g =>
g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)
sign g
rng' PrivateKey
pk HashFunction
hash ByteString
msg
        Just sig :: Signature
sig -> (Signature
sig, g
rng')
    where (Params _ _ q :: PrivateNumber
q) = PrivateKey -> Params
private_params PrivateKey
pk
          (k :: PrivateNumber
k, rng' :: g
rng') = g -> PrivateNumber -> (PrivateNumber, g)
forall g. CPRG g => g -> PrivateNumber -> (PrivateNumber, g)
generateMax g
rng PrivateNumber
q

-- | verify a bytestring using the public key.
verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool
verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool
verify hash :: HashFunction
hash pk :: PublicKey
pk (Signature r :: PrivateNumber
r s :: PrivateNumber
s) m :: ByteString
m
    -- Reject the signature if either 0 < r < q or 0 < s < q is not satisfied.
    | PrivateNumber
r PrivateNumber -> PrivateNumber -> Bool
forall a. Ord a => a -> a -> Bool
<= 0 Bool -> Bool -> Bool
|| PrivateNumber
r PrivateNumber -> PrivateNumber -> Bool
forall a. Ord a => a -> a -> Bool
>= PrivateNumber
q Bool -> Bool -> Bool
|| PrivateNumber
s PrivateNumber -> PrivateNumber -> Bool
forall a. Ord a => a -> a -> Bool
<= 0 Bool -> Bool -> Bool
|| PrivateNumber
s PrivateNumber -> PrivateNumber -> Bool
forall a. Ord a => a -> a -> Bool
>= PrivateNumber
q = Bool
False
    | Bool
otherwise                            = PrivateNumber
v PrivateNumber -> PrivateNumber -> Bool
forall a. Eq a => a -> a -> Bool
== PrivateNumber
r
    where (Params p :: PrivateNumber
p g :: PrivateNumber
g q :: PrivateNumber
q) = PublicKey -> Params
public_params PublicKey
pk
          y :: PrivateNumber
y       = PublicKey -> PrivateNumber
public_y PublicKey
pk
          hm :: PrivateNumber
hm      = ByteString -> PrivateNumber
os2ip (ByteString -> PrivateNumber) -> ByteString -> PrivateNumber
forall a b. (a -> b) -> a -> b
$ HashFunction
hash ByteString
m

          w :: PrivateNumber
w       = Maybe PrivateNumber -> PrivateNumber
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe PrivateNumber -> PrivateNumber)
-> Maybe PrivateNumber -> PrivateNumber
forall a b. (a -> b) -> a -> b
$ PrivateNumber -> PrivateNumber -> Maybe PrivateNumber
inverse PrivateNumber
s PrivateNumber
q
          u1 :: PrivateNumber
u1      = (PrivateNumber
hmPrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
*PrivateNumber
w) PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
q
          u2 :: PrivateNumber
u2      = (PrivateNumber
rPrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
*PrivateNumber
w) PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
q
          v :: PrivateNumber
v       = ((PrivateNumber -> PrivateNumber -> PrivateNumber -> PrivateNumber
expFast PrivateNumber
g PrivateNumber
u1 PrivateNumber
p) PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Num a => a -> a -> a
* (PrivateNumber -> PrivateNumber -> PrivateNumber -> PrivateNumber
expFast PrivateNumber
y PrivateNumber
u2 PrivateNumber
p)) PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
p PrivateNumber -> PrivateNumber -> PrivateNumber
forall a. Integral a => a -> a -> a
`mod` PrivateNumber
q