-- 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