diff --git a/openpgp/internal/algorithm/aead.go b/openpgp/internal/algorithm/aead.go index 02d26a862..d06706518 100644 --- a/openpgp/internal/algorithm/aead.go +++ b/openpgp/internal/algorithm/aead.go @@ -12,11 +12,6 @@ import ( // operation. type AEADMode uint8 -// Id returns the algorithm ID, as a byte, of mode. -func (mode AEADMode) Id() uint8 { - return uint8(mode) -} - // Supported modes of operation (see RFC4880bis [EAX] and RFC7253) const ( AEADModeEAX = AEADMode(1) diff --git a/openpgp/internal/algorithm/cipher.go b/openpgp/internal/algorithm/cipher.go index df3e5396c..c76a75bcd 100644 --- a/openpgp/internal/algorithm/cipher.go +++ b/openpgp/internal/algorithm/cipher.go @@ -46,7 +46,7 @@ var CipherById = map[uint8]Cipher{ type CipherFunction uint8 -// Id returns the algorithm ID, as a byte, of cipher. +// ID returns the algorithm Id, as a byte, of cipher. func (sk CipherFunction) Id() uint8 { return uint8(sk) } diff --git a/openpgp/internal/encoding/short_byte_string.go b/openpgp/internal/encoding/short_byte_string.go new file mode 100644 index 000000000..0c3b91233 --- /dev/null +++ b/openpgp/internal/encoding/short_byte_string.go @@ -0,0 +1,50 @@ +package encoding + +import ( + "io" +) + +type ShortByteString struct { + length uint8 + data []byte +} + +func NewShortByteString(data []byte) *ShortByteString { + byteLength := uint8(len(data)) + + return &ShortByteString{byteLength, data} +} + +func (byteString *ShortByteString) Bytes() []byte { + return byteString.data +} + +func (byteString *ShortByteString) BitLength() uint16 { + return uint16(byteString.length) * 8 +} + +func (byteString *ShortByteString) EncodedBytes() []byte { + encodedLength := [1]byte{ + uint8(byteString.length), + } + return append(encodedLength[:], byteString.data...) +} + +func (byteString *ShortByteString) EncodedLength() uint16 { + return uint16(byteString.length) + 1 +} + +func (byteString *ShortByteString) ReadFrom(r io.Reader) (int64, error) { + var lengthBytes [1]byte + if n, err := io.ReadFull(r, lengthBytes[:]); err != nil { + return int64(n), err + } + + byteString.length = uint8(lengthBytes[0]) + + byteString.data = make([]byte, byteString.length) + if n, err := io.ReadFull(r, byteString.data); err != nil { + return int64(n + 1), err + } + return int64(byteString.length + 1), nil +} diff --git a/openpgp/internal/encoding/short_byte_string_test.go b/openpgp/internal/encoding/short_byte_string_test.go new file mode 100644 index 000000000..37510a355 --- /dev/null +++ b/openpgp/internal/encoding/short_byte_string_test.go @@ -0,0 +1,61 @@ +package encoding + +import ( + "bytes" + "testing" +) + +var octetStreamTests = []struct { + data []byte +}{ + { + data: []byte{0x0, 0x0, 0x0}, + }, + { + data: []byte{0x1, 0x2, 0x03}, + }, + { + data: make([]byte, 255), + }, +} + +func TestShortByteString(t *testing.T) { + for i, test := range octetStreamTests { + octetStream := NewShortByteString(test.data) + + if b := octetStream.Bytes(); !bytes.Equal(b, test.data) { + t.Errorf("#%d: bad creation got:%x want:%x", i, b, test.data) + } + + expectedBitLength := uint16(len(test.data)) * 8 + if bitLength := octetStream.BitLength(); bitLength != expectedBitLength { + t.Errorf("#%d: bad bit length got:%d want :%d", i, bitLength, expectedBitLength) + } + + expectedEncodedLength := uint16(len(test.data)) + 1 + if encodedLength := octetStream.EncodedLength(); encodedLength != expectedEncodedLength { + t.Errorf("#%d: bad encoded length got:%d want:%d", i, encodedLength, expectedEncodedLength) + } + + encodedBytes := octetStream.EncodedBytes() + if !bytes.Equal(encodedBytes[1:], test.data) { + t.Errorf("#%d: bad encoded bytes got:%x want:%x", i, encodedBytes[1:], test.data) + } + + encodedLength := int(encodedBytes[0]) + if encodedLength != len(test.data) { + t.Errorf("#%d: bad encoded length got:%d want%d", i, encodedLength, len(test.data)) + } + + newStream := new(ShortByteString) + newStream.ReadFrom(bytes.NewReader(encodedBytes)) + + if !checkEquality(newStream, octetStream) { + t.Errorf("#%d: bad parsing of encoded octet stream", i) + } + } +} + +func checkEquality(left *ShortByteString, right *ShortByteString) bool { + return (left.length == right.length) && (bytes.Equal(left.data, right.data)) +} diff --git a/openpgp/key_generation.go b/openpgp/key_generation.go index 3b15eeb2c..df4fab454 100644 --- a/openpgp/key_generation.go +++ b/openpgp/key_generation.go @@ -383,10 +383,9 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { return x25519.GenerateKey(config.Random()) case packet.PubKeyAlgoEd448, packet.PubKeyAlgoX448: // When passing Ed448, we generate an x448 subkey return x448.GenerateKey(config.Random()) - case packet.ExperimentalPubKeyAlgoHMAC, packet.ExperimentalPubKeyAlgoAEAD: // When passing HMAC, we generate an AEAD subkey + case packet.ExperimentalPubKeyAlgoAEAD: cipher := algorithm.CipherFunction(config.Cipher()) - aead := algorithm.AEADMode(config.AEAD().Mode()) - return symmetric.AEADGenerateKey(config.Random(), cipher, aead) + return symmetric.AEADGenerateKey(config.Random(), cipher) case packet.PubKeyAlgoMldsa65Ed25519, packet.PubKeyAlgoMldsa87Ed448: if pubKeyAlgo, err = packet.GetMatchingMlkem(config.PublicKeyAlgorithm()); err != nil { return nil, err diff --git a/openpgp/keys_test.go b/openpgp/keys_test.go index f418b3044..8bddbeb7a 100644 --- a/openpgp/keys_test.go +++ b/openpgp/keys_test.go @@ -1214,13 +1214,16 @@ func TestAddHMACSubkey(t *testing.T) { t.Error("generated Public and Private Key differ") } - if !bytes.Equal(parsedPublicKey.FpSeed[:], generatedPublicKey.FpSeed[:]) { + if !bytes.Equal(parsedPrivateKey.HashSeed[:], generatedPrivateKey.HashSeed[:]) { t.Error("parsed wrong hash seed") } if parsedPrivateKey.PublicKey.Hash != generatedPrivateKey.PublicKey.Hash { t.Error("parsed wrong cipher id") } + if !bytes.Equal(parsedPrivateKey.PublicKey.BindingHash[:], generatedPrivateKey.PublicKey.BindingHash[:]) { + t.Error("parsed wrong binding hash") + } } func TestSerializeSymmetricSubkeyError(t *testing.T) { @@ -1232,13 +1235,13 @@ func TestSerializeSymmetricSubkeyError(t *testing.T) { buf := bytes.NewBuffer(nil) w, _ := armor.Encode(buf, "PGP PRIVATE KEY BLOCK", nil) - entity.PrimaryKey.PubKeyAlgo = 128 + entity.PrimaryKey.PubKeyAlgo = 100 err = entity.Serialize(w) if err == nil { t.Fatal(err) } - entity.PrimaryKey.PubKeyAlgo = 129 + entity.PrimaryKey.PubKeyAlgo = 101 err = entity.Serialize(w) if err == nil { t.Fatal(err) @@ -1289,15 +1292,15 @@ func TestAddAEADSubkey(t *testing.T) { t.Error("generated Public and Private Key differ") } - if !bytes.Equal(parsedPublicKey.FpSeed[:], generatedPublicKey.FpSeed[:]) { + if !bytes.Equal(parsedPrivateKey.HashSeed[:], generatedPrivateKey.HashSeed[:]) { t.Error("parsed wrong hash seed") } if parsedPrivateKey.PublicKey.Cipher.Id() != generatedPrivateKey.PublicKey.Cipher.Id() { t.Error("parsed wrong cipher id") } - if parsedPrivateKey.PublicKey.AEADMode.Id() != generatedPrivateKey.PublicKey.AEADMode.Id() { - t.Error("parsed wrong aead mode") + if !bytes.Equal(parsedPrivateKey.PublicKey.BindingHash[:], generatedPrivateKey.PublicKey.BindingHash[:]) { + t.Error("parsed wrong binding hash") } } @@ -1341,11 +1344,11 @@ func TestNoSymmetricKeySerialized(t *testing.T) { t.Error("Private key was serialized with public") } - firstFpSeed := entity.Subkeys[1].PublicKey.PublicKey.(*symmetric.AEADPublicKey).FpSeed - i = bytes.Index(w.Bytes(), firstFpSeed[:]) + firstBindingHash := entity.Subkeys[1].PublicKey.PublicKey.(*symmetric.AEADPublicKey).BindingHash + i = bytes.Index(w.Bytes(), firstBindingHash[:]) - secondFpSeed := entity.Subkeys[2].PublicKey.PublicKey.(*symmetric.HMACPublicKey).FpSeed - k = bytes.Index(w.Bytes(), secondFpSeed[:]) + secondBindingHash := entity.Subkeys[2].PublicKey.PublicKey.(*symmetric.HMACPublicKey).BindingHash + k = bytes.Index(w.Bytes(), secondBindingHash[:]) if (i > 0) || (k > 0) { t.Errorf("Symmetric public key metadata exported %d %d", i, k) } @@ -2052,19 +2055,17 @@ mQ00BF00000BCAD0000000000000000000000000000000000000000000000000 func TestSymmetricKeys(t *testing.T) { data := `-----BEGIN PGP PRIVATE KEY BLOCK----- -xUoEZyoQrIEImuGs5gaOTekO00WQx6MDnyBPvxmpMiOgeVse7+aqarsAc8F5 -NFm3pVkFDZxX0MqRCPqCwsa/BXJGlrEdMAwSNckOV80xUGVyc2lzdGVudCBT -eW1tZXRyaWMgS2V5IDxwZXJzaXN0ZW50QGV4YW1wbGUub3JnPsKvBBOBCgCF -BYJnKhCsAwsJBwmQDqlD7wlMH9dFFAAAAAAAHAAgc2FsdEBub3RhdGlvbnMu -b3BlbnBncGpzLm9yZ4pMjYSZvCHJsWo5/hQJ3qfDMVMnetCsdS4ZSR6oeO7l -BRUKCAwOBBYAAgECGQECmwMCHgEWIQSbMhUPoVGIuE9u9GAOqUPvCUwf1wAA -QXxcTdhWEMhv+uYj8lUjGbDiqMHc7oGQSattlK89H9KT18dLBGcqEKyACQPs -AUFGawprheOyMQEYmVQUCoTdw4SVAxPk3Wkdbd7YtQATgtwB+JTCDy4de8F+ -yKpsXCJEFrVCsVnFyyY3gH5Wgw5PwpoEGIEKAHAFgmcqEKwJkA6pQ+8JTB/X -RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdwNnP67WFb -3vwFQkTQHsuFKLqvtvpQdnDs9RmvPxLZUwKbDBYhBJsyFQ+hUYi4T270YA6p -Q+8JTB/XAAC0o7OPSjaqMfpfYDUewr7Ehi5kFRCDBwbxLWFryAiICULT -=ywfD +xWoEYs7w5mUIcFvlmkuricX26x138uvHGlwIaxWIbRnx1+ggPcveTcwA4zSZ +n6XcD0Q5aLe6dTEBwCyfUecZ/nA0W8Pl9xBHfjIjQuxcUBnIqxZ061RZPjef +D/XIQga1ftLDelhylQwL7R3TzQ1TeW1tZXRyaWMgS2V5wmkEEGUIAB0FAmLO +8OYECwkHCAMVCAoEFgACAQIZAQIbAwIeAQAhCRCRTKq2ObiQKxYhBMHTTXXF +ULQ2M2bYNJFMqrY5uJArIawgJ+5RSsN8VNuZTKJbG88TIedU05wwKjW3wqvT +X6Z7yfbHagRizvDmZAluL/kJo6hZ1kFENpQkWD/Kfv1vAG3nbxhsVEzBQ6a1 +OAD24BaKJz6gWgj4lASUNK5OuXnLc3J79Bt1iRGkSbiPzRs/bplB4TwbILeC +ZLeDy9kngZDosgsIk5sBgGEqS9y5HiHCVQQYZQgACQUCYs7w5gIbDAAhCRCR +TKq2ObiQKxYhBMHTTXXFULQ2M2bYNJFMqrY5uJArENkgL0Bc+OI/1na0XWqB +TxGVotQ4A/0u0VbOMEUfnrI8Fms= +=RdCW -----END PGP PRIVATE KEY BLOCK----- ` keys, err := ReadArmoredKeyRing(strings.NewReader(data)) diff --git a/openpgp/packet/encrypted_key.go b/openpgp/packet/encrypted_key.go index 5cbc966d4..59f1c847f 100644 --- a/openpgp/packet/encrypted_key.go +++ b/openpgp/packet/encrypted_key.go @@ -17,6 +17,7 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/ecdh" "github.com/ProtonMail/go-crypto/openpgp/elgamal" "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" "github.com/ProtonMail/go-crypto/openpgp/mlkem_ecdh" "github.com/ProtonMail/go-crypto/openpgp/symmetric" @@ -35,12 +36,15 @@ type EncryptedKey struct { CipherFunc CipherFunction // only valid after a successful Decrypt for a v3 packet Key []byte // only valid after a successful Decrypt - encryptedMPI1 encoding.Field // Only valid in RSA, Elgamal, ECDH, AEAD and PQC keys + encryptedMPI1 encoding.Field // Only valid in RSA, Elgamal, ECDH, and PQC keys encryptedMPI2 encoding.Field // Only valid in Elgamal, ECDH and PQC keys encryptedMPI3 encoding.Field // Only valid in PQC keys ephemeralPublicX25519 *x25519.PublicKey // used for x25519 ephemeralPublicX448 *x448.PublicKey // used for x448 encryptedSession []byte // used for x25519 and x448 + + nonce []byte + aeadMode algorithm.AEADMode } func (e *EncryptedKey) parse(r io.Reader) (err error) { @@ -138,11 +142,20 @@ func (e *EncryptedKey) parse(r io.Reader) (err error) { return } case ExperimentalPubKeyAlgoAEAD: - ivAndCiphertext, err := io.ReadAll(r) - if err != nil { - return err + var aeadMode [1]byte + if _, err = readFull(r, aeadMode[:]); err != nil { + return + } + e.aeadMode = algorithm.AEADMode(aeadMode[0]) + nonceLength := e.aeadMode.NonceLength() + e.nonce = make([]byte, nonceLength) + if _, err = readFull(r, e.nonce); err != nil { + return + } + e.encryptedMPI1 = new(encoding.ShortByteString) + if _, err = e.encryptedMPI1.ReadFrom(r); err != nil { + return } - e.encryptedMPI1 = encoding.NewOctetArray(ivAndCiphertext) case PubKeyAlgoMlkem768X25519: if e.encryptedMPI1, e.encryptedMPI2, e.encryptedMPI3, cipherFunction, err = mlkem_ecdh.DecodeFields(r, 32, 1088, e.Version == 6); err != nil { return err @@ -211,7 +224,7 @@ func (e *EncryptedKey) Decrypt(priv *PrivateKey, config *Config) error { b, err = x448.Decrypt(priv.PrivateKey.(*x448.PrivateKey), e.ephemeralPublicX448, e.encryptedSession) case ExperimentalPubKeyAlgoAEAD: priv := priv.PrivateKey.(*symmetric.AEADPrivateKey) - b, err = priv.Decrypt(e.encryptedMPI1.Bytes(), priv.PublicKey.AEADMode) + b, err = priv.Decrypt(e.nonce, e.encryptedMPI1.Bytes(), e.aeadMode) case PubKeyAlgoMlkem768X25519, PubKeyAlgoMlkem1024X448: ecE := e.encryptedMPI1.Bytes() kE := e.encryptedMPI2.Bytes() @@ -453,7 +466,7 @@ func SerializeEncryptedKeyAEADwithHiddenOption(w io.Writer, pub *PublicKey, ciph case PubKeyAlgoX448: return serializeEncryptedKeyX448(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*x448.PublicKey), keyBlock, byte(cipherFunc), version) case ExperimentalPubKeyAlgoAEAD: - return serializeEncryptedKeyAEAD(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*symmetric.AEADPublicKey), keyBlock) + return serializeEncryptedKeyAEAD(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*symmetric.AEADPublicKey), keyBlock, config.AEAD()) case PubKeyAlgoMlkem768X25519, PubKeyAlgoMlkem1024X448: return serializeEncryptedKeyMlkem(w, config.Random(), buf[:lenHeaderWritten], pub.PublicKey.(*mlkem_ecdh.PublicKey), keyBlock, byte(cipherFunc), version) case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly, ExperimentalPubKeyAlgoHMAC: @@ -627,16 +640,20 @@ func serializeEncryptedKeyX448(w io.Writer, rand io.Reader, header []byte, pub * return x448.EncodeFields(w, ephemeralPublicX448, ciphertext, cipherFunc, version == 6) } -func serializeEncryptedKeyAEAD(w io.Writer, rand io.Reader, header []byte, pub *symmetric.AEADPublicKey, keyBlock []byte) error { - mode := pub.AEADMode - iv, ciphertext, err := pub.Encrypt(rand, keyBlock, mode) +func serializeEncryptedKeyAEAD(w io.Writer, rand io.Reader, header []byte, pub *symmetric.AEADPublicKey, keyBlock []byte, config *AEADConfig) error { + mode := algorithm.AEADMode(config.Mode()) + iv, ciphertextRaw, err := pub.Encrypt(rand, keyBlock, mode) if err != nil { return errors.InvalidArgumentError("AEAD encryption failed: " + err.Error()) } + ciphertextShortByteString := encoding.NewShortByteString(ciphertextRaw) + + buffer := append([]byte{byte(mode)}, iv...) + buffer = append(buffer, ciphertextShortByteString.EncodedBytes()...) + packetLen := len(header) /* header length */ - packetLen += int(len(iv)) - packetLen += int(len(ciphertext)) + packetLen += int(len(buffer)) err = serializeHeader(w, packetTypeEncryptedKey, packetLen) if err != nil { @@ -648,12 +665,7 @@ func serializeEncryptedKeyAEAD(w io.Writer, rand io.Reader, header []byte, pub * return err } - _, err = w.Write(iv[:]) - if err != nil { - return err - } - - _, err = w.Write(ciphertext) + _, err = w.Write(buffer) return err } diff --git a/openpgp/packet/encrypted_key_test.go b/openpgp/packet/encrypted_key_test.go index b52bb51fa..5ed0a8ed3 100644 --- a/openpgp/packet/encrypted_key_test.go +++ b/openpgp/packet/encrypted_key_test.go @@ -16,6 +16,7 @@ import ( "crypto" "crypto/rsa" + "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" "github.com/ProtonMail/go-crypto/openpgp/x25519" "github.com/ProtonMail/go-crypto/openpgp/x448" ) @@ -340,9 +341,11 @@ func TestSerializingEncryptedKey(t *testing.T) { } func TestSymmetricallyEncryptedKey(t *testing.T) { - const encryptedKeyHex = "c13d03999bd17d726446da80df9db940896a0b0e48f4d3b26e2dfbcf59ca7d30b65ea95ebb072e643407c732c479093b9d180c2eb51c98814e1bbbc6d0a17f" + const encryptedKeyHex = "c14f03999bd17d726446da64018cb4d628ae753c646b81f87f21269cd733df9db940896a0b0e48f4d3b26e2dfbcf59ca7d30b65ea95ebb072e643407c732c479093b9d180c2eb51c98814e1bbbc6d0a17f" - expectedIvAndCiphertext := []byte{0xdf, 0x9d, 0xb9, 0x40, 0x89, 0x6a, 0x0b, 0x0e, 0x48, 0xf4, 0xd3, 0xb2, 0x6e, 0x2d, 0xfb, 0xcf, 0x59, 0xca, 0x7d, 0x30, 0xb6, 0x5e, 0xa9, 0x5e, 0xbb, 0x07, 0x2e, 0x64, 0x34, 0x07, 0xc7, 0x32, 0xc4, 0x79, 0x09, 0x3b, 0x9d, 0x18, 0x0c, 0x2e, 0xb5, 0x1c, 0x98, 0x81, 0x4e, 0x1b, 0xbb, 0xc6, 0xd0, 0xa1, 0x7f} + expectedNonce := []byte{0x8c, 0xb4, 0xd6, 0x28, 0xae, 0x75, 0x3c, 0x64, 0x6b, 0x81, 0xf8, 0x7f, 0x21, 0x26, 0x9c, 0xd7} + + expectedCiphertext := []byte{0xdf, 0x9d, 0xb9, 0x40, 0x89, 0x6a, 0x0b, 0x0e, 0x48, 0xf4, 0xd3, 0xb2, 0x6e, 0x2d, 0xfb, 0xcf, 0x59, 0xca, 0x7d, 0x30, 0xb6, 0x5e, 0xa9, 0x5e, 0xbb, 0x07, 0x2e, 0x64, 0x34, 0x07, 0xc7, 0x32, 0xc4, 0x79, 0x09, 0x3b, 0x9d, 0x18, 0x0c, 0x2e, 0xb5, 0x1c, 0x98, 0x81, 0x4e, 0x1b, 0xbb, 0xc6, 0xd0, 0xa1, 0x7f} p, err := Read(readerFromHex(encryptedKeyHex)) if err != nil { @@ -354,7 +357,15 @@ func TestSymmetricallyEncryptedKey(t *testing.T) { t.Fatalf("didn't parse and EncryptedKey, got %#v", p) } - if !bytes.Equal(expectedIvAndCiphertext, ek.encryptedMPI1.Bytes()) { - t.Errorf("Parsed wrong ciphertext, got %x, expected %x", ek.encryptedMPI1.Bytes(), expectedIvAndCiphertext) + if ek.aeadMode != algorithm.AEADModeEAX { + t.Errorf("Parsed wrong aead mode, got %d, expected: 1", ek.aeadMode) + } + + if !bytes.Equal(expectedNonce, ek.nonce) { + t.Errorf("Parsed wrong nonce, got %x, expected %x", ek.nonce, expectedNonce) + } + + if !bytes.Equal(expectedCiphertext, ek.encryptedMPI1.Bytes()) { + t.Errorf("Parsed wrong ciphertext, got %x, expected %x", ek.encryptedMPI1.Bytes(), expectedCiphertext) } } diff --git a/openpgp/packet/packet.go b/openpgp/packet/packet.go index 7dfdb2a3e..f65efb2e5 100644 --- a/openpgp/packet/packet.go +++ b/openpgp/packet/packet.go @@ -507,8 +507,8 @@ const ( PubKeyAlgoEd25519 PublicKeyAlgorithm = 27 PubKeyAlgoEd448 PublicKeyAlgorithm = 28 - ExperimentalPubKeyAlgoAEAD PublicKeyAlgorithm = 128 - ExperimentalPubKeyAlgoHMAC PublicKeyAlgorithm = 129 + ExperimentalPubKeyAlgoAEAD PublicKeyAlgorithm = 100 + ExperimentalPubKeyAlgoHMAC PublicKeyAlgorithm = 101 // Deprecated in RFC 4880, Section 13.5. Use key flags instead. PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 diff --git a/openpgp/packet/private_key.go b/openpgp/packet/private_key.go index ba40066c7..aa0bfd61d 100644 --- a/openpgp/packet/private_key.go +++ b/openpgp/packet/private_key.go @@ -543,11 +543,19 @@ func serializeEd448PrivateKey(w io.Writer, priv *ed448.PrivateKey) error { } func serializeAEADPrivateKey(w io.Writer, priv *symmetric.AEADPrivateKey) (err error) { + _, err = w.Write(priv.HashSeed[:]) + if err != nil { + return + } _, err = w.Write(priv.Key) return } func serializeHMACPrivateKey(w io.Writer, priv *symmetric.HMACPrivateKey) (err error) { + _, err = w.Write(priv.HashSeed[:]) + if err != nil { + return + } _, err = w.Write(priv.Key) return err } @@ -1191,11 +1199,17 @@ func (pk *PrivateKey) parseAEADPrivateKey(data []byte) (err error) { aeadPriv := new(symmetric.AEADPrivateKey) aeadPriv.PublicKey = *pubKey + copy(aeadPriv.HashSeed[:], data[:32]) + priv := make([]byte, pubKey.Cipher.KeySize()) - copy(priv, data[:]) + copy(priv, data[32:]) aeadPriv.Key = priv aeadPriv.PublicKey.Key = aeadPriv.Key + if err = validateAEADParameters(aeadPriv); err != nil { + return + } + pk.PrivateKey = aeadPriv pk.PublicKey.PublicKey = &aeadPriv.PublicKey return @@ -1207,16 +1221,38 @@ func (pk *PrivateKey) parseHMACPrivateKey(data []byte) (err error) { hmacPriv := new(symmetric.HMACPrivateKey) hmacPriv.PublicKey = *pubKey + copy(hmacPriv.HashSeed[:], data[:32]) + priv := make([]byte, pubKey.Hash.Size()) - copy(priv, data[:]) - hmacPriv.Key = priv[:] + copy(priv, data[32:]) + hmacPriv.Key = data[32:] hmacPriv.PublicKey.Key = hmacPriv.Key + if err = validateHMACParameters(hmacPriv); err != nil { + return + } + pk.PrivateKey = hmacPriv pk.PublicKey.PublicKey = &hmacPriv.PublicKey return } +func validateAEADParameters(priv *symmetric.AEADPrivateKey) error { + return validateCommonSymmetric(priv.HashSeed, priv.PublicKey.BindingHash) +} + +func validateHMACParameters(priv *symmetric.HMACPrivateKey) error { + return validateCommonSymmetric(priv.HashSeed, priv.PublicKey.BindingHash) +} + +func validateCommonSymmetric(seed [32]byte, bindingHash [32]byte) error { + expectedBindingHash := symmetric.ComputeBindingHash(seed) + if !bytes.Equal(expectedBindingHash, bindingHash[:]) { + return errors.KeyInvalidError("symmetric: wrong binding hash") + } + return nil +} + // parseMldsaEddsaPrivateKey parses a ML-DSA + EdDSA private key as specified in // https://www.ietf.org/archive/id/draft-ietf-openpgp-pqc-05.html#name-key-material-packets-2 func (pk *PrivateKey) parseMldsaEddsaPrivateKey(data []byte, ecLen, seedLen int) (err error) { diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index 794cebec3..b96469f07 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -748,22 +748,21 @@ func (pk *PublicKey) parseEd448(r io.Reader) (err error) { } func (pk *PublicKey) parseAEAD(r io.Reader) (err error) { - var algOctets [2]byte - _, err = readFull(r, algOctets[:]) + var cipher [1]byte + _, err = readFull(r, cipher[:]) if err != nil { return } - var fpSeed [32]byte - _, err = readFull(r, fpSeed[:]) + var bindingHash [32]byte + _, err = readFull(r, bindingHash[:]) if err != nil { return } symmetric := &symmetric.AEADPublicKey{ - Cipher: algorithm.CipherFunction(algOctets[0]), - AEADMode: algorithm.AEADMode(algOctets[1]), - FpSeed: fpSeed, + Cipher: algorithm.CipherFunction(cipher[0]), + BindingHash: bindingHash, } pk.PublicKey = symmetric @@ -776,8 +775,7 @@ func (pk *PublicKey) parseHMAC(r io.Reader) (err error) { if err != nil { return } - var fpSeed [32]byte - _, err = readFull(r, fpSeed[:]) + bindingHash, err := readBindingHash(r) if err != nil { return } @@ -788,8 +786,8 @@ func (pk *PublicKey) parseHMAC(r io.Reader) (err error) { } symmetric := &symmetric.HMACPublicKey{ - Hash: hmacHash, - FpSeed: fpSeed, + Hash: hmacHash, + BindingHash: bindingHash, } pk.PublicKey = symmetric @@ -922,12 +920,9 @@ func (pk *PublicKey) algorithmSpecificByteCount() uint32 { length += ed25519.PublicKeySize case PubKeyAlgoEd448: length += ed448.PublicKeySize - case ExperimentalPubKeyAlgoAEAD: - length += 2 // Symmetric and AEAD algorithm octets - length += 32 // Fingerprint seed - case ExperimentalPubKeyAlgoHMAC: + case ExperimentalPubKeyAlgoAEAD, ExperimentalPubKeyAlgoHMAC: length += 1 // Hash octet - length += 32 // Fingerprint seed + length += 32 // Binding hash case PubKeyAlgoMlkem768X25519, PubKeyAlgoMlkem1024X448, PubKeyAlgoMldsa65Ed25519, PubKeyAlgoMldsa87Ed448: length += uint32(pk.p.EncodedLength()) @@ -1026,11 +1021,11 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { return case ExperimentalPubKeyAlgoAEAD: symmKey := pk.PublicKey.(*symmetric.AEADPublicKey) - algOctets := [2]byte{symmKey.Cipher.Id(), symmKey.AEADMode.Id()} - if _, err = w.Write(algOctets[:]); err != nil { + cipherOctet := [1]byte{symmKey.Cipher.Id()} + if _, err = w.Write(cipherOctet[:]); err != nil { return } - _, err = w.Write(symmKey.FpSeed[:]) + _, err = w.Write(symmKey.BindingHash[:]) return case ExperimentalPubKeyAlgoHMAC: symmKey := pk.PublicKey.(*symmetric.HMACPublicKey) @@ -1038,7 +1033,7 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err error) { if _, err = w.Write(hashOctet[:]); err != nil { return } - _, err = w.Write(symmKey.FpSeed[:]) + _, err = w.Write(symmKey.BindingHash[:]) return case PubKeyAlgoMlkem768X25519, PubKeyAlgoMlkem1024X448, PubKeyAlgoMldsa65Ed25519, PubKeyAlgoMldsa87Ed448: @@ -1385,7 +1380,7 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) { case PubKeyAlgoEd448: bitLength = ed448.PublicKeySize * 8 case ExperimentalPubKeyAlgoAEAD: - bitLength = uint16(pk.PublicKey.(*symmetric.AEADPublicKey).Cipher.KeySize()) * 8 + bitLength = 32 case PubKeyAlgoMlkem768X25519, PubKeyAlgoMlkem1024X448, PubKeyAlgoMldsa65Ed25519, PubKeyAlgoMldsa87Ed448: bitLength = pk.q.BitLength() // TODO: Discuss if this makes sense. diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index fe36cc4f9..6c1ae22dc 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -346,11 +346,10 @@ func (sig *Signature) parse(r io.Reader) (err error) { return } case ExperimentalPubKeyAlgoHMAC: - hmac, err := io.ReadAll(r) - if err != nil { - return err + sig.HMAC = new(encoding.ShortByteString) + if _, err = sig.HMAC.ReadFrom(r); err != nil { + return } - sig.HMAC = encoding.NewOctetArray(hmac) case PubKeyAlgoMldsa65Ed25519: if err = sig.parseMldsaEddsaSignature(r, 64, mldsa65.SignatureSize); err != nil { return @@ -1038,7 +1037,7 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e case ExperimentalPubKeyAlgoHMAC: sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil) if err == nil { - sig.HMAC = encoding.NewOctetArray(sigdata) + sig.HMAC = encoding.NewShortByteString(sigdata) } case PubKeyAlgoMldsa65Ed25519, PubKeyAlgoMldsa87Ed448: if sig.Version != 6 { diff --git a/openpgp/packet/signature_test.go b/openpgp/packet/signature_test.go index c2598280b..19940387a 100644 --- a/openpgp/packet/signature_test.go +++ b/openpgp/packet/signature_test.go @@ -83,7 +83,7 @@ ltm2aQaG } func TestSymmetricSignatureRead(t *testing.T) { - const serializedPacket = "c271040181080006050260639e4e002109107fc6eeae2d3315b1162104e29ad49f0b7d0b12bb0401407fc6eeae2d3315b13adc0ecca603da8e6f3c82727ffc3e9416bc0236c9665498dda14f1c1dd4e4acacc7725d6dac7598e0951b5f1f8789714fb7fcdda4a9f10056134a7edf9d9a4fc45d" + const serializedPacket = "c272040165080006050260639e4e002109107fc6eeae2d3315b1162104e29ad49f0b7d0b12bb0401407fc6eeae2d3315b13adc400ecca603da8e6f3c82727ffc3e9416bc0236c9665498dda14f1c1dd4e4acacc7725d6dac7598e0951b5f1f8789714fb7fcdda4a9f10056134a7edf9d9a4fc45d" expectedHMAC := []byte{0x0e, 0xcc, 0xa6, 0x03, 0xda, 0x8e, 0x6f, 0x3c, 0x82, 0x72, 0x7f, 0xfc, 0x3e, 0x94, 0x16, 0xbc, 0x02, 0x36, 0xc9, 0x66, 0x54, 0x98, 0xdd, 0xa1, 0x4f, 0x1c, 0x1d, 0xd4, 0xe4, 0xac, 0xac, 0xc7, 0x72, 0x5d, 0x6d, 0xac, 0x75, 0x98, 0xe0, 0x95, 0x1b, 0x5f, 0x1f, 0x87, 0x89, 0x71, 0x4f, 0xb7, 0xfc, 0xdd, 0xa4, 0xa9, 0xf1, 0x00, 0x56, 0x13, 0x4a, 0x7e, 0xdf, 0x9d, 0x9a, 0x4f, 0xc4, 0x5d} packet, err := Read(readerFromHex(serializedPacket)) diff --git a/openpgp/read_write_test_data.go b/openpgp/read_write_test_data.go index 5c08f6bc1..f8ef7fabc 100644 --- a/openpgp/read_write_test_data.go +++ b/openpgp/read_write_test_data.go @@ -459,27 +459,18 @@ byVJHvLO/XErtC+GNIJeMg== // A key that contains a persistent AEAD subkey const keyWithAEADSubkey = `-----BEGIN PGP PRIVATE KEY BLOCK----- -xVgEZypNfBYJKwYBBAHaRw8BAQdAag5k2wQ5kNPa/BAhUuAucrG8o9p71riM -34x8NwQ9G1wAAP0cmDSK7NLI2LzyIQtLpAANHoAyLxkObT2N6SK9gTt6NQ4z -zRd0ZXN0IDx0ZXN0QGV4YW1wbGUub3JnPsLAEwQTFgoAhQWCZypNfAMLCQcJ -kH3vtREeAXvNRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5v -cmdIXNnr8sRWIc56Ttw5TvcBQ4kBZDf7DwQPQQRchEoCwQUVCggMDgQWAAIB -AhkBApsDAh4BFiEELiytxINFTJSqscZgfe+1ER4Be80AAA8kAQCURpNRDBuK -HMHUUhyfs4ba3KXWZ8tu5Doqx8HXCHuovQEAj8pO//gt8PZlt6P0tVqZItsg -dkjH67KM5PdtlvSMrgfHXQRnKk18EgorBgEEAZdVAQUBAQdAVUVOljcQeIuG -6S2DyrqbO73UtqOK4kOXt5c238AOygwDAQgHAAD/VUjA1uCSGVb4tlz4h0PS -ewITrKGqO87MCd3ZUyM8VyAQ9cK+BBgWCgBwBYJnKk18CZB977URHgF7zUUU -AAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3Jn+hW1SjRxZh+F -Kpe+KXLtk9QJp/2ly/EbTv43hLi+/FsCmwwWIQQuLK3Eg0VMlKqxxmB977UR -HgF7zQAA33IA/RcTNF+3EBI273gWHy/tsSLJ1r05hJ7/DEN+KvIe7bNvAP4j -dGqPDRabcstbF+MmunFJoDSiuikYN1rdskDZ52+rAMdLBGcqTaiACQP6GAck -iE9MdrWMpykKn4MNfe5+3HQ+PvkLKSxhRwNZGwDHOv2+yJJNTcbgeC7Z/POf -PyOum0vrd35zd5LteFyRXhJlwr4EGBYKAHAFgmcqTagJkH3vtREeAXvNRRQA -AAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdaLY3r2qR/IS3L -7Wa0Vewc1s90cf0OUpy3AVGPOKKGYQKbDBYhBC4srcSDRUyUqrHGYH3vtREe -AXvNAAAgcgD+IwOjsj+BB+qlIL/XEaccgIhT27NDKnBWtOGmyDZufwIA/idj -089k5VoCQMVWHQVDk8oumkxweFLNjkev5LeEm7QI -=2WdX +xVgEYs/4KxYJKwYBBAHaRw8BAQdA7tIsntXluwloh/H62PJMqasjP00M86fv +/Pof9A968q8AAQDYcgkPKUdWAxsDjDHJfouPS4q5Me3ks+umlo5RJdwLZw4k +zQ1TeW1tZXRyaWMgS2V5wowEEBYKAB0FAmLP+CsECwkHCAMVCAoEFgACAQIZ +AQIbAwIeAQAhCRDkNhFDvaU8vxYhBDJNoyEFquVOCf99d+Q2EUO9pTy/5XQA +/1F2YPouv0ydBDJU3EOS/4bmPt7yqvzciWzeKVEOkzYuAP9OsP7q/5ccqOPX +mmRUKwd82/cNjdzdnWZ8Tq89XMwMAMdqBGLP+CtkCfFyZxOMF0BWLwAE8pLy +RVj2n2K7k6VvrhyuTqDkFDUFALiSLrEfnmTKlsPYS3/YzsODF354ccR63q73 +3lmCrvFRyaf6AHvVrBYPbJR+VhuTjZTwZKvPPKv0zVdSqi5JDEQiocJ4BBgW +CAAJBQJiz/grAhsMACEJEOQ2EUO9pTy/FiEEMk2jIQWq5U4J/3135DYRQ72l +PL+fEQEA7RaRbfa+AtiRN7a4GuqVEDZi3qtQZ2/Qcb27/LkAD0sA/3r9drYv +jyu46h1fdHHyo0HS2MiShZDZ8u60JnDltloD +=8TxH -----END PGP PRIVATE KEY BLOCK----- ` diff --git a/openpgp/symmetric/aead.go b/openpgp/symmetric/aead.go index e13137e28..b9d389dc6 100644 --- a/openpgp/symmetric/aead.go +++ b/openpgp/symmetric/aead.go @@ -7,55 +7,53 @@ import ( type AEADPublicKey struct { Cipher algorithm.CipherFunction - AEADMode algorithm.AEADMode - FpSeed [32]byte - // While this is a "public" key, the symmetric key needs to be present here. - // Symmetric cryptographic operations use the same key material for - // signing and verifying, and go-crypto assumes that a public key type will - // be used for encryption. Thus, this `Key` field must never be exported - // publicly. + BindingHash [32]byte Key []byte } type AEADPrivateKey struct { PublicKey AEADPublicKey + HashSeed [32]byte Key []byte } -func AEADGenerateKey(rand io.Reader, cipher algorithm.CipherFunction, aead algorithm.AEADMode) (priv *AEADPrivateKey, err error) { +func AEADGenerateKey(rand io.Reader, cipher algorithm.CipherFunction) (priv *AEADPrivateKey, err error) { priv, err = generatePrivatePartAEAD(rand, cipher) if err != nil { return } - priv.generatePublicPartAEAD(rand, cipher, aead) + priv.generatePublicPartAEAD(cipher) return } func generatePrivatePartAEAD(rand io.Reader, cipher algorithm.CipherFunction) (priv *AEADPrivateKey, err error) { priv = new(AEADPrivateKey) + var seed [32]byte + _, err = rand.Read(seed[:]) + if err != nil { + return + } + key := make([]byte, cipher.KeySize()) _, err = rand.Read(key) if err != nil { return } + + priv.HashSeed = seed priv.Key = key return } -func (priv *AEADPrivateKey) generatePublicPartAEAD(rand io.Reader, cipher algorithm.CipherFunction, aead algorithm.AEADMode) (err error) { +func (priv *AEADPrivateKey) generatePublicPartAEAD(cipher algorithm.CipherFunction) (err error) { priv.PublicKey.Cipher = cipher - priv.PublicKey.AEADMode = aead - var seed [32]byte - _, err = rand.Read(seed[:]) - if err != nil { - return - } + bindingHash := ComputeBindingHash(priv.HashSeed) priv.PublicKey.Key = make([]byte, len(priv.Key)) copy(priv.PublicKey.Key, priv.Key) - copy(priv.PublicKey.FpSeed[:], seed[:]) + copy(priv.PublicKey.BindingHash[:], bindingHash) return } @@ -68,12 +66,10 @@ func (pub *AEADPublicKey) Encrypt(rand io.Reader, data []byte, mode algorithm.AE return } -func (priv *AEADPrivateKey) Decrypt(ivAndCiphertext []byte, mode algorithm.AEADMode) (message []byte, err error) { - nonceLength := mode.NonceLength() - iv := ivAndCiphertext[:nonceLength] - ciphertext := ivAndCiphertext[nonceLength:] +func (priv *AEADPrivateKey) Decrypt(nonce []byte, ciphertext []byte, mode algorithm.AEADMode) (message []byte, err error) { + block := priv.PublicKey.Cipher.New(priv.Key) aead := mode.New(block) - message, err = aead.Open(nil, iv, ciphertext, nil) + message, err = aead.Open(nil, nonce, ciphertext, nil) return } diff --git a/openpgp/symmetric/hmac.go b/openpgp/symmetric/hmac.go index 50755f8ec..e9d61475c 100644 --- a/openpgp/symmetric/hmac.go +++ b/openpgp/symmetric/hmac.go @@ -3,6 +3,7 @@ package symmetric import ( "crypto" "crypto/hmac" + "crypto/sha256" "io" "github.com/ProtonMail/go-crypto/openpgp/errors" @@ -10,8 +11,8 @@ import ( ) type HMACPublicKey struct { - Hash algorithm.Hash - FpSeed [32]byte + Hash algorithm.Hash + BindingHash [32]byte // While this is a "public" key, the symmetric key needs to be present here. // Symmetric cryptographic operations use the same key material for // signing and verifying, and go-crypto assumes that a public key type will @@ -22,6 +23,7 @@ type HMACPublicKey struct { type HMACPrivateKey struct { PublicKey HMACPublicKey + HashSeed [32]byte Key []byte } @@ -31,12 +33,17 @@ func HMACGenerateKey(rand io.Reader, hash algorithm.Hash) (priv *HMACPrivateKey, return } - priv.generatePublicPartHMAC(rand, hash) + priv.generatePublicPartHMAC(hash) return } func generatePrivatePartHMAC(rand io.Reader, hash algorithm.Hash) (priv *HMACPrivateKey, err error) { priv = new(HMACPrivateKey) + var seed [32]byte + _, err = rand.Read(seed[:]) + if err != nil { + return + } key := make([]byte, hash.Size()) _, err = rand.Read(key) @@ -44,25 +51,29 @@ func generatePrivatePartHMAC(rand io.Reader, hash algorithm.Hash) (priv *HMACPri return } + priv.HashSeed = seed priv.Key = key return } -func (priv *HMACPrivateKey) generatePublicPartHMAC(rand io.Reader, hash algorithm.Hash) (err error) { +func (priv *HMACPrivateKey) generatePublicPartHMAC(hash algorithm.Hash) (err error) { priv.PublicKey.Hash = hash - var seed [32]byte - _, err = rand.Read(seed[:]) - if err != nil { - return - } - copy(priv.PublicKey.FpSeed[:], seed[:]) + bindingHash := ComputeBindingHash(priv.HashSeed) + copy(priv.PublicKey.BindingHash[:], bindingHash) priv.PublicKey.Key = make([]byte, len(priv.Key)) copy(priv.PublicKey.Key, priv.Key) return } +func ComputeBindingHash(seed [32]byte) []byte { + bindingHash := sha256.New() + bindingHash.Write(seed[:]) + + return bindingHash.Sum(nil) +} + func (priv *HMACPrivateKey) Public() crypto.PublicKey { return &priv.PublicKey } diff --git a/openpgp/v2/key_generation.go b/openpgp/v2/key_generation.go index 4716d2b8d..84d328c22 100644 --- a/openpgp/v2/key_generation.go +++ b/openpgp/v2/key_generation.go @@ -465,8 +465,7 @@ func newDecrypter(config *packet.Config) (decrypter interface{}, err error) { return x448.GenerateKey(config.Random()) case packet.ExperimentalPubKeyAlgoHMAC, packet.ExperimentalPubKeyAlgoAEAD: // When passing HMAC, we generate an AEAD subkey cipher := algorithm.CipherFunction(config.Cipher()) - aead := algorithm.AEADMode(config.AEAD().Mode()) - return symmetric.AEADGenerateKey(config.Random(), cipher, aead) + return symmetric.AEADGenerateKey(config.Random(), cipher) case packet.PubKeyAlgoMldsa65Ed25519, packet.PubKeyAlgoMldsa87Ed448: if pubKeyAlgo, err = packet.GetMatchingMlkem(config.PublicKeyAlgorithm()); err != nil { return nil, err diff --git a/openpgp/v2/keys_test.go b/openpgp/v2/keys_test.go index 35c30f279..c9d277340 100644 --- a/openpgp/v2/keys_test.go +++ b/openpgp/v2/keys_test.go @@ -2068,13 +2068,16 @@ func TestAddHMACSubkey(t *testing.T) { t.Error("generated Public and Private Key differ") } - if !bytes.Equal(parsedPublicKey.FpSeed[:], generatedPublicKey.FpSeed[:]) { + if !bytes.Equal(parsedPrivateKey.HashSeed[:], generatedPrivateKey.HashSeed[:]) { t.Error("parsed wrong hash seed") } if parsedPrivateKey.PublicKey.Hash != generatedPrivateKey.PublicKey.Hash { t.Error("parsed wrong cipher id") } + if !bytes.Equal(parsedPrivateKey.PublicKey.BindingHash[:], generatedPrivateKey.PublicKey.BindingHash[:]) { + t.Error("parsed wrong binding hash") + } } func TestSerializeSymmetricSubkeyError(t *testing.T) { @@ -2086,13 +2089,13 @@ func TestSerializeSymmetricSubkeyError(t *testing.T) { buf := bytes.NewBuffer(nil) w, _ := armor.Encode(buf, "PGP PRIVATE KEY BLOCK", nil) - entity.PrimaryKey.PubKeyAlgo = 128 + entity.PrimaryKey.PubKeyAlgo = 100 err = entity.Serialize(w) if err == nil { t.Fatal(err) } - entity.PrimaryKey.PubKeyAlgo = 129 + entity.PrimaryKey.PubKeyAlgo = 101 err = entity.Serialize(w) if err == nil { t.Fatal(err) @@ -2143,15 +2146,15 @@ func TestAddAEADSubkey(t *testing.T) { t.Error("generated Public and Private Key differ") } - if !bytes.Equal(parsedPublicKey.FpSeed[:], generatedPublicKey.FpSeed[:]) { + if !bytes.Equal(parsedPrivateKey.HashSeed[:], generatedPrivateKey.HashSeed[:]) { t.Error("parsed wrong hash seed") } if parsedPrivateKey.PublicKey.Cipher.Id() != generatedPrivateKey.PublicKey.Cipher.Id() { t.Error("parsed wrong cipher id") } - if parsedPrivateKey.PublicKey.AEADMode.Id() != generatedPrivateKey.PublicKey.AEADMode.Id() { - t.Error("parsed wrong aead mode") + if !bytes.Equal(parsedPrivateKey.PublicKey.BindingHash[:], generatedPrivateKey.PublicKey.BindingHash[:]) { + t.Error("parsed wrong binding hash") } } @@ -2195,11 +2198,11 @@ func TestNoSymmetricKeySerialized(t *testing.T) { t.Error("Private key was serialized with public") } - firstFpSeed := entity.Subkeys[1].PublicKey.PublicKey.(*symmetric.AEADPublicKey).FpSeed - i = bytes.Index(w.Bytes(), firstFpSeed[:]) + firstBindingHash := entity.Subkeys[1].PublicKey.PublicKey.(*symmetric.AEADPublicKey).BindingHash + i = bytes.Index(w.Bytes(), firstBindingHash[:]) - secondFpSeed := entity.Subkeys[2].PublicKey.PublicKey.(*symmetric.HMACPublicKey).FpSeed - k = bytes.Index(w.Bytes(), secondFpSeed[:]) + secondBindingHash := entity.Subkeys[2].PublicKey.PublicKey.(*symmetric.HMACPublicKey).BindingHash + k = bytes.Index(w.Bytes(), secondBindingHash[:]) if (i > 0) || (k > 0) { t.Errorf("Symmetric public key metadata exported %d %d", i, k) } @@ -2208,20 +2211,18 @@ func TestNoSymmetricKeySerialized(t *testing.T) { func TestSymmetricKeys(t *testing.T) { data := `-----BEGIN PGP PRIVATE KEY BLOCK----- - -xUoEZyoQrIEImuGs5gaOTekO00WQx6MDnyBPvxmpMiOgeVse7+aqarsAc8F5 -NFm3pVkFDZxX0MqRCPqCwsa/BXJGlrEdMAwSNckOV80xUGVyc2lzdGVudCBT -eW1tZXRyaWMgS2V5IDxwZXJzaXN0ZW50QGV4YW1wbGUub3JnPsKvBBOBCgCF -BYJnKhCsAwsJBwmQDqlD7wlMH9dFFAAAAAAAHAAgc2FsdEBub3RhdGlvbnMu -b3BlbnBncGpzLm9yZ4pMjYSZvCHJsWo5/hQJ3qfDMVMnetCsdS4ZSR6oeO7l -BRUKCAwOBBYAAgECGQECmwMCHgEWIQSbMhUPoVGIuE9u9GAOqUPvCUwf1wAA -QXxcTdhWEMhv+uYj8lUjGbDiqMHc7oGQSattlK89H9KT18dLBGcqEKyACQPs -AUFGawprheOyMQEYmVQUCoTdw4SVAxPk3Wkdbd7YtQATgtwB+JTCDy4de8F+ -yKpsXCJEFrVCsVnFyyY3gH5Wgw5PwpoEGIEKAHAFgmcqEKwJkA6pQ+8JTB/X -RRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdwNnP67WFb -3vwFQkTQHsuFKLqvtvpQdnDs9RmvPxLZUwKbDBYhBJsyFQ+hUYi4T270YA6p -Q+8JTB/XAAC0o7OPSjaqMfpfYDUewr7Ehi5kFRCDBwbxLWFryAiICULT -=ywfD + +xWoEYs7w5mUIcFvlmkuricX26x138uvHGlwIaxWIbRnx1+ggPcveTcwA4zSZ +n6XcD0Q5aLe6dTEBwCyfUecZ/nA0W8Pl9xBHfjIjQuxcUBnIqxZ061RZPjef +D/XIQga1ftLDelhylQwL7R3TzQ1TeW1tZXRyaWMgS2V5wmkEEGUIAB0FAmLO +8OYECwkHCAMVCAoEFgACAQIZAQIbAwIeAQAhCRCRTKq2ObiQKxYhBMHTTXXF +ULQ2M2bYNJFMqrY5uJArIawgJ+5RSsN8VNuZTKJbG88TIedU05wwKjW3wqvT +X6Z7yfbHagRizvDmZAluL/kJo6hZ1kFENpQkWD/Kfv1vAG3nbxhsVEzBQ6a1 +OAD24BaKJz6gWgj4lASUNK5OuXnLc3J79Bt1iRGkSbiPzRs/bplB4TwbILeC +ZLeDy9kngZDosgsIk5sBgGEqS9y5HiHCVQQYZQgACQUCYs7w5gIbDAAhCRCR +TKq2ObiQKxYhBMHTTXXFULQ2M2bYNJFMqrY5uJArENkgL0Bc+OI/1na0XWqB +TxGVotQ4A/0u0VbOMEUfnrI8Fms= +=RdCW -----END PGP PRIVATE KEY BLOCK----- ` keys, err := ReadArmoredKeyRing(strings.NewReader(data)) diff --git a/openpgp/v2/read_write_test_data.go b/openpgp/v2/read_write_test_data.go index 5779ed2aa..5a9bad487 100644 --- a/openpgp/v2/read_write_test_data.go +++ b/openpgp/v2/read_write_test_data.go @@ -744,27 +744,18 @@ xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE= // A key that contains a persistent AEAD subkey const keyWithAEADSubkey = `-----BEGIN PGP PRIVATE KEY BLOCK----- -xVgEZypNfBYJKwYBBAHaRw8BAQdAag5k2wQ5kNPa/BAhUuAucrG8o9p71riM -34x8NwQ9G1wAAP0cmDSK7NLI2LzyIQtLpAANHoAyLxkObT2N6SK9gTt6NQ4z -zRd0ZXN0IDx0ZXN0QGV4YW1wbGUub3JnPsLAEwQTFgoAhQWCZypNfAMLCQcJ -kH3vtREeAXvNRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5v -cmdIXNnr8sRWIc56Ttw5TvcBQ4kBZDf7DwQPQQRchEoCwQUVCggMDgQWAAIB -AhkBApsDAh4BFiEELiytxINFTJSqscZgfe+1ER4Be80AAA8kAQCURpNRDBuK -HMHUUhyfs4ba3KXWZ8tu5Doqx8HXCHuovQEAj8pO//gt8PZlt6P0tVqZItsg -dkjH67KM5PdtlvSMrgfHXQRnKk18EgorBgEEAZdVAQUBAQdAVUVOljcQeIuG -6S2DyrqbO73UtqOK4kOXt5c238AOygwDAQgHAAD/VUjA1uCSGVb4tlz4h0PS -ewITrKGqO87MCd3ZUyM8VyAQ9cK+BBgWCgBwBYJnKk18CZB977URHgF7zUUU -AAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3Jn+hW1SjRxZh+F -Kpe+KXLtk9QJp/2ly/EbTv43hLi+/FsCmwwWIQQuLK3Eg0VMlKqxxmB977UR -HgF7zQAA33IA/RcTNF+3EBI273gWHy/tsSLJ1r05hJ7/DEN+KvIe7bNvAP4j -dGqPDRabcstbF+MmunFJoDSiuikYN1rdskDZ52+rAMdLBGcqTaiACQP6GAck -iE9MdrWMpykKn4MNfe5+3HQ+PvkLKSxhRwNZGwDHOv2+yJJNTcbgeC7Z/POf -PyOum0vrd35zd5LteFyRXhJlwr4EGBYKAHAFgmcqTagJkH3vtREeAXvNRRQA -AAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmdaLY3r2qR/IS3L -7Wa0Vewc1s90cf0OUpy3AVGPOKKGYQKbDBYhBC4srcSDRUyUqrHGYH3vtREe -AXvNAAAgcgD+IwOjsj+BB+qlIL/XEaccgIhT27NDKnBWtOGmyDZufwIA/idj -089k5VoCQMVWHQVDk8oumkxweFLNjkev5LeEm7QI -=2WdX +xVgEYs/4KxYJKwYBBAHaRw8BAQdA7tIsntXluwloh/H62PJMqasjP00M86fv +/Pof9A968q8AAQDYcgkPKUdWAxsDjDHJfouPS4q5Me3ks+umlo5RJdwLZw4k +zQ1TeW1tZXRyaWMgS2V5wowEEBYKAB0FAmLP+CsECwkHCAMVCAoEFgACAQIZ +AQIbAwIeAQAhCRDkNhFDvaU8vxYhBDJNoyEFquVOCf99d+Q2EUO9pTy/5XQA +/1F2YPouv0ydBDJU3EOS/4bmPt7yqvzciWzeKVEOkzYuAP9OsP7q/5ccqOPX +mmRUKwd82/cNjdzdnWZ8Tq89XMwMAMdqBGLP+CtkCfFyZxOMF0BWLwAE8pLy +RVj2n2K7k6VvrhyuTqDkFDUFALiSLrEfnmTKlsPYS3/YzsODF354ccR63q73 +3lmCrvFRyaf6AHvVrBYPbJR+VhuTjZTwZKvPPKv0zVdSqi5JDEQiocJ4BBgW +CAAJBQJiz/grAhsMACEJEOQ2EUO9pTy/FiEEMk2jIQWq5U4J/3135DYRQ72l +PL+fEQEA7RaRbfa+AtiRN7a4GuqVEDZi3qtQZ2/Qcb27/LkAD0sA/3r9drYv +jyu46h1fdHHyo0HS2MiShZDZ8u60JnDltloD +=8TxH -----END PGP PRIVATE KEY BLOCK----- `