]> Cypherpunks repositories - gostls13.git/commitdiff
asn1: extensions needed for parsing Kerberos
authorAdam Langley <agl@golang.org>
Tue, 29 Mar 2011 14:33:47 +0000 (10:33 -0400)
committerAdam Langley <agl@golang.org>
Tue, 29 Mar 2011 14:33:47 +0000 (10:33 -0400)
* Adds support for GENERAL STRING
* Adds support for APPLICATION tagged values.
* Add UnmarshalWithParams to set parameters for the top-level
  structure

R=golang-dev, rsc1, r
CC=golang-dev
https://golang.org/cl/4291075

src/pkg/asn1/asn1.go
src/pkg/asn1/asn1_test.go
src/pkg/asn1/common.go

index d06b1d4d776efb8d242015260f256abcdf5fb0c0..c5314517b34f428841d4b8304990ae406261c1e3 100644 (file)
@@ -389,6 +389,11 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec
                if err != nil {
                        return
                }
+               // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
+               // that a sequence of them can be parsed into a []string.
+               if t.tag == tagGeneralString {
+                       t.tag = tagPrintableString
+               }
                if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
                        err = StructuralError{"sequence tag mismatch"}
                        return
@@ -516,7 +521,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                return
        }
        if params.explicit {
-               if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
+               expectedClass := classContextSpecific
+               if params.application {
+                       expectedClass = classApplication
+               }
+               if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
                        if t.length > 0 {
                                t, offset, err = parseTagAndLength(bytes, offset)
                                if err != nil {
@@ -551,6 +560,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
        if universalTag == tagPrintableString && t.tag == tagIA5String {
                universalTag = tagIA5String
        }
+       // Likewise for GeneralString
+       if universalTag == tagPrintableString && t.tag == tagGeneralString {
+               universalTag = tagGeneralString
+       }
 
        // Special case for time: UTCTime and GeneralizedTime both map to the
        // Go type time.Time.
@@ -566,6 +579,11 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                expectedTag = *params.tag
        }
 
+       if !params.explicit && params.application && params.tag != nil {
+               expectedClass = classApplication
+               expectedTag = *params.tag
+       }
+
        // We have unwrapped any explicit tagging at this point.
        if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
                // Tags don't match. Again, it could be an optional element.
@@ -701,6 +719,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
                        v, err = parseIA5String(innerBytes)
                case tagT61String:
                        v, err = parseT61String(innerBytes)
+               case tagGeneralString:
+                       // GeneralString is specified in ISO-2022/ECMA-35,
+                       // A brief review suggests that it includes structures
+                       // that allow the encoding to change midstring and
+                       // such. We give up and pass it as an 8-bit string.
+                       v, err = parseT61String(innerBytes)
                default:
                        err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
                }
@@ -776,8 +800,14 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
 // Other ASN.1 types are not supported; if it encounters them,
 // Unmarshal returns a parse error.
 func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) {
+       return UnmarshalWithParams(b, val, "")
+}
+
+// UnmarshalWithParams allows field parameters to be specified for the
+// top-level element. The form of the params is the same as the field tags.
+func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err os.Error) {
        v := reflect.NewValue(val).(*reflect.PtrValue).Elem()
-       offset, err := parseField(v, b, 0, fieldParameters{})
+       offset, err := parseField(v, b, 0, parseFieldParameters(params))
        if err != nil {
                return nil, err
        }
index 34b5f1ecda6806fd04084a8a6aefa82d7b9e2be0..b7767656a428dfa51294aaaa7e537aa886b0baea 100644 (file)
@@ -249,11 +249,12 @@ var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParame
        {"printable", fieldParameters{stringType: tagPrintableString}},
        {"optional", fieldParameters{optional: true}},
        {"explicit", fieldParameters{explicit: true, tag: new(int)}},
+       {"application", fieldParameters{application: true, tag: new(int)}},
        {"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}},
        {"default:42", fieldParameters{defaultValue: newInt64(42)}},
        {"tag:17", fieldParameters{tag: newInt(17)}},
        {"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}},
-       {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}},
+       {"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, false, newInt64(42), newInt(17), 0, false}},
        {"set", fieldParameters{set: true}},
 }
 
index 4a5eca1450f0f61e6fef1ae0fc0ca588ea9dfa85..f2254a41bba850dc4e978fcd46e219f95d8f606f 100644 (file)
@@ -32,6 +32,7 @@ const (
        tagIA5String       = 22
        tagUTCTime         = 23
        tagGeneralizedTime = 24
+       tagGeneralString   = 27
 )
 
 const (
@@ -67,7 +68,8 @@ type tagAndLength struct {
 // fieldParameters is the parsed representation of tag string from a structure field.
 type fieldParameters struct {
        optional     bool   // true iff the field is OPTIONAL
-       explicit     bool   // true iff and EXPLICIT tag is in use.
+       explicit     bool   // true iff an EXPLICIT tag is in use.
+       application  bool   // true iff an APPLICATION tag is in use.
        defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
        tag          *int   // the EXPLICIT or IMPLICIT tag (maybe nil).
        stringType   int    // the string tag to use when marshaling.
@@ -89,7 +91,6 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        ret.explicit = true
                        if ret.tag == nil {
                                ret.tag = new(int)
-                               *ret.tag = 0
                        }
                case part == "ia5":
                        ret.stringType = tagIA5String
@@ -109,6 +110,11 @@ func parseFieldParameters(str string) (ret fieldParameters) {
                        }
                case part == "set":
                        ret.set = true
+               case part == "application":
+                       ret.application = true
+                       if ret.tag == nil {
+                               ret.tag = new(int)
+                       }
                }
        }
        return