-- |
-- Module      :  Crypto.Ecdsa.Utils
-- Copyright   :  Aleksandr Krupenkin 2016-2021
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  portable
--
-- ECDSA module helper functions.
--

module Crypto.Ecdsa.Utils where

import           Crypto.Number.Serialize    (i2osp, os2ip)
import           Crypto.PubKey.ECC.ECDSA    (PrivateKey (..), PublicKey (..))
import           Crypto.PubKey.ECC.Generate (generateQ)
import           Crypto.PubKey.ECC.Types    (CurveName (SEC_p256k1), Point (..),
                                             getCurveByName)
import           Data.ByteArray             (ByteArray, ByteArrayAccess,
                                             singleton)

-- | Import ECDSA private key from byte array.
--
-- Input array should have 32 byte length.
importKey :: ByteArrayAccess privateKey => privateKey -> PrivateKey
{-# INLINE importKey #-}
importKey :: privateKey -> PrivateKey
importKey = Curve -> PrivateNumber -> PrivateKey
PrivateKey (CurveName -> Curve
getCurveByName CurveName
SEC_p256k1) (PrivateNumber -> PrivateKey)
-> (privateKey -> PrivateNumber) -> privateKey -> PrivateKey
forall b c a. (b -> c) -> (a -> b) -> a -> c
. privateKey -> PrivateNumber
forall ba. ByteArrayAccess ba => ba -> PrivateNumber
os2ip

-- | Export private key to byte array (32 byte length).
exportKey :: ByteArray privateKey => PrivateKey -> privateKey
{-# INLINE exportKey #-}
exportKey :: PrivateKey -> privateKey
exportKey (PrivateKey Curve
_ PrivateNumber
key) = PrivateNumber -> privateKey
forall ba. ByteArray ba => PrivateNumber -> ba
i2osp PrivateNumber
key

-- | Get public key appropriate to private key.
--
-- /WARNING:/ Vulnerable to timing attacks.
derivePubKey :: PrivateKey -> PublicKey
{-# INLINE derivePubKey #-}
derivePubKey :: PrivateKey -> PublicKey
derivePubKey (PrivateKey Curve
curve PrivateNumber
p) = Curve -> PublicPoint -> PublicKey
PublicKey Curve
curve (Curve -> PrivateNumber -> PublicPoint
generateQ Curve
curve PrivateNumber
p)

-- | Export public key to byte array (64 byte length).
exportPubKey :: ByteArray publicKey => PublicKey -> publicKey
exportPubKey :: PublicKey -> publicKey
exportPubKey (PublicKey Curve
_ PublicPoint
PointO)      = publicKey
forall a. Monoid a => a
mempty
exportPubKey (PublicKey Curve
_ (Point PrivateNumber
x PrivateNumber
y)) = PrivateNumber -> publicKey
forall ba. ByteArray ba => PrivateNumber -> ba
i2osp PrivateNumber
x publicKey -> publicKey -> publicKey
forall a. Semigroup a => a -> a -> a
<> PrivateNumber -> publicKey
forall ba. ByteArray ba => PrivateNumber -> ba
i2osp PrivateNumber
y

-- | Export compressed public key to byte array (33 byte length).
exportPubKeyCompress :: ByteArray publicKey => PublicKey -> publicKey
exportPubKeyCompress :: PublicKey -> publicKey
exportPubKeyCompress (PublicKey Curve
_ PublicPoint
PointO) = publicKey
forall a. Monoid a => a
mempty
exportPubKeyCompress (PublicKey Curve
_ (Point PrivateNumber
x PrivateNumber
y)) = publicKey
prefix publicKey -> publicKey -> publicKey
forall a. Semigroup a => a -> a -> a
<> PrivateNumber -> publicKey
forall ba. ByteArray ba => PrivateNumber -> ba
i2osp PrivateNumber
x
  where
    prefix :: publicKey
prefix | PrivateNumber -> Bool
forall a. Integral a => a -> Bool
even PrivateNumber
y = Word8 -> publicKey
forall a. ByteArray a => Word8 -> a
singleton Word8
0x02
           | Bool
otherwise = Word8 -> publicKey
forall a. ByteArray a => Word8 -> a
singleton Word8
0x03