hOpenPGP-3.1: Codec/Encryption/OpenPGP/Types/Internal/PKITypes.hs
-- PKITypes.hs: OpenPGP (RFC9580) data types for public/secret keys
-- Copyright © 2012-2026 Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
module Codec.Encryption.OpenPGP.Types.Internal.PKITypes where
import qualified Data.Aeson as A
import qualified Data.ByteString as B
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Data (Data (..))
import qualified Data.Data as DData
import Data.Hashable (Hashable (..))
import Data.Ord (comparing)
import Data.Typeable (Typeable)
import Data.Word (Word16)
import GHC.Generics (Generic)
import Prettyprinter (Pretty (..), (<+>))
import Codec.Encryption.OpenPGP.Types.Internal.Base
import Codec.Encryption.OpenPGP.Types.Internal.CryptonNewtypes
data EdSigningCurve
= EdSigningCurve25519
| EdSigningCurve448
deriving (Data, Eq, Generic, Ord, Show, Typeable)
instance Hashable EdSigningCurve
instance Pretty EdSigningCurve where
pretty EdSigningCurve25519 = pretty "Ed25519"
pretty EdSigningCurve448 = pretty "Ed448"
instance A.FromJSON EdSigningCurve
instance A.ToJSON EdSigningCurve
newtype EPoint
= EPoint
{ unEPoint :: Integer
}
deriving (Data, Eq, Generic, Ord, Pretty, Show, Typeable)
instance Hashable EPoint
instance A.FromJSON EPoint
instance A.ToJSON EPoint
data EdPoint
= PrefixedNativeEPoint EPoint
| NativeEPoint EPoint
deriving (Data, Eq, Generic, Ord, Show, Typeable)
instance Hashable EdPoint
instance Pretty EdPoint where
pretty (PrefixedNativeEPoint ep) = pretty "prefixed-native" <+> pretty ep
pretty (NativeEPoint ep) = pretty "native" <+> pretty ep
instance A.FromJSON EdPoint
instance A.ToJSON EdPoint
data PKey
= RSAPubKey RSA_PublicKey
| DSAPubKey DSA_PublicKey
| ElGamalPubKey Integer Integer Integer
| ECDHPubKey PKey HashAlgorithm SymmetricAlgorithm
| ECDSAPubKey ECDSA_PublicKey
| EdDSAPubKey EdSigningCurve EdPoint
| MLKEMPubKey B.ByteString
| MLDSAPubKey B.ByteString
| SLHDSAPubKey B.ByteString
| UnknownPKey ByteString
deriving (Data, Eq, Generic, Ord, Show, Typeable)
instance Hashable PKey
instance Pretty PKey where
pretty (RSAPubKey p) = pretty "RSA" <+> pretty p
pretty (DSAPubKey p) = pretty "DSA" <+> pretty p
pretty (ElGamalPubKey p g y) =
pretty "Elgamal" <+> pretty p <+> pretty g <+> pretty y
pretty (ECDHPubKey p ha sa) =
pretty "ECDH" <+> pretty p <+> pretty ha <+> pretty sa
pretty (ECDSAPubKey p) = pretty "ECDSA" <+> pretty p
pretty (EdDSAPubKey c ep) = pretty c <+> pretty ep
pretty (MLKEMPubKey bs) = pretty "ML-KEM" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (MLDSAPubKey bs) = pretty "ML-DSA" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (SLHDSAPubKey bs) = pretty "SLH-DSA" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (UnknownPKey bs) = pretty "<unknown>" <+> pretty (bsToHexUpper bs)
instance A.ToJSON PKey where
toJSON (RSAPubKey p) = A.toJSON p
toJSON (DSAPubKey p) = A.toJSON p
toJSON (ElGamalPubKey p g y) = A.toJSON (p, g, y)
toJSON (ECDHPubKey p ha sa) = A.toJSON (p, ha, sa)
toJSON (ECDSAPubKey p) = A.toJSON p
toJSON (EdDSAPubKey c ep) = A.toJSON (c, ep)
toJSON (MLKEMPubKey bs) = A.toJSON (B.unpack bs)
toJSON (MLDSAPubKey bs) = A.toJSON (B.unpack bs)
toJSON (SLHDSAPubKey bs) = A.toJSON (B.unpack bs)
toJSON (UnknownPKey bs) = A.toJSON (BL.unpack bs)
data SKey
= RSAPrivateKey RSA_PrivateKey
| DSAPrivateKey DSA_PrivateKey
| ElGamalPrivateKey Integer
| ECDHPrivateKey ECDSA_PrivateKey
| ECDSAPrivateKey ECDSA_PrivateKey
| EdDSAPrivateKey EdSigningCurve B.ByteString
| X25519PrivateKey B.ByteString
| X448PrivateKey B.ByteString
| MLKEMPrivateKey B.ByteString
| MLDSAPrivateKey B.ByteString
| SLHDSAPrivateKey B.ByteString
| UnknownSKey ByteString
deriving (Data, Eq, Generic, Ord, Show, Typeable)
instance Hashable SKey
instance Pretty SKey where
pretty (RSAPrivateKey p) = pretty "RSA" <+> pretty p
pretty (DSAPrivateKey p) = pretty "DSA" <+> pretty p
pretty (ElGamalPrivateKey p) = pretty "Elgamal" <+> pretty p
pretty (ECDHPrivateKey p) = pretty "ECDH" <+> pretty p
pretty (ECDSAPrivateKey p) = pretty "ECDSA" <+> pretty p
pretty (EdDSAPrivateKey c bs) =
pretty c <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (X25519PrivateKey bs) = pretty "X25519" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (X448PrivateKey bs) = pretty "X448" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (MLKEMPrivateKey bs) =
pretty "ML-KEM-priv" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (MLDSAPrivateKey bs) =
pretty "ML-DSA-priv" <+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (SLHDSAPrivateKey bs) =
pretty "SLH-DSA-priv"
<+> pretty (bsToHexUpper (BL.fromStrict bs))
pretty (UnknownSKey bs) = pretty "<unknown>" <+> pretty (bsToHexUpper bs)
instance A.ToJSON SKey where
toJSON (RSAPrivateKey k) = A.toJSON k
toJSON (DSAPrivateKey k) = A.toJSON k
toJSON (ElGamalPrivateKey k) = A.toJSON k
toJSON (ECDHPrivateKey k) = A.toJSON k
toJSON (ECDSAPrivateKey k) = A.toJSON k
toJSON (EdDSAPrivateKey c bs) = A.toJSON (c, B.unpack bs)
toJSON (X25519PrivateKey bs) = A.toJSON (B.unpack bs)
toJSON (X448PrivateKey bs) = A.toJSON (B.unpack bs)
toJSON (MLKEMPrivateKey bs) = A.toJSON (B.unpack bs)
toJSON (MLDSAPrivateKey bs) = A.toJSON (B.unpack bs)
toJSON (SLHDSAPrivateKey bs) = A.toJSON (B.unpack bs)
toJSON (UnknownSKey bs) = A.toJSON (BL.unpack bs)
data PKPayload (v :: KeyVersion) where
PKPayloadV3
:: ThirtyTwoBitTimeStamp
-> V3Expiration
-> PubKeyAlgorithm
-> PKey
-> PKPayload 'DeprecatedV3
PKPayloadV4
:: ThirtyTwoBitTimeStamp
-> PubKeyAlgorithm
-> PKey
-> PKPayload 'V4
PKPayloadV6
:: ThirtyTwoBitTimeStamp
-> PubKeyAlgorithm
-> PKey
-> PKPayload 'V6
deriving instance Eq (PKPayload v)
deriving instance Ord (PKPayload v)
deriving instance Show (PKPayload v)
instance Hashable (PKPayload v) where
hashWithSalt s = hashWithSalt s . pkPayloadFields
instance Pretty (PKPayload v) where
pretty pkp =
let (kv, ts, v3e, pka, p) = pkPayloadFields pkp
in pretty kv
<+> pretty ts
<+> pretty v3e
<+> pretty pka
<+> pretty p
instance A.ToJSON (PKPayload v) where
toJSON = A.toJSON . pkPayloadFields
data SomePKPayload where
SomePKPayload :: PKPayload v -> SomePKPayload
deriving instance Show SomePKPayload
deriving instance Typeable SomePKPayload
pkPayloadDataType :: DData.DataType
pkPayloadDataType =
DData.mkDataType
"Codec.Encryption.OpenPGP.Types.Internal.PKITypes.SomePKPayload"
[pkPayloadConstr]
pkPayloadConstr :: DData.Constr
pkPayloadConstr = DData.mkConstr pkPayloadDataType "PKPayload" [] DData.Prefix
instance Data SomePKPayload where
gfoldl f z (PKPayload kv ts v3e pka p) =
z PKPayload `f` kv `f` ts `f` v3e `f` pka `f` p
gunfold k z c
| c == pkPayloadConstr = k (k (k (k (k (z PKPayload)))))
| otherwise =
error "gunfold: invalid constructor for SomePKPayload"
toConstr _ = pkPayloadConstr
dataTypeOf _ = pkPayloadDataType
instance Eq SomePKPayload where
a == b = somePKPayloadFields a == somePKPayloadFields b
instance Ord SomePKPayload where
compare = comparing somePKPayloadFields
instance Hashable SomePKPayload where
hashWithSalt s = hashWithSalt s . somePKPayloadFields
instance Pretty SomePKPayload where
pretty (PKPayload kv ts v3e pka p) =
pretty kv
<+> pretty ts
<+> pretty v3e
<+> pretty pka
<+> pretty p
instance A.ToJSON SomePKPayload where
toJSON = A.toJSON . somePKPayloadFields
pkPayloadFields
:: PKPayload v
-> ( KeyVersion
, ThirtyTwoBitTimeStamp
, V3Expiration
, PubKeyAlgorithm
, PKey
)
pkPayloadFields (PKPayloadV3 ts v3e pka p) = (DeprecatedV3, ts, v3e, pka, p)
pkPayloadFields (PKPayloadV4 ts pka p) = (V4, ts, 0, pka, p)
pkPayloadFields (PKPayloadV6 ts pka p) = (V6, ts, 0, pka, p)
somePKPayloadFields
:: SomePKPayload
-> ( KeyVersion
, ThirtyTwoBitTimeStamp
, V3Expiration
, PubKeyAlgorithm
, PKey
)
somePKPayloadFields (SomePKPayload pkp) = pkPayloadFields pkp
pattern PKPayload
:: KeyVersion
-> ThirtyTwoBitTimeStamp
-> V3Expiration
-> PubKeyAlgorithm
-> PKey
-> SomePKPayload
pattern PKPayload kv ts v3e pka p <-
(somePKPayloadFields -> (kv, ts, v3e, pka, p))
where
PKPayload DeprecatedV3 ts v3e pka p = SomePKPayload (PKPayloadV3 ts v3e pka p)
PKPayload V4 ts _ pka p = SomePKPayload (PKPayloadV4 ts pka p)
PKPayload V6 ts _ pka p = SomePKPayload (PKPayloadV6 ts pka p)
{-# COMPLETE PKPayload #-}
_keyVersion :: SomePKPayload -> KeyVersion
_keyVersion (PKPayload kv _ _ _ _) = kv
_timestamp :: SomePKPayload -> ThirtyTwoBitTimeStamp
_timestamp (PKPayload _ ts _ _ _) = ts
_v3exp :: SomePKPayload -> V3Expiration
_v3exp (PKPayload _ _ v3e _ _) = v3e
_pkalgo :: SomePKPayload -> PubKeyAlgorithm
_pkalgo (PKPayload _ _ _ pka _) = pka
_pubkey :: SomePKPayload -> PKey
_pubkey (PKPayload _ _ _ _ p) = p
data SKAddendum
= SUS16bit SymmetricAlgorithm S2K IV ByteString
| SUSSHA1 SymmetricAlgorithm S2K IV ByteString
| SUSAEAD SymmetricAlgorithm AEADAlgorithm S2K IV ByteString
| SUSym SymmetricAlgorithm IV ByteString
| SUUnencrypted SKey Word16
deriving (Data, Eq, Generic, Show, Typeable)
instance Ord SKAddendum where
compare (SUS16bit sa1 s2k1 iv1 bs1) (SUS16bit sa2 s2k2 iv2 bs2) =
compare sa1 sa2
<> compare s2k1 s2k2
<> compare iv1 iv2
<> compare bs1 bs2
compare (SUSSHA1 sa1 s2k1 iv1 bs1) (SUSSHA1 sa2 s2k2 iv2 bs2) =
compare sa1 sa2
<> compare s2k1 s2k2
<> compare iv1 iv2
<> compare bs1 bs2
compare (SUSAEAD sa1 aa1 s2k1 iv1 bs1) (SUSAEAD sa2 aa2 s2k2 iv2 bs2) =
compare sa1 sa2
<> compare aa1 aa2
<> compare s2k1 s2k2
<> compare iv1 iv2
<> compare bs1 bs2
compare (SUSym sa1 iv1 bs1) (SUSym sa2 iv2 bs2) =
compare sa1 sa2 <> compare iv1 iv2 <> compare bs1 bs2
compare (SUUnencrypted sk1 ck1) (SUUnencrypted sk2 ck2) =
compare sk1 sk2 <> compare ck1 ck2
compare SUS16bit {} SUSSHA1 {} = LT
compare SUS16bit {} SUSAEAD {} = LT
compare SUS16bit {} SUSym {} = LT
compare SUS16bit {} SUUnencrypted {} = LT
compare SUSSHA1 {} SUS16bit {} = GT
compare SUSSHA1 {} SUSAEAD {} = LT
compare SUSSHA1 {} SUSym {} = LT
compare SUSSHA1 {} SUUnencrypted {} = LT
compare SUSAEAD {} SUS16bit {} = GT
compare SUSAEAD {} SUSSHA1 {} = GT
compare SUSAEAD {} SUSym {} = LT
compare SUSAEAD {} SUUnencrypted {} = LT
compare SUSym {} SUS16bit {} = GT
compare SUSym {} SUSSHA1 {} = GT
compare SUSym {} SUSAEAD {} = GT
compare SUSym {} SUUnencrypted {} = LT
compare SUUnencrypted {} _ = GT
instance Hashable SKAddendum
instance Pretty SKAddendum where
pretty (SUS16bit sa s2k iv bs) =
pretty "SUS16bit"
<+> pretty sa
<+> pretty s2k
<+> pretty iv
<+> pretty (bsToHexUpper bs)
pretty (SUSSHA1 sa s2k iv bs) =
pretty "SUSSHA1"
<+> pretty sa
<+> pretty s2k
<+> pretty iv
<+> pretty (bsToHexUpper bs)
pretty (SUSAEAD sa aa s2k iv bs) =
pretty "SUSAEAD"
<+> pretty sa
<+> pretty aa
<+> pretty s2k
<+> pretty iv
<+> pretty (bsToHexUpper bs)
pretty (SUSym sa iv bs) =
pretty "SUSym"
<+> pretty sa
<+> pretty iv
<+> pretty (bsToHexUpper bs)
pretty (SUUnencrypted s ck) =
pretty "SUUnencrypted" <+> pretty s <+> pretty ck
instance A.ToJSON SKAddendum where
toJSON (SUS16bit sa s2k iv bs) = A.toJSON (sa, s2k, iv, BL.unpack bs)
toJSON (SUSSHA1 sa s2k iv bs) = A.toJSON (sa, s2k, iv, BL.unpack bs)
toJSON (SUSAEAD sa aa s2k iv bs) = A.toJSON (sa, aa, s2k, iv, BL.unpack bs)
toJSON (SUSym sa iv bs) = A.toJSON (sa, iv, BL.unpack bs)
toJSON (SUUnencrypted s ck) = A.toJSON (s, ck)
class LegacyKeyVersion (v :: KeyVersion)
instance LegacyKeyVersion 'DeprecatedV3
instance LegacyKeyVersion 'V4
data SKAddendumV (v :: KeyVersion) where
SKA16bit
:: (LegacyKeyVersion v)
=> SymmetricAlgorithm
-> S2K
-> IV
-> ByteString
-> SKAddendumV v
SKASHA1Legacy
:: (LegacyKeyVersion v)
=> SymmetricAlgorithm
-> S2K
-> IV
-> ByteString
-> SKAddendumV v
SKASHA1V6
:: SymmetricAlgorithm
-> S2K
-> IV
-> ByteString
-> SKAddendumV 'V6
SKAAEADV6
:: SymmetricAlgorithm
-> AEADAlgorithm
-> S2K
-> IV
-> ByteString
-> SKAddendumV 'V6
SKAAEADLegacy
:: (LegacyKeyVersion v)
=> SymmetricAlgorithm
-> AEADAlgorithm
-> S2K
-> IV
-> ByteString
-> SKAddendumV v
SKASymLegacy
:: (LegacyKeyVersion v)
=> SymmetricAlgorithm
-> IV
-> ByteString
-> SKAddendumV v
SKASymV6
:: SymmetricAlgorithm
-> IV
-> ByteString
-> SKAddendumV 'V6
SKAUnencryptedLegacy
:: (LegacyKeyVersion v)
=> SKey
-> Word16
-> SKAddendumV v
SKAUnencryptedV6
:: SKey
-> SKAddendumV 'V6
deriving instance Show (SKAddendumV v)
data SomeSKAddendumV where
SomeSKAddendumV :: SKAddendumV v -> SomeSKAddendumV
deriving instance Show SomeSKAddendumV
toSKAddendum :: SKAddendumV v -> SKAddendum
toSKAddendum (SKA16bit sa s2k iv bs) = SUS16bit sa s2k iv bs
toSKAddendum (SKASHA1Legacy sa s2k iv bs) = SUSSHA1 sa s2k iv bs
toSKAddendum (SKASHA1V6 sa s2k iv bs) = SUSSHA1 sa s2k iv bs
toSKAddendum (SKAAEADV6 sa aa s2k iv bs) = SUSAEAD sa aa s2k iv bs
toSKAddendum (SKAAEADLegacy sa aa s2k iv bs) = SUSAEAD sa aa s2k iv bs
toSKAddendum (SKASymLegacy sa iv bs) = SUSym sa iv bs
toSKAddendum (SKASymV6 sa iv bs) = SUSym sa iv bs
toSKAddendum (SKAUnencryptedLegacy sk checksum) = SUUnencrypted sk checksum
toSKAddendum (SKAUnencryptedV6 sk) = SUUnencrypted sk 0
fromSKAddendumForKeyVersion
:: KeyVersion
-> SKAddendum
-> Either String SomeSKAddendumV
fromSKAddendumForKeyVersion DeprecatedV3 (SUS16bit sa s2k iv bs) =
Right
( SomeSKAddendumV
(SKA16bit sa s2k iv bs :: SKAddendumV 'DeprecatedV3)
)
fromSKAddendumForKeyVersion DeprecatedV3 (SUSSHA1 sa s2k iv bs) =
Right
( SomeSKAddendumV
(SKASHA1Legacy sa s2k iv bs :: SKAddendumV 'DeprecatedV3)
)
fromSKAddendumForKeyVersion DeprecatedV3 (SUSym sa iv bs) =
Right
( SomeSKAddendumV
(SKASymLegacy sa iv bs :: SKAddendumV 'DeprecatedV3)
)
fromSKAddendumForKeyVersion DeprecatedV3 (SUUnencrypted sk checksum) =
Right
( SomeSKAddendumV
(SKAUnencryptedLegacy sk checksum :: SKAddendumV 'DeprecatedV3)
)
fromSKAddendumForKeyVersion DeprecatedV3 (SUSAEAD sa aa s2k iv bs) =
Right
( SomeSKAddendumV
(SKAAEADLegacy sa aa s2k iv bs :: SKAddendumV 'DeprecatedV3)
)
fromSKAddendumForKeyVersion V4 (SUS16bit sa s2k iv bs) =
Right
(SomeSKAddendumV (SKA16bit sa s2k iv bs :: SKAddendumV 'V4))
fromSKAddendumForKeyVersion V4 (SUSSHA1 sa s2k iv bs) =
Right
(SomeSKAddendumV (SKASHA1Legacy sa s2k iv bs :: SKAddendumV 'V4))
fromSKAddendumForKeyVersion V4 (SUSym sa iv bs) =
Right
(SomeSKAddendumV (SKASymLegacy sa iv bs :: SKAddendumV 'V4))
fromSKAddendumForKeyVersion V4 (SUUnencrypted sk checksum) =
Right
( SomeSKAddendumV
(SKAUnencryptedLegacy sk checksum :: SKAddendumV 'V4)
)
fromSKAddendumForKeyVersion V4 (SUSAEAD sa aa s2k iv bs) =
Right
( SomeSKAddendumV
(SKAAEADLegacy sa aa s2k iv bs :: SKAddendumV 'V4)
)
fromSKAddendumForKeyVersion V6 (SUS16bit _ _ _ _) =
Left
"v6 secret keys must not use 16-bit checksum protected secret key addendums"
fromSKAddendumForKeyVersion V6 (SUSSHA1 sa s2k iv bs) =
Right (SomeSKAddendumV (SKASHA1V6 sa s2k iv bs))
fromSKAddendumForKeyVersion V6 (SUSAEAD sa aa s2k iv bs) =
Right (SomeSKAddendumV (SKAAEADV6 sa aa s2k iv bs))
fromSKAddendumForKeyVersion V6 (SUSym sa iv bs) =
Right (SomeSKAddendumV (SKASymV6 sa iv bs))
fromSKAddendumForKeyVersion V6 (SUUnencrypted sk _) =
Right (SomeSKAddendumV (SKAUnencryptedV6 sk))
fromSKAddendumForPKPayload
:: SomePKPayload
-> SKAddendum
-> Either String SomeSKAddendumV
fromSKAddendumForPKPayload pkp =
fromSKAddendumForKeyVersion (_keyVersion pkp)