morley-1.15.0: src/Tezos/Crypto/Ed25519.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
-- | Ed25519 cryptographic primitives.
module Tezos.Crypto.Ed25519
( -- * Cryptographic primitive types
PublicKey (..)
, SecretKey
, Signature (..)
, detSecretKey
, toPublic
-- * Raw bytes (no checksums, tags or anything)
, publicKeyToBytes
, mkPublicKey
, publicKeyLengthBytes
, signatureToBytes
, mkSignature
, signatureLengthBytes
-- * Formatting and parsing
, formatPublicKey
, mformatPublicKey
, parsePublicKey
, formatSecretKey
, parseSecretKey
, formatSignature
, mformatSignature
, parseSignature
-- * Signing
, sign
, checkSignature
) where
import Crypto.Error (onCryptoFailure)
import qualified Crypto.PubKey.Ed25519 as Ed25519
import Data.ByteArray (ByteArray, ByteArrayAccess, convert)
import Fmt (Buildable, build)
import Michelson.Text
import Tezos.Crypto.Hash
import Tezos.Crypto.Util
----------------------------------------------------------------------------
-- Types, instances, conversions
----------------------------------------------------------------------------
-- | ED25519 public cryptographic key.
newtype PublicKey = PublicKey
{ unPublicKey :: Ed25519.PublicKey
} deriving stock (Show, Eq, Generic)
instance NFData PublicKey
instance Ord PublicKey where
compare = compare `on` (publicKeyToBytes :: PublicKey -> ByteString)
-- | ED25519 secret cryptographic key.
newtype SecretKey = SecretKey
{ unSecretKey :: Ed25519.SecretKey
} deriving stock (Show, Eq, Generic)
instance NFData SecretKey
-- | Deterministicaly generate a secret key from seed.
detSecretKey :: ByteString -> SecretKey
detSecretKey seed = SecretKey $ deterministic seed Ed25519.generateSecretKey
-- | Create a public key from a secret key.
toPublic :: SecretKey -> PublicKey
toPublic = PublicKey . Ed25519.toPublic . unSecretKey
-- | ED25519 cryptographic signature.
newtype Signature = Signature
{ unSignature :: Ed25519.Signature
} deriving stock (Show, Eq, Generic)
instance NFData Signature
----------------------------------------------------------------------------
-- Conversion to/from raw bytes (no checksums, tags or anything)
----------------------------------------------------------------------------
-- | Convert a 'PublicKey' to raw bytes.
publicKeyToBytes :: ByteArray ba => PublicKey -> ba
publicKeyToBytes = convert . unPublicKey
-- | Make a 'PublicKey' from raw bytes.
mkPublicKey :: ByteArrayAccess ba => ba -> Either CryptoParseError PublicKey
mkPublicKey =
onCryptoFailure (Left . CryptoParseCryptoError) (Right . PublicKey) .
Ed25519.publicKey
publicKeyLengthBytes :: Integral n => n
publicKeyLengthBytes = fromIntegral Ed25519.publicKeySize
-- | Convert a 'Signature' to raw bytes.
signatureToBytes :: ByteArray ba => Signature -> ba
signatureToBytes = convert . unSignature
-- | Make a 'Signature' from raw bytes.
mkSignature :: ByteArrayAccess ba => ba -> Either CryptoParseError Signature
mkSignature =
onCryptoFailure (Left . CryptoParseCryptoError) (Right . Signature) .
Ed25519.signature
signatureLengthBytes :: Integral n => n
signatureLengthBytes = fromIntegral Ed25519.signatureSize
mkSecretKey :: ByteArrayAccess ba => ba -> Either CryptoParseError SecretKey
mkSecretKey = onCryptoFailure (Left . CryptoParseCryptoError) (Right . SecretKey) .
Ed25519.secretKey
----------------------------------------------------------------------------
-- Magic bytes
----------------------------------------------------------------------------
publicKeyTag :: ByteString
publicKeyTag = "\13\15\37\217"
secretKeyTag :: ByteString
secretKeyTag = "\13\15\58\7"
signatureTag :: ByteString
signatureTag = "\9\245\205\134\18"
----------------------------------------------------------------------------
-- Formatting
----------------------------------------------------------------------------
formatPublicKey :: PublicKey -> Text
formatPublicKey = formatImpl publicKeyTag . unPublicKey
mformatPublicKey :: PublicKey -> MText
mformatPublicKey = unsafeMkMText . formatPublicKey
instance Buildable PublicKey where
build = build . formatPublicKey
parsePublicKey :: Text -> Either CryptoParseError PublicKey
parsePublicKey = parseImpl publicKeyTag mkPublicKey
formatSecretKey :: SecretKey -> Text
formatSecretKey = formatImpl secretKeyTag . unSecretKey
instance Buildable SecretKey where
build = build . formatSecretKey
parseSecretKey :: Text -> Either CryptoParseError SecretKey
parseSecretKey = parseImpl secretKeyTag mkSecretKey
formatSignature :: Signature -> Text
formatSignature = formatImpl signatureTag . unSignature
mformatSignature :: Signature -> MText
mformatSignature = unsafeMkMText . formatSignature
instance Buildable Signature where
build = build . formatSignature
parseSignature :: Text -> Either CryptoParseError Signature
parseSignature = parseImpl signatureTag mkSignature
----------------------------------------------------------------------------
-- Signing
----------------------------------------------------------------------------
-- | Sign a message using the secret key.
sign :: SecretKey -> ByteString -> Signature
sign sk =
Signature .
Ed25519.sign (unSecretKey sk) (unPublicKey (toPublic sk)) . blake2b
-- | Check that a sequence of bytes has been signed with a given key.
checkSignature :: PublicKey -> Signature -> ByteString -> Bool
checkSignature (PublicKey pk) (Signature sig) bytes =
Ed25519.verify pk (blake2b bytes) sig