]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/asn1: support Unmarshaling NumericString
authorMansour Rahimi <rahimi.mnr@gmail.com>
Sat, 18 Nov 2017 21:00:16 +0000 (22:00 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Sat, 25 Nov 2017 17:08:32 +0000 (17:08 +0000)
ASN.1 has an specific string type, called NumericString (tag 18). The
value of this type can be numeric characters (0-9) and space.

Fixes #22396

Change-Id: Ia6d81ab7faa311ff22759bf76862626974d3013e
Reviewed-on: https://go-review.googlesource.com/78655
Run-TryBot: Adam Langley <agl@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/encoding/asn1/asn1.go
src/encoding/asn1/asn1_test.go
src/encoding/asn1/common.go
src/encoding/asn1/marshal.go
src/encoding/asn1/marshal_test.go

index fb03b06abab40466a45ce2b6c31c80af8c9b74f8..4459ce4ed6142bad9c50613973c501d15b882629 100644 (file)
@@ -372,6 +372,25 @@ func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
        return
 }
 
+// NumericString
+
+// parseNumericString parses an ASN.1 NumericString from the given byte array
+// and returns it.
+func parseNumericString(bytes []byte) (ret string, err error) {
+       for _, b := range bytes {
+               if !isNumeric(b) {
+                       return "", SyntaxError{"NumericString contains invalid character"}
+               }
+       }
+       return string(bytes), nil
+}
+
+// isNumeric reports whether the given b is in the ASN.1 NumericString set.
+func isNumeric(b byte) bool {
+       return '0' <= b && b <= '9' ||
+               b == ' '
+}
+
 // PrintableString
 
 // parsePrintableString parses an ASN.1 PrintableString from the given byte
@@ -561,7 +580,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
                        return
                }
                switch t.tag {
-               case TagIA5String, TagGeneralString, TagT61String, TagUTF8String:
+               case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
                        // We pretend that various other string types are
                        // PRINTABLE STRINGs so that a sequence of them can be
                        // parsed into a []string.
@@ -643,6 +662,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                        switch t.tag {
                        case TagPrintableString:
                                result, err = parsePrintableString(innerBytes)
+                       case TagNumericString:
+                               result, err = parseNumericString(innerBytes)
                        case TagIA5String:
                                result, err = parseIA5String(innerBytes)
                        case TagT61String:
@@ -729,7 +750,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
        if universalTag == TagPrintableString {
                if t.class == ClassUniversal {
                        switch t.tag {
-                       case TagIA5String, TagGeneralString, TagT61String, TagUTF8String:
+                       case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
                                universalTag = t.tag
                        }
                } else if params.stringType != 0 {
@@ -907,6 +928,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                switch universalTag {
                case TagPrintableString:
                        v, err = parsePrintableString(innerBytes)
+               case TagNumericString:
+                       v, err = parseNumericString(innerBytes)
                case TagIA5String:
                        v, err = parseIA5String(innerBytes)
                case TagT61String:
@@ -980,7 +1003,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 //
 // An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
 //
-// An ASN.1 PrintableString or IA5String can be written to a string.
+// An ASN.1 PrintableString, IA5String, or NumericString can be written to a string.
 //
 // Any of the above ASN.1 values can be written to an interface{}.
 // The value stored in the interface has the corresponding Go type.
index 7ff9c05cc0c82a60e75875ab142134e0f3628535..56129530f5ccb466909b0846c7af2921121407cc 100644 (file)
@@ -424,6 +424,7 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
        {"generalized", fieldParameters{timeType: TagGeneralizedTime}},
        {"utc", fieldParameters{timeType: TagUTCTime}},
        {"printable", fieldParameters{stringType: TagPrintableString}},
+       {"numeric", fieldParameters{stringType: TagNumericString}},
        {"optional", fieldParameters{optional: true}},
        {"explicit", fieldParameters{explicit: true, tag: new(int)}},
        {"application", fieldParameters{application: true, tag: new(int)}},
@@ -496,6 +497,7 @@ var unmarshalTestData = []struct {
        {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
        {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
        {[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
+       {[]byte{0x12, 0x0b, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '}, newString("0123456789 ")},
 }
 
 func TestUnmarshal(t *testing.T) {
index 3e4dfd16795609b32543dac22a176a5000ce3421..a6589a521af4610f810c663c1aa5cfef7b73cf04 100644 (file)
@@ -30,6 +30,7 @@ const (
        TagUTF8String      = 12
        TagSequence        = 16
        TagSet             = 17
+       TagNumericString   = 18
        TagPrintableString = 19
        TagT61String       = 20
        TagIA5String       = 22
@@ -106,6 +107,8 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        ret.stringType = TagIA5String
                case part == "printable":
                        ret.stringType = TagPrintableString
+               case part == "numeric":
+                       ret.stringType = TagNumericString
                case part == "utf8":
                        ret.stringType = TagUTF8String
                case strings.HasPrefix(part, "default:"):
index 3f46e03d35fdec9880cd3eb0233e9c58be834c78..422614f65708f8ea1d006acd33b7f695befb7d24 100644 (file)
@@ -289,6 +289,16 @@ func makeIA5String(s string) (e encoder, err error) {
        return stringEncoder(s), nil
 }
 
+func makeNumericString(s string) (e encoder, err error) {
+       for i := 0; i < len(s); i++ {
+               if !isNumeric(s[i]) {
+                       return nil, StructuralError{"NumericString contains invalid character"}
+               }
+       }
+
+       return stringEncoder(s), nil
+}
+
 func makeUTF8String(s string) encoder {
        return stringEncoder(s)
 }
@@ -506,6 +516,8 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
                        return makeIA5String(v.String())
                case TagPrintableString:
                        return makePrintableString(v.String())
+               case TagNumericString:
+                       return makeNumericString(v.String())
                default:
                        return makeUTF8String(v.String()), nil
                }
index 75adc303b0732e17bcf26df50bc237e2917ac5c4..ac3d31ff9ee8f31bba1c7f8638cf4e9bf810e6ad 100644 (file)
@@ -80,6 +80,10 @@ type applicationTest struct {
        B int `asn1:"application,tag:1,explicit"`
 }
 
+type numericStringTest struct {
+       A string `asn1:"numeric"`
+}
+
 type testSET []int
 
 var PST = time.FixedZone("PST", -8*60*60)
@@ -164,6 +168,7 @@ var marshalTests = []marshalTest{
        {defaultTest{1}, "3000"},
        {defaultTest{2}, "3003020102"},
        {applicationTest{1, 2}, "30084001016103020102"},
+       {numericStringTest{"1 9"}, "30051203312039"},
 }
 
 func TestMarshal(t *testing.T) {
@@ -212,6 +217,9 @@ type marshalErrTest struct {
 
 var marshalErrTests = []marshalErrTest{
        {bigIntStruct{nil}, "empty integer"},
+       {numericStringTest{"a"}, "invalid character"},
+       {ia5StringTest{"\xb0"}, "invalid character"},
+       {printableStringTest{"!"}, "invalid character"},
 }
 
 func TestMarshalError(t *testing.T) {