morley-1.8.0: src/Tezos/Crypto/P256.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
-- | P256 cryptographic primitives.
module Tezos.Crypto.P256
( -- * Cryptographic primitive types
PublicKey (..)
, SecretKey
, Signature (..)
, detSecretKey
, detSecretKeyDo
, toPublic
-- * Raw bytes (no checksums, tags or anything)
, publicKeyToBytes
, mkPublicKey
, publicKeyLengthBytes
, signatureToBytes
, mkSignature
, signatureLengthBytes
-- * Formatting and parsing
, formatPublicKey
, mformatPublicKey
, parsePublicKey
, formatSignature
, mformatSignature
, parseSignature
, formatSecretKey
, parseSecretKey
-- * Signing
, sign
, checkSignature
) where
import Crypto.Hash (Blake2b_256(..))
import qualified Crypto.PubKey.ECC.ECDSA as ECDSA
import qualified Crypto.PubKey.ECC.Generate as ECC.Generate
import Crypto.PubKey.ECC.Types (Curve(..), CurveName(..), getCurveByName)
import Crypto.Random (MonadRandom)
import Data.ByteArray (ByteArray, ByteArrayAccess)
import Fmt (Buildable, build)
import Michelson.Text
import Tezos.Crypto.Util
curve :: Curve
curve = getCurveByName SEC_p256r1
----------------------------------------------------------------------------
-- Types, instances, conversions
----------------------------------------------------------------------------
-- | P256 public cryptographic key.
newtype PublicKey = PublicKey
{ unPublicKey :: ECDSA.PublicKey
} deriving stock (Eq, Show, Generic)
instance NFData PublicKey where
rnf (PublicKey (ECDSA.PublicKey cu q))
= rnfCurve cu `seq` rnf q
-- | P256 secret cryptographic key.
newtype SecretKey = SecretKey
{ unSecretKey :: ECDSA.KeyPair
} deriving stock (Show, Eq, Generic)
instance NFData SecretKey where
rnf (SecretKey (ECDSA.KeyPair cu pp pn)) =
rnfCurve cu `seq` rnf (pp, pn)
-- | Deterministicaly generate a secret key from seed.
detSecretKey :: ByteString -> SecretKey
detSecretKey seed = deterministic seed $ detSecretKeyDo
detSecretKeyDo :: MonadRandom m => m SecretKey
detSecretKeyDo = SecretKey <$> do
(publicKey, privateKey) <- ECC.Generate.generate curve
return $
ECDSA.KeyPair curve (ECDSA.public_q publicKey) (ECDSA.private_d privateKey)
-- | Create a public key from a secret key.
toPublic :: SecretKey -> PublicKey
toPublic =
PublicKey .
ECDSA.PublicKey curve . (\(ECDSA.KeyPair _ pp _) -> pp) . unSecretKey
-- | P256 cryptographic signature.
newtype Signature = Signature
{ unSignature :: ECDSA.Signature
} deriving stock (Show, Eq, Generic)
instance NFData Signature where
rnf (Signature (ECDSA.Signature a b)) = rnf a `seq` rnf b
----------------------------------------------------------------------------
-- Conversion to/from raw bytes (no checksums, tags or anything)
----------------------------------------------------------------------------
-- | Convert a 'PublicKey' to raw bytes.
publicKeyToBytes :: forall ba. ByteArray ba => PublicKey -> ba
publicKeyToBytes (PublicKey p) =
publicKeyToBytes_ curve p
-- | Make a 'PublicKey' from raw bytes.
mkPublicKey :: ByteArrayAccess ba => ba -> Either CryptoParseError PublicKey
mkPublicKey ba = PublicKey <$> mkPublicKey_ curve ba
publicKeyLengthBytes :: Integral n => n
publicKeyLengthBytes = publicKeyLengthBytes_ curve
-- | Convert a 'PublicKey' to raw bytes.
signatureToBytes :: ByteArray ba => Signature -> ba
signatureToBytes (Signature sig) =
signatureToBytes_ curve sig
-- | Make a 'Signature' from raw bytes.
mkSignature :: ByteArray ba => ba -> Either CryptoParseError Signature
mkSignature ba = Signature <$> mkSignature_ curve ba
signatureLengthBytes :: Integral n => n
signatureLengthBytes = signatureLengthBytes_ curve
mkSecretKey :: ByteArray ba => ba -> Either CryptoParseError SecretKey
mkSecretKey = Right . SecretKey . mkSecretKey_ curve
-- | Convert a 'PublicKey' to raw bytes.
secretKeyToBytes :: ByteArray ba => SecretKey -> ba
secretKeyToBytes (SecretKey kp) =
secretKeyToBytes_ kp
----------------------------------------------------------------------------
-- Magic bytes
----------------------------------------------------------------------------
publicKeyTag :: ByteString
publicKeyTag = "\003\178\139\127"
-- | Base16 format is @1051eebd@
secretKeyTag :: ByteString
secretKeyTag = "\016\081\238\189"
signatureTag :: ByteString
signatureTag = "\054\240\044\052"
----------------------------------------------------------------------------
-- Formatting
----------------------------------------------------------------------------
formatPublicKey :: PublicKey -> Text
formatPublicKey = formatImpl @ByteString publicKeyTag . publicKeyToBytes
mformatPublicKey :: PublicKey -> MText
mformatPublicKey = mkMTextUnsafe . formatPublicKey
instance Buildable PublicKey where
build = build . formatPublicKey
parsePublicKey :: Text -> Either CryptoParseError PublicKey
parsePublicKey = parseImpl publicKeyTag mkPublicKey
formatSignature :: Signature -> Text
formatSignature = formatImpl @ByteString signatureTag . signatureToBytes
mformatSignature :: Signature -> MText
mformatSignature = mkMTextUnsafe . formatSignature
instance Buildable Signature where
build = build . formatSignature
parseSignature :: Text -> Either CryptoParseError Signature
parseSignature = parseImpl signatureTag mkSignature
formatSecretKey :: SecretKey -> Text
formatSecretKey = formatImpl @ByteString secretKeyTag . secretKeyToBytes
instance Buildable SecretKey where
build = build . formatSecretKey
parseSecretKey :: Text -> Either CryptoParseError SecretKey
parseSecretKey = parseImpl secretKeyTag mkSecretKey
----------------------------------------------------------------------------
-- Signing
----------------------------------------------------------------------------
-- | Sign a message using the secret key.
sign :: MonadRandom m => SecretKey -> ByteString -> m Signature
sign (SecretKey keyPair) =
fmap Signature . ECDSA.sign (ECDSA.toPrivateKey keyPair) Blake2b_256
-- | Check that a sequence of bytes has been signed with a given key.
checkSignature :: PublicKey -> Signature -> ByteString -> Bool
checkSignature (PublicKey pk) (Signature sig) =
ECDSA.verify Blake2b_256 pk sig