VERSION | 2 +- doc/devel/release.html | 7 +++++++ src/crypto/x509/root_windows.go | 20 ++++++++++++++++++-- src/internal/x/crypto/cryptobyte/asn1.go | 5 +++-- src/internal/x/crypto/cryptobyte/asn1_test.go | 4 ++++ src/internal/x/crypto/cryptobyte/string.go | 7 +------ diff --git a/VERSION b/VERSION index b115ecfa6a9fb8c36e28e3def6ddb7ea4f9c8ab3..33bccfc8ee1c7f1da3cbf8d009fbbcf79f6e5322 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -go1.12.15 \ No newline at end of file +go1.12.16 \ No newline at end of file diff --git a/doc/devel/release.html b/doc/devel/release.html index 487367c35f8bd941235634b7a8e8b850b334ff2f..38a9040d5e120d2cd59e93bac679598d7ead21ba 100644 --- a/doc/devel/release.html +++ b/doc/devel/release.html @@ -139,6 +139,13 @@ the Go 1.12.15 milestone on our issue tracker for details.
+
+go1.12.16 (released 2020/01/28) includes two security fixes to
+the crypto/x509 package. See the
+Go
+1.12.16 milestone on our issue tracker for details.
+
diff --git a/src/crypto/x509/root_windows.go b/src/crypto/x509/root_windows.go index 74d395df707dfff6d56fe0d484f08e4e4033f682..3da3d06e739df53642b36c1768d911fb7a0b8b29 100644 --- a/src/crypto/x509/root_windows.go +++ b/src/crypto/x509/root_windows.go @@ -219,10 +219,26 @@ chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount)) if err != nil { return nil, err } + if len(chain) < 1 { + return nil, errors.New("x509: internal error: system verifier returned an empty chain") + } - chains = append(chains, chain) + // Mitigate CVE-2020-0601, where the Windows system verifier might be + // tricked into using custom curve parameters for a trusted root, by + // double-checking all ECDSA signatures. If the system was tricked into + // using spoofed parameters, the signature will be invalid for the correct + // ones we parsed. (We don't support custom curves ourselves.) + for i, parent := range chain[1:] { + if parent.PublicKeyAlgorithm != ECDSA { + continue + } + if err := parent.CheckSignature(chain[i].SignatureAlgorithm, + chain[i].RawTBSCertificate, chain[i].Signature); err != nil { + return nil, err + } + } - return chains, nil + return [][]*Certificate{chain}, nil } func loadSystemRoots() (*CertPool, error) { diff --git a/src/internal/x/crypto/cryptobyte/asn1.go b/src/internal/x/crypto/cryptobyte/asn1.go index 2d40680ddd3b7f0e3f0885275fb4afee898dbfab..758ac3a649699a57ebe69c4bbbe278b9eb6770e5 100644 --- a/src/internal/x/crypto/cryptobyte/asn1.go +++ b/src/internal/x/crypto/cryptobyte/asn1.go @@ -470,7 +470,8 @@ // ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. // It reports whether the read was successful. func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { var bytes String - if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 { + if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 || + len(bytes)*8/8 != len(bytes) { return false } @@ -740,7 +741,7 @@ } length = headerLen + len32 } - if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) { + if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) { return false } if skipHeader && !out.Skip(int(headerLen)) { diff --git a/src/internal/x/crypto/cryptobyte/asn1_test.go b/src/internal/x/crypto/cryptobyte/asn1_test.go index ca28e3bcfb2565b3c67461265964fe809e88853e..f90d768edb1bea376698e0cb49ecfe4dff659093 100644 --- a/src/internal/x/crypto/cryptobyte/asn1_test.go +++ b/src/internal/x/crypto/cryptobyte/asn1_test.go @@ -31,6 +31,10 @@ {"invalid long form length", []byte{0x30, 0x81, 1, 1}, 0x30, false, nil}, {"non-minimal length", append([]byte{0x30, 0x82, 0, 0x80}, make([]byte, 0x80)...), 0x30, false, nil}, {"invalid tag", []byte{0xa1, 3, 0x4, 1, 1}, 31, false, nil}, {"high tag", []byte{0x1f, 0x81, 0x80, 0x01, 2, 1, 2}, 0xff /* actually 0x4001, but tag is uint8 */, false, nil}, + {"2**31 - 1 length", []byte{0x30, 0x84, 0x7f, 0xff, 0xff, 0xff}, 0x30, false, nil}, + {"2**32 - 1 length", []byte{0x30, 0x84, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil}, + {"2**63 - 1 length", []byte{0x30, 0x88, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil}, + {"2**64 - 1 length", []byte{0x30, 0x88, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil}, } func TestReadASN1(t *testing.T) { diff --git a/src/internal/x/crypto/cryptobyte/string.go b/src/internal/x/crypto/cryptobyte/string.go index bd2ed2e207c6e0c9f898063ba5351aa2ae2c26f4..a3ecf638282fd466e0f514d5626f05dabfc450d6 100644 --- a/src/internal/x/crypto/cryptobyte/string.go +++ b/src/internal/x/crypto/cryptobyte/string.go @@ -24,7 +24,7 @@ // read advances a String by n bytes and returns them. If less than n bytes // remain, it returns nil. func (s *String) read(n int) []byte { - if len(*s) < n { + if len(*s) < n || n < 0 { return nil } v := (*s)[:n] @@ -104,11 +104,6 @@ var length uint32 for _, b := range lenBytes { length = length << 8 length = length | uint32(b) - } - if int(length) < 0 { - // This currently cannot overflow because we read uint24 at most, but check - // anyway in case that changes in the future. - return false } v := s.read(int(length)) if v == nil {