Add support for ASN.1 "choice"

This commit is contained in:
InfiniteLoopSpace 2018-11-14 12:36:41 +01:00
parent 5ca2e7da6f
commit a52e547fc7
4 changed files with 39 additions and 8 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

View File

@ -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)
}

View File

@ -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":

View File

@ -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
}