]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/asn1: respect “explicit” and “tag” when unmarshaling RawValues.
authorAdam Langley <agl@golang.org>
Thu, 15 Dec 2016 23:02:03 +0000 (15:02 -0800)
committerAdam Langley <agl@golang.org>
Sat, 30 Sep 2017 17:01:26 +0000 (17:01 +0000)
Previously, any “explicit” and/or “tag” decorations on a RawValue would
be ignored when unmarshaling. The RawValue would swallow whatever
element was encountered.

This change causes these decorations to be respected. Thus a field like:
  Foo asn1.RawValue `asn1:"explicit,tag:1,optional"`
will only match if an explicit tag with value one is encountered.
Otherwise the RawValue will get the default value and parsing will move
onto the next element.

Thanks to Martin Kreichgauer for reporting the issue.

Change-Id: If6c4488685b9bd039cb5e352d6d75744f98dbb1f
Reviewed-on: https://go-review.googlesource.com/34503
Run-TryBot: Adam Langley <agl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
src/encoding/asn1/asn1.go
src/encoding/asn1/asn1_test.go
src/encoding/asn1/common.go
src/encoding/asn1/marshal.go

index 73193c34076bf2f7f96e2b1babe49c2a225fee4b..1a018389386f8b44b8aae16687ec475cefdb7d18 100644 (file)
@@ -536,7 +536,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
 // a number of ASN.1 values from the given byte slice and returns them as a
 // slice of Go values of the given type.
 func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) {
-       expectedTag, compoundType, ok := getUniversalType(elemType)
+       matchAny, expectedTag, compoundType, ok := getUniversalType(elemType)
        if !ok {
                err = StructuralError{"unknown Go type for slice"}
                return
@@ -562,7 +562,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
                        t.tag = TagUTCTime
                }
 
-               if t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag {
+               if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) {
                        err = StructuralError{"sequence tag mismatch"}
                        return
                }
@@ -617,23 +617,6 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                return
        }
 
-       // Deal with raw values.
-       if fieldType == rawValueType {
-               var t tagAndLength
-               t, offset, err = parseTagAndLength(bytes, offset)
-               if err != nil {
-                       return
-               }
-               if invalidLength(offset, t.length, len(bytes)) {
-                       err = SyntaxError{"data truncated"}
-                       return
-               }
-               result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]}
-               offset += t.length
-               v.Set(reflect.ValueOf(result))
-               return
-       }
-
        // Deal with the ANY type.
        if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
                var t tagAndLength
@@ -682,11 +665,6 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                }
                return
        }
-       universalTag, compoundType, ok1 := getUniversalType(fieldType)
-       if !ok1 {
-               err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
-               return
-       }
 
        t, offset, err := parseTagAndLength(bytes, offset)
        if err != nil {
@@ -702,7 +680,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                        return
                }
                if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
-                       if t.length > 0 {
+                       if fieldType == rawValueType {
+                               // The inner element should not be parsed for RawValues.
+                       } else if t.length > 0 {
                                t, offset, err = parseTagAndLength(bytes, offset)
                                if err != nil {
                                        return
@@ -727,6 +707,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                }
        }
 
+       matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType)
+       if !ok1 {
+               err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
+               return
+       }
+
        // Special case for strings: all the ASN.1 string types map to the Go
        // type string. getUniversalType returns the tag for PrintableString
        // when it sees a string, so if we see a different string type on the
@@ -752,21 +738,25 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                universalTag = TagSet
        }
 
+       matchAnyClassAndTag := matchAny
        expectedClass := ClassUniversal
        expectedTag := universalTag
 
        if !params.explicit && params.tag != nil {
                expectedClass = ClassContextSpecific
                expectedTag = *params.tag
+               matchAnyClassAndTag = false
        }
 
        if !params.explicit && params.application && params.tag != nil {
                expectedClass = ClassApplication
                expectedTag = *params.tag
+               matchAnyClassAndTag = false
        }
 
        // We have unwrapped any explicit tagging at this point.
-       if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
+       if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) ||
+               (!matchAny && t.isCompound != compoundType) {
                // Tags don't match. Again, it could be an optional element.
                ok := setDefaultValue(v, params)
                if ok {
@@ -785,6 +775,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
 
        // We deal with the structures defined in this package first.
        switch fieldType {
+       case rawValueType:
+               result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]}
+               v.Set(reflect.ValueOf(result))
+               return
        case objectIdentifierType:
                newSlice, err1 := parseObjectIdentifier(innerBytes)
                v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
index 355ff8c41ca4afcd5089f10a594810acd061b6b5..7ff9c05cc0c82a60e75875ab142134e0f3628535 100644 (file)
@@ -1033,3 +1033,60 @@ func TestNull(t *testing.T) {
                t.Errorf("Expected Unmarshal of NullBytes to yield %v, got %v", NullRawValue, unmarshaled)
        }
 }
+
+func TestExplicitTagRawValueStruct(t *testing.T) {
+       type foo struct {
+               A RawValue `asn1:"optional,explicit,tag:5"`
+               B []byte   `asn1:"optional,explicit,tag:6"`
+       }
+       before := foo{B: []byte{1, 2, 3}}
+       derBytes, err := Marshal(before)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var after foo
+       if rest, err := Unmarshal(derBytes, &after); err != nil || len(rest) != 0 {
+               t.Fatal(err)
+       }
+
+       got := fmt.Sprintf("%#v", after)
+       want := fmt.Sprintf("%#v", before)
+       if got != want {
+               t.Errorf("got %s, want %s (DER: %x)", got, want, derBytes)
+       }
+}
+
+func TestTaggedRawValue(t *testing.T) {
+       type taggedRawValue struct {
+               A RawValue `asn1:"tag:5"`
+       }
+       type untaggedRawValue struct {
+               A RawValue
+       }
+       const isCompound = 0x20
+       const tag = 5
+
+       tests := []struct {
+               shouldMatch bool
+               derBytes    []byte
+       }{
+               {false, []byte{0x30, 3, TagInteger, 1, 1}},
+               {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag, 1, 1}},
+               {true, []byte{0x30, 3, (ClassContextSpecific << 6) | tag | isCompound, 1, 1}},
+               {false, []byte{0x30, 3, (ClassApplication << 6) | tag | isCompound, 1, 1}},
+       }
+
+       for i, test := range tests {
+               var tagged taggedRawValue
+               if _, err := Unmarshal(test.derBytes, &tagged); (err == nil) != test.shouldMatch {
+                       t.Errorf("#%d: unexpected result parsing %x: %s", i, test.derBytes, err)
+               }
+
+               // An untagged RawValue should accept anything.
+               var untagged untaggedRawValue
+               if _, err := Unmarshal(test.derBytes, &untagged); err != nil {
+                       t.Errorf("#%d: unexpected failure parsing %x with untagged RawValue: %s", i, test.derBytes, err)
+               }
+       }
+}
index cd93b27ecb85a617568d825345a64f60fa09c86f..3e4dfd16795609b32543dac22a176a5000ce3421 100644 (file)
@@ -136,36 +136,38 @@ func parseFieldParameters(str string) (ret fieldParameters) {
 
 // Given a reflected Go type, getUniversalType returns the default tag number
 // and expected compound flag.
-func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
+func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) {
        switch t {
+       case rawValueType:
+               return true, -1, false, true
        case objectIdentifierType:
-               return TagOID, false, true
+               return false, TagOID, false, true
        case bitStringType:
-               return TagBitString, false, true
+               return false, TagBitString, false, true
        case timeType:
-               return TagUTCTime, false, true
+               return false, TagUTCTime, false, true
        case enumeratedType:
-               return TagEnum, false, true
+               return false, TagEnum, false, true
        case bigIntType:
-               return TagInteger, false, true
+               return false, TagInteger, false, true
        }
        switch t.Kind() {
        case reflect.Bool:
-               return TagBoolean, false, true
+               return false, TagBoolean, false, true
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-               return TagInteger, false, true
+               return false, TagInteger, false, true
        case reflect.Struct:
-               return TagSequence, true, true
+               return false, TagSequence, true, true
        case reflect.Slice:
                if t.Elem().Kind() == reflect.Uint8 {
-                       return TagOctetString, false, true
+                       return false, TagOctetString, false, true
                }
                if strings.HasSuffix(t.Name(), "SET") {
-                       return TagSet, true, true
+                       return false, TagSet, true, true
                }
-               return TagSequence, true, true
+               return false, TagSequence, true, true
        case reflect.String:
-               return TagPrintableString, false, true
+               return false, TagPrintableString, false, true
        }
-       return 0, false, false
+       return false, 0, false, false
 }
index b081431200b9d61984cee8e2bbdb7d23bab8af9c..7f8119e9ae48942a4aee42acaccb46b27a460baa 100644 (file)
@@ -556,8 +556,8 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
                return t, nil
        }
 
-       tag, isCompound, ok := getUniversalType(v.Type())
-       if !ok {
+       matchAny, tag, isCompound, ok := getUniversalType(v.Type())
+       if !ok || matchAny {
                return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
        }