packages feed

paseto-0.1.0.0: src/Crypto/Paseto/Keys.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

-- | PASETO cryptographic keys.
module Crypto.Paseto.Keys
  ( -- * Symmetric keys
    SymmetricKey (..)
  , symmetricKeyToBytes
  , bytesToSymmetricKeyV3
  , bytesToSymmetricKeyV4
  , generateSymmetricKeyV3
  , generateSymmetricKeyV4

    -- * Asymmetric keys
    -- ** Signing keys
  , SigningKey (..)
  , signingKeyToBytes
  , bytesToSigningKeyV3
  , bytesToSigningKeyV4
  , generateSigningKeyV3
  , generateSigningKeyV4
    -- ** Verification keys
  , VerificationKey (..)
  , verificationKeyToBytes
  , bytesToVerificationKeyV3
  , bytesToVerificationKeyV4
  , fromSigningKey
  ) where

import qualified Crypto.Error as Crypto
import qualified Crypto.Paseto.Keys.V3 as V3
import Crypto.Paseto.Mode ( Version (..) )
import Crypto.Paseto.ScrubbedBytes
  ( ScrubbedBytes32 (..), generateScrubbedBytes32, mkScrubbedBytes32 )
import qualified Crypto.PubKey.Ed25519 as Crypto.Ed25519
import Data.ByteArray ( ScrubbedBytes, constEq )
import qualified Data.ByteArray as BA
import Data.ByteString ( ByteString )
import Prelude

------------------------------------------------------------------------------
-- Symmetric keys
------------------------------------------------------------------------------

-- | Symmetric key.
--
-- Note that this type's 'Eq' instance performs a constant-time equality
-- check.
data SymmetricKey v where
  -- | Version 3 symmetric key.
  SymmetricKeyV3 :: !ScrubbedBytes32 -> SymmetricKey V3

  -- | Version 4 symmetric key.
  SymmetricKeyV4 :: !ScrubbedBytes32 -> SymmetricKey V4

instance Eq (SymmetricKey v) where
  SymmetricKeyV3 x == SymmetricKeyV3 y = x `constEq` y
  SymmetricKeyV4 x == SymmetricKeyV4 y = x `constEq` y

-- | Get the raw bytes associated with a symmetric key.
symmetricKeyToBytes :: SymmetricKey v -> ScrubbedBytes
symmetricKeyToBytes k =
  case k of
    SymmetricKeyV3 (ScrubbedBytes32 bs) -> bs
    SymmetricKeyV4 (ScrubbedBytes32 bs) -> bs

-- | Construct a version 3 symmetric key from bytes.
--
-- If the provided byte string does not have a length of @32@ (@256@ bits),
-- 'Nothing' is returned.
bytesToSymmetricKeyV3 :: ScrubbedBytes -> Maybe (SymmetricKey V3)
bytesToSymmetricKeyV3 = (SymmetricKeyV3 <$>) . mkScrubbedBytes32

-- | Construct a version 4 symmetric key from bytes.
--
-- If the provided byte string does not have a length of @32@ (@256@ bits),
-- 'Nothing' is returned.
bytesToSymmetricKeyV4 :: ScrubbedBytes -> Maybe (SymmetricKey V4)
bytesToSymmetricKeyV4 = (SymmetricKeyV4 <$>) . mkScrubbedBytes32

-- | Randomly generate a version 3 symmetric key.
generateSymmetricKeyV3 :: IO (SymmetricKey V3)
generateSymmetricKeyV3 = SymmetricKeyV3 <$> generateScrubbedBytes32

-- | Randomly generate a version 4 symmetric key.
generateSymmetricKeyV4 :: IO (SymmetricKey V4)
generateSymmetricKeyV4 = SymmetricKeyV4 <$> generateScrubbedBytes32

------------------------------------------------------------------------------
-- Asymmetric keys
------------------------------------------------------------------------------

-- | Signing key (also known as a private\/secret key).
--
-- Note that this type's 'Eq' instance performs a constant-time equality
-- check.
data SigningKey v where
  -- | Version 3 signing key.
  SigningKeyV3 :: !V3.PrivateKeyP384 -> SigningKey V3

  -- | Version 3 signing key.
  SigningKeyV4 :: !Crypto.Ed25519.SecretKey -> SigningKey V4

instance Eq (SigningKey v) where
  x == y = signingKeyToBytes x `constEq` signingKeyToBytes y

-- | Get the raw bytes associated with a signing key.
signingKeyToBytes :: SigningKey v -> ScrubbedBytes
signingKeyToBytes sk =
  case sk of
    SigningKeyV3 k -> V3.encodePrivateKeyP384 k
    SigningKeyV4 k -> BA.convert k

-- | Construct a version 3 signing key from bytes.
bytesToSigningKeyV3 :: ScrubbedBytes -> Either V3.ScalarDecodingError (SigningKey V3)
bytesToSigningKeyV3 bs = SigningKeyV3 <$> V3.decodePrivateKeyP384 bs

-- | Construct a version 4 signing key from bytes.
bytesToSigningKeyV4 :: ScrubbedBytes -> Maybe (SigningKey V4)
bytesToSigningKeyV4 bs =
  SigningKeyV4
    <$> Crypto.maybeCryptoError (Crypto.Ed25519.secretKey bs)

-- | Randomly generate a version 3 signing key.
generateSigningKeyV3 :: IO (SigningKey V3)
generateSigningKeyV3 = SigningKeyV3 <$> V3.generatePrivateKeyP384

-- | Randomly generate a version 4 signing key.
generateSigningKeyV4 :: IO (SigningKey V4)
generateSigningKeyV4 = SigningKeyV4 <$> Crypto.Ed25519.generateSecretKey

-- | Verification key (also known as a public key).
data VerificationKey v where
  -- | Version 3 verification key.
  VerificationKeyV3 :: !V3.PublicKeyP384 -> VerificationKey V3

  -- | Version 4 verification key.
  VerificationKeyV4 :: !Crypto.Ed25519.PublicKey -> VerificationKey V4

deriving instance Eq (VerificationKey v)

-- | Get the raw bytes associated with a verification key.
verificationKeyToBytes :: VerificationKey v -> ByteString
verificationKeyToBytes vk =
  case vk of
    VerificationKeyV3 k -> V3.encodePublicKeyP384 k
    VerificationKeyV4 k -> BA.convert k

-- | Construct a version 3 verification key from bytes.
--
-- The input 'ByteString' is expected to be formatted as either a compressed
-- or uncompressed elliptic curve public key 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).
bytesToVerificationKeyV3 :: ByteString -> Either V3.PublicKeyP384DecodingError (VerificationKey V3)
bytesToVerificationKeyV3 bs = VerificationKeyV3 <$> V3.decodePublicKeyP384 bs

-- | Construct a version 4 verification key from bytes.
bytesToVerificationKeyV4 :: ByteString -> Maybe (VerificationKey V4)
bytesToVerificationKeyV4 bs =
  VerificationKeyV4
    <$> Crypto.maybeCryptoError (Crypto.Ed25519.publicKey bs)

-- | Get the 'VerificationKey' which corresponds to a given 'SigningKey'.
fromSigningKey :: SigningKey v -> VerificationKey v
fromSigningKey sk =
  case sk of
    SigningKeyV3 k -> VerificationKeyV3 (V3.fromPrivateKeyP384 k)
    SigningKeyV4 k -> VerificationKeyV4 (Crypto.Ed25519.toPublic k)