Skip to content

Commit 8524931

Browse files
rolandshoemakergopherbot
authored andcommitted
crypto/x509: reject serial numbers longer than 20 octets
Updates #65085 Change-Id: I8e5fb6c77c54f07247b30afea9fe8c548bf6d0be Reviewed-on: https://go-review.googlesource.com/c/go/+/562975 Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]>
1 parent db13584 commit 8524931

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

doc/godebug.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ Go 1.23 changed the behavior of
188188
serial numbers that are negative. This change can be reverted with
189189
the the [`x509negativeserial` setting](/pkg/crypto/x509/#ParseCertificate).
190190

191+
Go 1.23 changed the behavior of
192+
[crypto/x509.ParseCertificate](/pkg/crypto/x509/#ParseCertificate) to reject
193+
serial numbers that are longer than 20 octets. This change can be reverted with
194+
the the [`x509seriallength` setting](/pkg/crypto/x509/#ParseCertificate).
195+
191196
### Go 1.22
192197

193198
Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size

src/crypto/x509/parser.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ func processExtensions(out *Certificate) error {
817817
}
818818

819819
var x509negativeserial = godebug.New("x509negativeserial")
820+
var x509seriallength = godebug.New("x509seriallength")
820821

821822
func parseCertificate(der []byte) (*Certificate, error) {
822823
cert := &Certificate{}
@@ -857,10 +858,27 @@ func parseCertificate(der []byte) (*Certificate, error) {
857858
return nil, errors.New("x509: invalid version")
858859
}
859860

861+
var serialBytes cryptobyte.String
862+
if !tbs.ReadASN1Element(&serialBytes, cryptobyte_asn1.INTEGER) {
863+
return nil, errors.New("x509: malformed serial number")
864+
}
865+
// We add two bytes for the tag and length (if the length was multi-byte,
866+
// which is possible, the length of the serial would be more than 256 bytes,
867+
// so this condition would trigger anyway).
868+
if len(serialBytes) > 20+2 {
869+
if x509seriallength.Value() != "1" {
870+
return nil, errors.New("x509: serial number too long (>20 octets)")
871+
} else {
872+
x509seriallength.IncNonDefault()
873+
}
874+
}
860875
serial := new(big.Int)
861-
if !tbs.ReadASN1Integer(serial) {
876+
if !serialBytes.ReadASN1Integer(serial) {
862877
return nil, errors.New("x509: malformed serial number")
863878
}
879+
// We do not reject zero serials, because they are unfortunately common
880+
// in important root certificates which will not expire for a number of
881+
// years.
864882
if serial.Sign() == -1 {
865883
if x509negativeserial.Value() != "1" {
866884
return nil, errors.New("x509: negative serial number")
@@ -1008,6 +1026,10 @@ func parseCertificate(der []byte) (*Certificate, error) {
10081026
// Before Go 1.23, ParseCertificate accepted certificates with negative serial
10091027
// numbers. This behavior can be restored by including "x509negativeserial=1" in
10101028
// the GODEBUG environment variable.
1029+
//
1030+
// Before Go 1.23, ParseCertificate accepted certificates with serial numbers
1031+
// longer than 20 octets. This behavior can be restored by including
1032+
// "x509seriallength=1" in the GODEBUG environment variable.
10111033
func ParseCertificate(der []byte) (*Certificate, error) {
10121034
cert, err := parseCertificate(der)
10131035
if err != nil {

src/crypto/x509/x509_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4086,3 +4086,26 @@ func TestRejectCriticalSKI(t *testing.T) {
40864086
t.Fatalf("ParseCertificate() unexpected error: %v, want: %s", err, expectedErr)
40874087
}
40884088
}
4089+
4090+
func TestSerialTooLong(t *testing.T) {
4091+
template := Certificate{
4092+
Subject: pkix.Name{CommonName: "Cert"},
4093+
NotBefore: time.Unix(1000, 0),
4094+
NotAfter: time.Unix(100000, 0),
4095+
}
4096+
for _, serial := range []*big.Int{
4097+
big.NewInt(0).SetBytes(bytes.Repeat([]byte{5}, 21)),
4098+
big.NewInt(0).SetBytes(bytes.Repeat([]byte{255}, 20)),
4099+
} {
4100+
template.SerialNumber = serial
4101+
certDER, err := CreateCertificate(rand.Reader, &template, &template, rsaPrivateKey.Public(), rsaPrivateKey)
4102+
if err != nil {
4103+
t.Fatalf("CreateCertificate() unexpected error: %v", err)
4104+
}
4105+
expectedErr := "x509: serial number too long (>20 octets)"
4106+
_, err = ParseCertificate(certDER)
4107+
if err == nil || err.Error() != expectedErr {
4108+
t.Fatalf("ParseCertificate() unexpected error: %v, want: %s", err, expectedErr)
4109+
}
4110+
}
4111+
}

src/internal/godebugs/table.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ var All = []Info{
5454
{Name: "winreadlinkvolume", Package: "os", Changed: 22, Old: "0"},
5555
{Name: "winsymlink", Package: "os", Changed: 22, Old: "0"},
5656
{Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"},
57+
{Name: "x509seriallength", Package: "crypto/x509", Changed: 23, Old: "1"},
5758
{Name: "x509sha1", Package: "crypto/x509"},
5859
{Name: "x509usefallbackroots", Package: "crypto/x509"},
5960
{Name: "x509usepolicies", Package: "crypto/x509"},

src/runtime/metrics/doc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ Below is the full list of supported metrics, ordered lexicographically.
327327
package due to a non-default GODEBUG=x509negativeserial=...
328328
setting.
329329
330+
/godebug/non-default-behavior/x509seriallength:events
331+
The number of non-default behaviors executed by the crypto/x509
332+
package due to a non-default GODEBUG=x509seriallength=...
333+
setting.
334+
330335
/godebug/non-default-behavior/x509sha1:events
331336
The number of non-default behaviors executed by the crypto/x509
332337
package due to a non-default GODEBUG=x509sha1=... setting.

0 commit comments

Comments
 (0)