morley-1.4.0: src/Tezos/Crypto/P256.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
-- | P256 cryptographic primitives.
--
-- This module is mostly a stub, it doesn't implement actual crypto.
-- TODO (#18) implement crypto properly.
module Tezos.Crypto.P256
( -- * Cryptographic primitive types
PublicKey (..)
, SecretKey
, Signature (..)
, detSecretKey
, toPublic
-- * Generators
, genPublicKey
, genSecretKey
, genSignature
-- * Raw bytes (no checksums, tags or anything)
, publicKeyToBytes
, mkPublicKey
, publicKeyLengthBytes
, signatureToBytes
, mkSignature
, signatureLengthBytes
-- * Formatting and parsing
, formatPublicKey
, mformatPublicKey
, parsePublicKey
, formatSignature
, mformatSignature
, parseSignature
-- * Signing
, checkSignature
) where
import Crypto.Random (getRandomBytes)
import Data.ByteArray (ByteArray, ByteArrayAccess)
import qualified Data.ByteArray as BA
import qualified Data.ByteString as BS
import Fmt (Buildable, build)
import Hedgehog (MonadGen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.QuickCheck (Arbitrary(..), vector)
import Michelson.Text
import Tezos.Crypto.Util
----------------------------------------------------------------------------
-- Types, instances, conversions
----------------------------------------------------------------------------
-- | P256 public cryptographic key.
newtype PublicKey = PublicKey
{ unPublicKey :: ByteString
} deriving stock (Show, Eq, Generic)
instance Arbitrary PublicKey where
arbitrary = toPublic <$> arbitrary
instance NFData PublicKey
genPublicKey :: MonadGen m => m PublicKey
genPublicKey = toPublic <$> genSecretKey
-- | P256 secret cryptographic key.
newtype SecretKey = SecretKey
{ unSecretKey :: ByteString
} deriving stock (Show, Eq, Generic)
instance NFData SecretKey
-- | Deterministicaly generate a secret key from seed.
detSecretKey :: ByteString -> SecretKey
detSecretKey seed =
SecretKey $ deterministic seed $ getRandomBytes publicKeyLengthBytes
instance Arbitrary SecretKey where
arbitrary = detSecretKey . BS.pack <$> vector 32
genSecretKey :: MonadGen m => m SecretKey
genSecretKey = detSecretKey <$> Gen.bytes (Range.singleton 32)
-- | Create a public key from a secret key.
toPublic :: SecretKey -> PublicKey
toPublic = PublicKey . unSecretKey
-- | P256 cryptographic signature.
newtype Signature = Signature
{ unSignature :: ByteString
} deriving stock (Show, Eq, Generic)
instance Arbitrary Signature where
arbitrary = Signature . BS.pack <$> replicateM signatureLengthBytes arbitrary
instance NFData Signature
genSignature :: MonadGen m => m Signature
genSignature = Signature <$> Gen.bytes (Range.singleton signatureLengthBytes)
----------------------------------------------------------------------------
-- Conversion to/from raw bytes (no checksums, tags or anything)
----------------------------------------------------------------------------
-- | Convert a 'PublicKey' to raw bytes.
--
-- TODO (#18): implement properly.
publicKeyToBytes :: forall ba. ByteArray ba => PublicKey -> ba
publicKeyToBytes = BA.convert . unPublicKey
-- | Make a 'PublicKey' from raw bytes.
--
-- TODO (#18): implement properly.
mkPublicKey :: ByteArrayAccess ba => ba -> Either CryptoParseError PublicKey
mkPublicKey ba
| l == publicKeyLengthBytes =
Right $ PublicKey (BA.convert ba)
| otherwise =
Left $ CryptoParseUnexpectedLength "public key" l
where
l = BA.length ba
publicKeyLengthBytes :: Integral n => n
publicKeyLengthBytes = 33
-- | Convert a 'PublicKey' to raw bytes.
--
-- TODO (#18): implement properly.
signatureToBytes :: ByteArray ba => Signature -> ba
signatureToBytes = BA.convert . unSignature
-- | Make a 'Signature' from raw bytes.
--
-- TODO (#18): implement properly.
mkSignature :: ByteArray ba => ba -> Either CryptoParseError Signature
mkSignature ba
| l == signatureLengthBytes =
Right $ Signature (BA.convert ba)
| otherwise =
Left $ CryptoParseUnexpectedLength "signature" l
where
l = BA.length ba
signatureLengthBytes :: Integral n => n
signatureLengthBytes = 64
----------------------------------------------------------------------------
-- Magic bytes
----------------------------------------------------------------------------
publicKeyTag :: ByteString
publicKeyTag = "\003\178\139\127"
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
----------------------------------------------------------------------------
-- Signing
----------------------------------------------------------------------------
-- | Check that a sequence of bytes has been signed with a given key.
checkSignature :: PublicKey -> Signature -> ByteString -> Bool
checkSignature _ _ _ = False