]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/x509: write exact BitLength in ASN.1 encoding for certificate KeyUsage
authorAdam Langley <agl@golang.org>
Mon, 12 Jan 2015 21:58:30 +0000 (13:58 -0800)
committerAdam Langley <agl@golang.org>
Thu, 15 Jan 2015 01:58:54 +0000 (01:58 +0000)
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
src/crypto/x509/x509_test.go

index 3fa5b3d7571c29dbdaf6a939b222272f9b6ac3fb..fd5b8da1cf975b66c076c31d47720b788d5e524c 100644 (file)
@@ -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
                }
index 4f5173fb5dd27bbcb8589207be04dc1479d2217c..f275375ba76e6ff7b5b6148f22090daceb954697 100644 (file)
@@ -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
 //