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 <agl@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Adam Langley <agl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go
index ead2623..fbd60f6 100644
--- a/openpgp/packet/public_key.go
+++ b/openpgp/packet/public_key.go
@@ -244,7 +244,12 @@
 	}
 
 	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 7ad7d91..103696e 100644
--- a/openpgp/packet/public_key_test.go
+++ b/openpgp/packet/public_key_test.go
@@ -6,7 +6,10 @@
 
 import (
 	"bytes"
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"encoding/hex"
+	"math/big"
 	"testing"
 	"time"
 )
@@ -186,6 +189,29 @@
 	}
 }
 
+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"