From a52e547fc7a318d3a0ac5d4018b069850d3654e5 Mon Sep 17 00:00:00 2001 From: InfiniteLoopSpace <35842605+InfiniteLoopSpace@users.noreply.github.com> Date: Wed, 14 Nov 2018 12:36:41 +0100 Subject: [PATCH] Add support for ASN.1 "choice" --- .gitignore | 1 + asn1/asn1.go | 25 ++++++++++++++++++------- asn1/common.go | 3 +++ asn1/marshal.go | 18 +++++++++++++++++- 4 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/asn1/asn1.go b/asn1/asn1.go index 3cfd9d1..91893c7 100644 --- a/asn1/asn1.go +++ b/asn1/asn1.go @@ -572,7 +572,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i // parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse // 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) { +func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type, params fieldParameters) (ret reflect.Value, err error) { matchAny, expectedTag, compoundType, ok := getUniversalType(elemType) if !ok { err = StructuralError{"unknown Go type for slice"} @@ -599,7 +599,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type t.tag = TagUTCTime } - if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) { + if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) && !params.choice { err = StructuralError{"sequence tag mismatch"} return } @@ -611,10 +611,10 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type numElements++ } ret = reflect.MakeSlice(sliceType, numElements, numElements) - params := fieldParameters{} + fp := fieldParameters{choice: params.choice} offset := 0 for i := 0; i < numElements; i++ { - offset, err = parseField(ret.Index(i), bytes, offset, params) + offset, err = parseField(ret.Index(i), bytes, offset, fp) if err != nil { return } @@ -800,8 +800,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } // We have unwrapped any explicit tagging at this point. - if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) || - (!matchAny && t.isCompound != compoundType) { + if (!matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) || + (!matchAny && t.isCompound != compoundType)) && !params.choice { // Tags don't match. Again, it could be an optional element. ok := setDefaultValue(v, params) if ok { @@ -913,6 +913,17 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam innerOffset := 0 for i := 0; i < structType.NumField(); i++ { field := structType.Field(i) + if params.choice { + tag := parseFieldParameters(field.Tag.Get("asn1")).tag + if tag != nil && t.tag == *tag || t.class != ClassContextSpecific && tag == nil { + if tag == nil || params.set { + innerBytes = bytes[initOffset:offset] + } + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, fieldParameters{}) + return + } + continue + } if i == 0 && field.Type == rawContentsType { continue } @@ -932,7 +943,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam reflect.Copy(val, reflect.ValueOf(innerBytes)) return } - newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) + newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem(), params) if err1 == nil { val.Set(newSlice) } diff --git a/asn1/common.go b/asn1/common.go index 255d1eb..eb04301 100644 --- a/asn1/common.go +++ b/asn1/common.go @@ -74,6 +74,7 @@ type tagAndLength struct { type fieldParameters struct { optional bool // true iff the field is OPTIONAL explicit bool // true iff an EXPLICIT tag is in use. + choice bool // true iff CHOICE tag is in use. application bool // true iff an APPLICATION tag is in use. private bool // true iff a PRIVATE tag is in use. defaultValue *int64 // a default value for INTEGER typed fields (maybe nil). @@ -100,6 +101,8 @@ func parseFieldParameters(str string) (ret fieldParameters) { if ret.tag == nil { ret.tag = new(int) } + case part == "choice": + ret.choice = true case part == "generalized": ret.timeType = TagGeneralizedTime case part == "utc": diff --git a/asn1/marshal.go b/asn1/marshal.go index c9ae2ca..1f15548 100644 --- a/asn1/marshal.go +++ b/asn1/marshal.go @@ -480,7 +480,11 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error default: m := make([]encoder, n1) for i := 0; i < n1; i++ { - m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1"))) + fp := parseFieldParameters(t.Field(i + startingField).Tag.Get("asn1")) + if params.explicit && params.choice { + fp.explicit = true + } + m[i], err = makeField(v.Field(i+startingField), fp) if err != nil { return nil, err } @@ -495,6 +499,10 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error } var fp fieldParameters + fp.choice = params.choice + if params.choice && params.set { + fp.explicit = true + } switch l := v.Len(); l { case 0: @@ -640,6 +648,10 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) { if params.explicit { t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound})) + if params.choice { + t.tag = bytesEncoder(nil) + } + tt := new(taggedEncoder) tt.body = t @@ -660,6 +672,10 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) { t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound})) + if tag == TagSequence && params.choice { + t.tag = bytesEncoder(nil) + } + return t, nil }