From b4956d363a8b14623cc81db401707679605930a3 Mon Sep 17 00:00:00 2001 From: Mike Tsao Date: Tue, 16 Jan 2018 22:40:05 -0800 Subject: [PATCH] openpgp: serialize correct bitlength for generated ECC keys Rather than rounding the `type || X || Y` byte sequence to the next 8-bit boundary, packet.NewECDSAPublicKey() now rounds the X and Y coordinates individually, then adds the bitlength 3 of type 4 (compressed). For NIST P-256, this leads to a bit length of 515, rather than 520. GnuPG calculates 515 as well, and https://tools.ietf.org/html/rfc6637#section-6 explicitly states that "the exact size of the MPI payload is 515 bits for 'Curve P-256,'" so the new formula is consistent. Fixes golang/go#23460 Change-Id: I64b340d1c761bfd795a1a64dc2f7a831c8b2ff32 Reviewed-on: https://go-review.googlesource.com/87995 Reviewed-by: Adam Langley Reviewed-by: Filippo Valsorda Run-TryBot: Adam Langley TryBot-Result: Gobot Gobot --- openpgp/packet/public_key.go | 7 ++++++- openpgp/packet/public_key_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index ead26233..fbd60f69 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -244,7 +244,12 @@ func NewECDSAPublicKey(creationTime time.Time, pub *ecdsa.PublicKey) *PublicKey } pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) - pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes)) + + // The bit length is 3 (for the 0x04 specifying an uncompressed key) + // plus two field elements (for x and y), which are rounded up to the + // nearest byte. See https://tools.ietf.org/html/rfc6637#section-6 + fieldBytes := (pub.Curve.Params().BitSize + 7) & ^7 + pk.ec.p.bitLength = uint16(3 + fieldBytes + fieldBytes) pk.setFingerPrintAndKeyId() return pk diff --git a/openpgp/packet/public_key_test.go b/openpgp/packet/public_key_test.go index 7ad7d918..103696ee 100644 --- a/openpgp/packet/public_key_test.go +++ b/openpgp/packet/public_key_test.go @@ -6,7 +6,10 @@ package packet import ( "bytes" + "crypto/ecdsa" + "crypto/elliptic" "encoding/hex" + "math/big" "testing" "time" ) @@ -186,6 +189,29 @@ func TestEcc384Serialize(t *testing.T) { } } +func TestP256KeyID(t *testing.T) { + // Confirm that key IDs are correctly calculated for ECC keys. + ecdsaPub := &ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: fromHex("81fbbc20eea9e8d1c3ceabb0a8185925b113d1ac42cd5c78403bd83da19235c6"), + Y: fromHex("5ed6db13d91db34507d0129bf88981878d29adbf8fcd1720afdb767bb3fcaaff"), + } + pub := NewECDSAPublicKey(time.Unix(1297309478, 0), ecdsaPub) + + const want = uint64(0xd01055fbcadd268e) + if pub.KeyId != want { + t.Errorf("want key ID: %x, got %x", want, pub.KeyId) + } +} + +func fromHex(hex string) *big.Int { + n, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("bad hex number: " + hex) + } + return n +} + const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001"