paseto-0.1.0.0: src/Crypto/Paseto/Keys/V3.hs
{-# LANGUAGE PatternSynonyms #-}
-- | P384 ECDSA cryptographic keys.
module Crypto.Paseto.Keys.V3
( -- * Curve
curveP384
-- * Private key
, PrivateKeyP384 (PrivateKeyP384)
, unPrivateKeyP384
, mkPrivateKeyP384
, generatePrivateKeyP384
, encodePrivateKeyP384
, Internal.ScalarDecodingError (..)
, Internal.renderScalarDecodingError
, decodePrivateKeyP384
-- ** Helpers
, generateScalarP384
, isScalarValidP384
-- * Public key
, PublicKeyP384 (PublicKeyP384)
, unPublicKeyP384
, mkPublicKeyP384
, fromPrivateKeyP384
, encodePublicKeyP384
, Internal.CompressedPointDecodingError (..)
, Internal.UncompressedPointDecodingError (..)
, PublicKeyP384DecodingError (..)
, renderPublicKeyP384DecodingError
, decodePublicKeyP384
) where
import qualified Crypto.Paseto.Keys.V3.Internal as Internal
import qualified Crypto.PubKey.ECC.ECDSA as ECC.ECDSA
import qualified Crypto.PubKey.ECC.Prim as ECC
import qualified Crypto.PubKey.ECC.Types as ECC
import Data.Bifunctor ( bimap )
import Data.ByteArray ( ScrubbedBytes, constEq )
import Data.ByteString ( ByteString )
import qualified Data.ByteString as BS
import Data.Text ( Text )
import Prelude
-- | Elliptic curve 'ECC.SEC_p384r1'.
curveP384 :: ECC.Curve
curveP384 = ECC.getCurveByName ECC.SEC_p384r1
------------------------------------------------------------------------------
-- P384 private key
------------------------------------------------------------------------------
-- | Generate a random scalar on the curve 'ECC.SEC_p384r1'.
generateScalarP384 :: IO Integer
generateScalarP384 = ECC.scalarGenerate curveP384
-- | Whether a scalar is valid on the curve 'ECC.SEC_p384r1'.
isScalarValidP384 :: Integer -> Bool
isScalarValidP384 = Internal.isScalarValid curveP384
-- | ECDSA private key for curve 'ECC.SEC_p384r1'.
--
-- Note that this type's 'Eq' instance performs a constant-time equality
-- check.
newtype PrivateKeyP384 = MkPrivateKeyP384
{ unPrivateKeyP384 :: ECC.ECDSA.PrivateKey }
deriving newtype Show
instance Eq PrivateKeyP384 where
PrivateKeyP384 (ECC.ECDSA.PrivateKey cx dx) == PrivateKeyP384 (ECC.ECDSA.PrivateKey cy dy) =
Internal.encodeScalar cx dx `constEq` Internal.encodeScalar cy dy
pattern PrivateKeyP384 :: ECC.ECDSA.PrivateKey -> PrivateKeyP384
pattern PrivateKeyP384 pk <- MkPrivateKeyP384 pk
{-# COMPLETE PrivateKeyP384 #-}
-- | Construct a private key for curve 'ECC.SEC_p384r1'.
mkPrivateKeyP384 :: ECC.ECDSA.PrivateKey -> Maybe PrivateKeyP384
mkPrivateKeyP384 privKey@(ECC.ECDSA.PrivateKey curve d)
| curveP384 == curve && isScalarValidP384 d = Just (MkPrivateKeyP384 privKey)
| otherwise = Nothing
-- | Generate a private key for curve 'ECC.SEC_p384r1'.
generatePrivateKeyP384 :: IO PrivateKeyP384
generatePrivateKeyP384 =
MkPrivateKeyP384 . (ECC.ECDSA.PrivateKey curveP384)
<$> generateScalarP384
-- | Encode a private key into its binary format as defined in
-- [RFC 5915](https://tools.ietf.org/html/rfc5915), i.e. the @privateKey@
-- field described in
-- [section 3](https://datatracker.ietf.org/doc/html/rfc5915#section-3).
encodePrivateKeyP384 :: PrivateKeyP384 -> ScrubbedBytes
encodePrivateKeyP384 (PrivateKeyP384 (ECC.ECDSA.PrivateKey curve d)) =
Internal.encodeScalar curve d
-- | Decode a private key from its binary format as defined in
-- [RFC 5915](https://tools.ietf.org/html/rfc5915), i.e. the @privateKey@
-- field described in
-- [section 3](https://datatracker.ietf.org/doc/html/rfc5915#section-3).
decodePrivateKeyP384 :: ScrubbedBytes -> Either Internal.ScalarDecodingError PrivateKeyP384
decodePrivateKeyP384 bs =
MkPrivateKeyP384 . ECC.ECDSA.PrivateKey curve
<$> Internal.decodeScalar curve bs
where
curve :: ECC.Curve
curve = curveP384
------------------------------------------------------------------------------
-- P384 public key
------------------------------------------------------------------------------
-- | ECDSA public key for curve 'ECC.SEC_p384r1'.
newtype PublicKeyP384 = MkPublicKeyP384
{ unPublicKeyP384 :: ECC.ECDSA.PublicKey }
deriving newtype (Show, Eq)
pattern PublicKeyP384 :: ECC.ECDSA.PublicKey -> PublicKeyP384
pattern PublicKeyP384 pk <- MkPublicKeyP384 pk
{-# COMPLETE PublicKeyP384 #-}
-- | Construct a public key for curve 'ECC.SEC_p384r1'.
mkPublicKeyP384 :: ECC.ECDSA.PublicKey -> Maybe PublicKeyP384
mkPublicKeyP384 pubKey@(ECC.ECDSA.PublicKey curve point)
| curveP384 == curve && ECC.isPointValid curve point = Just (MkPublicKeyP384 pubKey)
| otherwise = Nothing
-- | Construct the 'PublicKeyP384' which corresponds to a given
-- 'PrivateKeyP384'.
fromPrivateKeyP384 :: PrivateKeyP384 -> PublicKeyP384
fromPrivateKeyP384 (PrivateKeyP384 privateKey) =
MkPublicKeyP384 (Internal.fromPrivateKey privateKey)
-- | Encode an elliptic curve point into its compressed binary format as
-- defined by [SEC 1](https://www.secg.org/sec1-v2.pdf) and
-- [RFC 5480 section 2.2](https://datatracker.ietf.org/doc/html/rfc5480#section-2.2).
encodePublicKeyP384 :: PublicKeyP384 -> ByteString
encodePublicKeyP384 (PublicKeyP384 (ECC.ECDSA.PublicKey c p)) =
case c of
ECC.CurveFP curvePrime -> Internal.encodePointCompressed curvePrime p
_ -> error "encodePublicKeyP384: impossible: secp384r1 curve is not a prime curve"
-- | Error decoding a public key for curve 'ECC.SEC_p384r1'.
data PublicKeyP384DecodingError
= -- | Error decoding a compressed public key.
PublicKeyP384DecodingCompressedError !Internal.CompressedPointDecodingError
| -- | Error decoding an uncompressed public key.
PublicKeyP384DecodingUncompressedError !Internal.UncompressedPointDecodingError
deriving stock (Show, Eq)
-- | Render a 'PublicKeyP384DecodingError' as 'Text'.
renderPublicKeyP384DecodingError :: PublicKeyP384DecodingError -> Text
renderPublicKeyP384DecodingError err =
case err of
PublicKeyP384DecodingCompressedError e ->
"Failed to decode compressed public key: "
<> Internal.renderCompressedPointDecodingError e
PublicKeyP384DecodingUncompressedError e ->
"Failed to decode uncompressed public key: "
<> Internal.renderUncompressedPointDecodingError e
-- | Decode a public key from either its compressed or uncompressed binary
-- format as defined by [SEC 1](https://www.secg.org/sec1-v2.pdf) and
-- [RFC 5480 section 2.2](https://datatracker.ietf.org/doc/html/rfc5480#section-2.2).
decodePublicKeyP384 :: ByteString -> Either PublicKeyP384DecodingError PublicKeyP384
decodePublicKeyP384 bs
| len == 49 = bimap PublicKeyP384DecodingCompressedError mkPk (Internal.decodePointCompressed curvePrime bs)
| otherwise = bimap PublicKeyP384DecodingUncompressedError mkPk (Internal.decodePointUncompressed curvePrime bs)
where
len :: Int
len = BS.length bs
curve :: ECC.Curve
curve = curveP384
curvePrime :: ECC.CurvePrime
curvePrime =
case curve of
ECC.CurveFP c -> c
_ -> error "decodePublicKeyP384: impossible: secp384r1 curve is not a prime curve"
mkPk :: ECC.Point -> PublicKeyP384
mkPk = MkPublicKeyP384 . (ECC.ECDSA.PublicKey curve)