Skip to content

Commit 2c47667

Browse files
FiloSottilegopherbot
authored andcommitted
cryptobyte: add support for ReadASN1Integer into []byte
This lets us extract large integers without involving math/big. While at it, drop some use of reflect where a type switch will do. Change-Id: Iebe2fb2267610bf95cf9747ba1d49b5ac9e62cda Reviewed-on: https://go-review.googlesource.com/c/crypto/+/451515 Run-TryBot: Filippo Valsorda <[email protected]> Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]>
1 parent 0ec7e83 commit 2c47667

File tree

2 files changed

+66
-33
lines changed

2 files changed

+66
-33
lines changed

cryptobyte/asn1.go

+40-33
Original file line numberDiff line numberDiff line change
@@ -264,36 +264,35 @@ func (s *String) ReadASN1Boolean(out *bool) bool {
264264
return true
265265
}
266266

267-
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
268-
269267
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
270-
// not point to an integer or to a big.Int, it panics. It reports whether the
271-
// read was successful.
268+
// not point to an integer, to a big.Int, or to a []byte it panics. Only
269+
// positive and zero values can be decoded into []byte, and they are returned as
270+
// big-endian binary values that share memory with s. Positive values will have
271+
// no leading zeroes, and zero will be returned as a single zero byte.
272+
// ReadASN1Integer reports whether the read was successful.
272273
func (s *String) ReadASN1Integer(out interface{}) bool {
273-
if reflect.TypeOf(out).Kind() != reflect.Ptr {
274-
panic("out is not a pointer")
275-
}
276-
switch reflect.ValueOf(out).Elem().Kind() {
277-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
274+
switch out := out.(type) {
275+
case *int, *int8, *int16, *int32, *int64:
278276
var i int64
279277
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
280278
return false
281279
}
282280
reflect.ValueOf(out).Elem().SetInt(i)
283281
return true
284-
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
282+
case *uint, *uint8, *uint16, *uint32, *uint64:
285283
var u uint64
286284
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
287285
return false
288286
}
289287
reflect.ValueOf(out).Elem().SetUint(u)
290288
return true
291-
case reflect.Struct:
292-
if reflect.TypeOf(out).Elem() == bigIntType {
293-
return s.readASN1BigInt(out.(*big.Int))
294-
}
289+
case *big.Int:
290+
return s.readASN1BigInt(out)
291+
case *[]byte:
292+
return s.readASN1Bytes(out)
293+
default:
294+
panic("out does not point to an integer type")
295295
}
296-
panic("out does not point to an integer type")
297296
}
298297

299298
func checkASN1Integer(bytes []byte) bool {
@@ -333,6 +332,21 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
333332
return true
334333
}
335334

335+
func (s *String) readASN1Bytes(out *[]byte) bool {
336+
var bytes String
337+
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
338+
return false
339+
}
340+
if bytes[0]&0x80 == 0x80 {
341+
return false
342+
}
343+
for len(bytes) > 1 && bytes[0] == 0 {
344+
bytes = bytes[1:]
345+
}
346+
*out = bytes
347+
return true
348+
}
349+
336350
func (s *String) readASN1Int64(out *int64) bool {
337351
var bytes String
338352
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
@@ -654,34 +668,27 @@ func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
654668
return s.ReadASN1(&unused, tag)
655669
}
656670

657-
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER
658-
// explicitly tagged with tag into out and advances. If no element with a
659-
// matching tag is present, it writes defaultValue into out instead. If out
660-
// does not point to an integer or to a big.Int, it panics. It reports
661-
// whether the read was successful.
671+
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER explicitly
672+
// tagged with tag into out and advances. If no element with a matching tag is
673+
// present, it writes defaultValue into out instead. Otherwise, it behaves like
674+
// ReadASN1Integer.
662675
func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
663-
if reflect.TypeOf(out).Kind() != reflect.Ptr {
664-
panic("out is not a pointer")
665-
}
666676
var present bool
667677
var i String
668678
if !s.ReadOptionalASN1(&i, &present, tag) {
669679
return false
670680
}
671681
if !present {
672-
switch reflect.ValueOf(out).Elem().Kind() {
673-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
674-
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
682+
switch out.(type) {
683+
case *int, *int8, *int16, *int32, *int64,
684+
*uint, *uint8, *uint16, *uint32, *uint64, *[]byte:
675685
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
676-
case reflect.Struct:
677-
if reflect.TypeOf(out).Elem() != bigIntType {
678-
panic("invalid integer type")
679-
}
680-
if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
681-
reflect.TypeOf(defaultValue).Elem() != bigIntType {
686+
case *big.Int:
687+
if defaultValue, ok := defaultValue.(*big.Int); ok {
688+
out.(*big.Int).Set(defaultValue)
689+
} else {
682690
panic("out points to big.Int, but defaultValue does not")
683691
}
684-
out.(*big.Int).Set(defaultValue.(*big.Int))
685692
default:
686693
panic("invalid integer type")
687694
}

cryptobyte/asn1_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,32 @@ func TestReadASN1IntegerSigned(t *testing.T) {
154154
}
155155
})
156156

157+
// Repeat the same cases, reading into a []byte.
158+
t.Run("bytes", func(t *testing.T) {
159+
for i, test := range testData64 {
160+
in := String(test.in)
161+
var out []byte
162+
ok := in.ReadASN1Integer(&out)
163+
if test.out < 0 {
164+
if ok {
165+
t.Errorf("#%d: in.ReadASN1Integer(%d) = %v, want false", i, test.out, ok)
166+
}
167+
continue
168+
}
169+
if !ok {
170+
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true", i, ok)
171+
continue
172+
}
173+
n := new(big.Int).SetBytes(out).Int64()
174+
if n != test.out {
175+
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %x, want %d", i, ok, out, test.out)
176+
}
177+
if out[0] == 0 && len(out) > 1 {
178+
t.Errorf("#%d: in.ReadASN1Integer() = %v; out = %x, has leading zeroes", i, ok, out)
179+
}
180+
}
181+
})
182+
157183
// Repeat with the implicit-tagging functions
158184
t.Run("WithTag", func(t *testing.T) {
159185
for i, test := range testData64 {

0 commit comments

Comments
 (0)