From 2fe8eadc9aed6e6637312ef93c4939f6344a48a0 Mon Sep 17 00:00:00 2001 From: Adam Langley <agl@golang.org> Date: Mon, 12 Jan 2015 13:58:30 -0800 Subject: [PATCH] crypto/x509: write exact BitLength in ASN.1 encoding for certificate KeyUsage The encoded value of the certificate KeyUsage did contain additonal padding that was not present with other certificate generators. According to ITU-T X.690 the BitLength value should have no padding in a DER encoding. See discussion: https://groups.google.com/forum/#!topic/golang-nuts/dzaJ3hMpDcs This CL has been discussed at: http://golang.org/cl/168990043 Change-Id: I1eff3f441b0566966a2d279631901ad9287c917d Reviewed-on: https://go-review.googlesource.com/2255 Reviewed-by: Adam Langley <agl@golang.org> --- src/crypto/x509/x509.go | 23 ++++++++++++++++++++++- src/crypto/x509/x509_test.go | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go index 3fa5b3d757..fd5b8da1cf 100644 --- a/src/crypto/x509/x509.go +++ b/src/crypto/x509/x509.go @@ -1135,6 +1135,26 @@ func reverseBitsInAByte(in byte) byte { return b3 } +// asn1BitLength returns the bit-length of bitString by considering the +// most-significant bit in a byte to be the "first" bit. This convention +// matches ASN.1, but differs from almost everything else. +func asn1BitLength(bitString []byte) int { + bitLen := len(bitString) * 8 + + for i := range bitString { + b := bitString[len(bitString)-i-1] + + for bit := uint(0); bit < 8; bit++ { + if (b>>bit)&1 == 1 { + return bitLen + } + bitLen-- + } + } + + return 0 +} + var ( oidExtensionSubjectKeyId = []int{2, 5, 29, 14} oidExtensionKeyUsage = []int{2, 5, 29, 15} @@ -1203,7 +1223,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { l = 2 } - ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: a[0:l], BitLength: l * 8}) + bitString := a[:l] + ret[n].Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)}) if err != nil { return } diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 4f5173fb5d..f275375ba7 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -1016,6 +1016,28 @@ func TestMaxPathLen(t *testing.T) { } } +func TestASN1BitLength(t *testing.T) { + tests := []struct { + bytes []byte + bitLen int + }{ + {nil, 0}, + {[]byte{0x00}, 0}, + {[]byte{0x00, 0x00}, 0}, + {[]byte{0xf0}, 4}, + {[]byte{0x88}, 5}, + {[]byte{0xff}, 8}, + {[]byte{0xff, 0x80}, 9}, + {[]byte{0xff, 0x81}, 16}, + } + + for i, test := range tests { + if got := asn1BitLength(test.bytes); got != test.bitLen { + t.Errorf("#%d: calculated bit-length of %d for %x, wanted %d", i, got, test.bytes, test.bitLen) + } + } +} + // This CSR was generated with OpenSSL: // openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key -config openssl.cnf // -- 2.30.9