diff --git a/crypto/base_test.go b/crypto/base_test.go index c4429c76..35924282 100644 --- a/crypto/base_test.go +++ b/crypto/base_test.go @@ -35,8 +35,8 @@ func readTestFile(name string, trimNewlines bool) string { func init() { testPGP = PGP() testPGP.defaultTime = NewConstantClock(testTime) // 2019-05-13T13:37:07+00:00 - testProfiles = []*profile.Custom{profile.Default(), profile.RFC4880(), profile.RFC9580()} - testProfileNames = []string{"Default", "RFC4880", "RFC9580"} + testProfiles = []*profile.Custom{profile.Default(), profile.RFC4880(), profile.RFC9580(), profile.PQC()} + testProfileNames = []string{"Default", "RFC4880", "RFC9580", "PQC"} initEncDecTest() initGenerateKeys() initArmoredKeys() diff --git a/crypto/key_clear.go b/crypto/key_clear.go index b19aae86..1721d42d 100644 --- a/crypto/key_clear.go +++ b/crypto/key_clear.go @@ -12,8 +12,14 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ed448" "github.com/ProtonMail/go-crypto/openpgp/eddsa" "github.com/ProtonMail/go-crypto/openpgp/elgamal" + "github.com/ProtonMail/go-crypto/openpgp/mldsa_eddsa" + "github.com/ProtonMail/go-crypto/openpgp/mlkem_ecdh" + "github.com/ProtonMail/go-crypto/openpgp/slhdsa" "github.com/ProtonMail/go-crypto/openpgp/x25519" "github.com/ProtonMail/go-crypto/openpgp/x448" + + "github.com/cloudflare/circl/sign/mldsa/mldsa65" + "github.com/cloudflare/circl/sign/mldsa/mldsa87" ) // Clear zeroes the sensitive data in the session key. @@ -75,6 +81,12 @@ func clearPrivateKey(privateKey interface{}) error { return clearX448PrivateKey(priv) case *ed448.PrivateKey: return clearEd448PrivateKey(priv) + case *mlkem_ecdh.PrivateKey: + return clearMlKemECDHPrivateKey(priv) + case *mldsa_eddsa.PrivateKey: + return clearMlDsaEdDSAPrivateKey(priv) + case *slhdsa.PrivateKey: + return clearSlhDsaPrivateKey(priv) default: return errors.New("gopenpgp: unknown private key") } @@ -164,3 +176,41 @@ func clearEd448PrivateKey(priv *ed448.PrivateKey) error { return nil } + +func clearMlKemECDHPrivateKey(priv *mlkem_ecdh.PrivateKey) error { + // Note: the priv.SecretMlkem type is internal in circl, so we can't clear its fields here. + // And, the key material is stored in a slice, so we can't overwrite it with zeros here. + // The best we can do is let the garbage collector clean it up. + priv.SecretMlkem = nil + clearMem(priv.SecretMlkemSeed) + clearMem(priv.SecretEc) + + return nil +} + +func clearMlDsaEdDSAPrivateKey(priv *mldsa_eddsa.PrivateKey) (err error) { + // Note: the priv.SecretMldsa type is internal in circl, so we can't clear its fields here. + // Instead, we overwrite the entire struct with zero values. + switch secretMldsa := priv.SecretMldsa.(type) { + case *mldsa65.PrivateKey: + *secretMldsa = mldsa65.PrivateKey{} + case *mldsa87.PrivateKey: + *secretMldsa = mldsa87.PrivateKey{} + default: + err = errors.New("gopenpgp: unexpected ML-DSA private key type") + } + priv.SecretMldsa = nil + clearMem(priv.SecretMldsaSeed) + clearMem(priv.SecretEc) + + return +} + +func clearSlhDsaPrivateKey(priv *slhdsa.PrivateKey) error { + // Note: the priv.SecretSlhdsa type is internal in circl, so we can't clear its fields here. + // And, the key material is stored in a slice, so we can't overwrite it with zeros here. + // The best we can do is let the garbage collector clean it up. + priv.SecretSlhdsa = nil + + return nil +} diff --git a/profile/preset.go b/profile/preset.go index 01ce53a1..8ec9844e 100644 --- a/profile/preset.go +++ b/profile/preset.go @@ -80,3 +80,39 @@ func RFC9580() *Custom { V6: true, } } + +func PQC() *Custom { + setKeyAlgorithm := func(cfg *packet.Config, securityLevel int8) { + switch securityLevel { + case constants.HighSecurity: + cfg.Algorithm = packet.PubKeyAlgoMldsa87Ed448 + default: + cfg.Algorithm = packet.PubKeyAlgoMldsa65Ed25519 + } + } + return &Custom{ + SetKeyAlgorithm: setKeyAlgorithm, + Hash: crypto.SHA512, + CipherEncryption: packet.CipherAES256, + CipherKeyEncryption: packet.CipherAES256, + CompressionAlgorithm: packet.CompressionZLIB, + AeadKeyEncryption: &packet.AEADConfig{ + DefaultMode: packet.AEADModeGCM, + }, + AeadEncryption: &packet.AEADConfig{ + DefaultMode: packet.AEADModeGCM, + }, + CompressionConfiguration: &packet.CompressionConfig{ + Level: 6, + }, + S2kKeyEncryption: &s2k.Config{ + S2KMode: s2k.Argon2S2K, + Argon2Config: &s2k.Argon2Config{}, + }, + S2kEncryption: &s2k.Config{ + S2KMode: s2k.Argon2S2K, + Argon2Config: &s2k.Argon2Config{}, + }, + V6: true, + } +}