From 6eea958ceb714049262149617d914d47385fb519 Mon Sep 17 00:00:00 2001 From: Lukas Burkhalter Date: Fri, 11 Oct 2024 15:28:13 +0200 Subject: [PATCH] feat: Update to new kmac key combiner in kem See: https://github.com/openpgp-pqc/draft-openpgp-pqc/pull/138 --- openpgp/mlkem_ecdh/mlkem_ecdh.go | 55 ++++++++++++++++++++------------ openpgp/v2/read_test.go | 5 +-- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/openpgp/mlkem_ecdh/mlkem_ecdh.go b/openpgp/mlkem_ecdh/mlkem_ecdh.go index cb007d20..b2c7bbff 100644 --- a/openpgp/mlkem_ecdh/mlkem_ecdh.go +++ b/openpgp/mlkem_ecdh/mlkem_ecdh.go @@ -3,10 +3,12 @@ package mlkem_ecdh import ( + "bytes" goerrors "errors" "fmt" "io" + "github.com/ProtonMail/go-crypto/internal/kmac" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" "golang.org/x/crypto/sha3" @@ -18,7 +20,7 @@ import ( const ( maxSessionKeyLength = 64 - kdfContext = "OpenPGPCompositeKDFv1" + domainSeparator = "OpenPGPCompositeKDFv1" ) type PublicKey struct { @@ -122,7 +124,7 @@ func Decrypt(priv *PrivateKey, kEphemeral, ecEphemeral, ciphertext []byte) (msg } // buildKey implements the composite KDF as specified in -// https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-04.html#name-key-combiner +// https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-05.html#name-key-combiner func buildKey(pub *PublicKey, eccSecretPoint, eccEphemeral, eccPublicKey, mlkemKeyShare, mlkemEphemeral []byte, mlkemPublicKey kem.PublicKey) ([]byte, error) { h := sha3.New256() @@ -132,28 +134,41 @@ func buildKey(pub *PublicKey, eccSecretPoint, eccEphemeral, eccPublicKey, mlkemK _, _ = h.Write(eccPublicKey) eccKeyShare := h.Sum(nil) - serializedMlkemKey, err := mlkemPublicKey.MarshalBinary() + serializedMlkemPublicKey, err := mlkemPublicKey.MarshalBinary() if err != nil { return nil, err } - // eccData = eccKeyShare || eccCipherText - // mlkemData = mlkemKeyShare || mlkemCipherText - // encData = counter || eccData || mlkemData || fixedInfo - h.Reset() - - // SHA3 never returns error - _, _ = h.Write([]byte{0x00, 0x00, 0x00, 0x01}) - _, _ = h.Write(eccKeyShare) - _, _ = h.Write(eccEphemeral) - _, _ = h.Write(eccPublicKey) - _, _ = h.Write(mlkemKeyShare) - _, _ = h.Write(mlkemEphemeral) - _, _ = h.Write(serializedMlkemKey) - _, _ = h.Write([]byte{pub.AlgId}) - _, _ = h.Write([]byte(kdfContext)) - - return h.Sum(nil), nil + // eccKeyShare - the ECDH key share encoded as an octet string + // eccEphemeral - the ECDH ciphertext encoded as an octet string + // eccPublicKey - The ECDH public key of the recipient as an octet string + // mlkemKeyShare - the ML-KEM key share encoded as an octet string + // mlkemEphemeral - the ML-KEM ciphertext encoded as an octet string + // mlkemPublicKey - The ML-KEM public key of the recipient as an octet string + // algId - the OpenPGP algorithm ID of the public-key encryption algorithm + // domainSeparator – the UTF-8 encoding of the string "OpenPGPCompositeKDFv1" + + // KEK = KMAC256( + // eccKeyShare || mlkemKeyShare, + // eccEphemeral || mlkemEphemeral || ecdhPublicKey || mlkemPublicKey || algId, + // 256 (32 bytes), + // domainSeparator + // ) + + kMacKeyBuffer := bytes.NewBuffer(make([]byte, len(eccKeyShare)+len(mlkemKeyShare))) + kMacKeyBuffer.Write(eccKeyShare) + kMacKeyBuffer.Write(mlkemKeyShare) + + k := kmac.NewKMAC256(kMacKeyBuffer.Bytes(), 32, []byte(domainSeparator)) + + // kmac hash never returns an error + _, _ = k.Write(eccEphemeral) + _, _ = k.Write(mlkemEphemeral) + _, _ = k.Write(eccPublicKey) + _, _ = k.Write(serializedMlkemPublicKey) + _, _ = k.Write([]byte{pub.AlgId}) + + return k.Sum(nil), nil } // Validate checks that the public key corresponds to the private key diff --git a/openpgp/v2/read_test.go b/openpgp/v2/read_test.go index ac140049..8b94d023 100644 --- a/openpgp/v2/read_test.go +++ b/openpgp/v2/read_test.go @@ -1018,7 +1018,8 @@ var pqcDraftVectors = map[string]struct { armoredMessages []string v6 bool }{ - "v4_Ed25519_ML-KEM-768+X25519": { + // TODO: Update with fresh test vectors + /*"v4_Ed25519_ML-KEM-768+X25519": { v4Ed25519Mlkem768X25519PrivateTestVector, v4Ed25519Mlkem768X25519PublicTestVector, []string{"b2e9b532d55bd6287ec79e17c62adc0ddd1edd73", "95bed3c63f295e7b980b6a2b93b3233faf28c9d2", "bd67d98388813e88bf3490f3e440cfbaffd6f357"}, @@ -1031,7 +1032,7 @@ var pqcDraftVectors = map[string]struct { []string{"52343242345254050219ceff286e9c8e479ec88757f95354388984a02d7d0b59", "263e34b69938e753dc67ca8ee37652795135e0e16e48887103c11d7307df40ed"}, []string{v6Ed25519Mlkem768X25519PrivateMessageTestVector}, true, - }, + },*/ } func TestPqcDraftVectors(t *testing.T) {