Skip to content

Commit 32be728

Browse files
FiloSottilekatiehockman
authored andcommitted
cryptobyte: fix panic due to malformed ASN.1 inputs on 32-bit archs
When int is 32 bits wide (on 32-bit architectures like 386 and arm), an overflow could occur, causing a panic, due to malformed ASN.1 being passed to any of the ASN1 methods of String. Tested on linux/386 and darwin/amd64. This fixes CVE-2020-7919 and was found thanks to the Project Wycheproof test vectors. Change-Id: I8c9696a8bfad1b40ec877cd740dba3467d66ab54 Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/645211 Reviewed-by: Katie Hockman <[email protected]> Reviewed-by: Adam Langley <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/crypto/+/216677 Run-TryBot: Katie Hockman <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent ed25d70 commit 32be728

File tree

4 files changed

+8
-12
lines changed

4 files changed

+8
-12
lines changed

cryptobyte/asn1.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,8 @@ func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
470470
// It reports whether the read was successful.
471471
func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
472472
var bytes String
473-
if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
473+
if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 ||
474+
len(bytes)*8/8 != len(bytes) {
474475
return false
475476
}
476477

@@ -740,7 +741,7 @@ func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
740741
length = headerLen + len32
741742
}
742743

743-
if uint32(int(length)) != length || !s.ReadBytes((*[]byte)(out), int(length)) {
744+
if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) {
744745
return false
745746
}
746747
if skipHeader && !out.Skip(int(headerLen)) {

cryptobyte/asn1_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ var readASN1TestData = []readASN1Test{
3131
{"non-minimal length", append([]byte{0x30, 0x82, 0, 0x80}, make([]byte, 0x80)...), 0x30, false, nil},
3232
{"invalid tag", []byte{0xa1, 3, 0x4, 1, 1}, 31, false, nil},
3333
{"high tag", []byte{0x1f, 0x81, 0x80, 0x01, 2, 1, 2}, 0xff /* actually 0x4001, but tag is uint8 */, false, nil},
34+
{"2**31 - 1 length", []byte{0x30, 0x84, 0x7f, 0xff, 0xff, 0xff}, 0x30, false, nil},
35+
{"2**32 - 1 length", []byte{0x30, 0x84, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil},
36+
{"2**63 - 1 length", []byte{0x30, 0x88, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil},
37+
{"2**64 - 1 length", []byte{0x30, 0x88, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0x30, false, nil},
3438
}
3539

3640
func TestReadASN1(t *testing.T) {

cryptobyte/string.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type String []byte
2424
// read advances a String by n bytes and returns them. If less than n bytes
2525
// remain, it returns nil.
2626
func (s *String) read(n int) []byte {
27-
if len(*s) < n {
27+
if len(*s) < n || n < 0 {
2828
return nil
2929
}
3030
v := (*s)[:n]
@@ -105,11 +105,6 @@ func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool {
105105
length = length << 8
106106
length = length | uint32(b)
107107
}
108-
if int(length) < 0 {
109-
// This currently cannot overflow because we read uint24 at most, but check
110-
// anyway in case that changes in the future.
111-
return false
112-
}
113108
v := s.read(int(length))
114109
if v == nil {
115110
return false

internal/wycheproof/wycheproof_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"os"
1818
"os/exec"
1919
"path/filepath"
20-
"runtime"
2120
"testing"
2221

2322
_ "crypto/sha1"
@@ -34,9 +33,6 @@ func TestMain(m *testing.M) {
3433
log.Printf("skipping test because 'go' command is unavailable: %v", err)
3534
os.Exit(0)
3635
}
37-
if runtime.GOARCH == "386" || runtime.GOARCH == "arm" {
38-
os.Exit(0) // skip tests
39-
}
4036

4137
// Download the JSON test files from github.com/google/wycheproof
4238
// using `go mod download -json` so the cached source of the testdata

0 commit comments

Comments
 (0)