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 // 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 // a number of ASN.1 values from the given byte slice and returns them as a
// slice of Go values of the given type. // 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) matchAny, expectedTag, compoundType, ok := getUniversalType(elemType)
if !ok { if !ok {
err = StructuralError{"unknown Go type for slice"} err = StructuralError{"unknown Go type for slice"}
@ -599,7 +599,7 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
t.tag = TagUTCTime 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"} err = StructuralError{"sequence tag mismatch"}
return return
} }
@ -611,10 +611,10 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
numElements++ numElements++
} }
ret = reflect.MakeSlice(sliceType, numElements, numElements) ret = reflect.MakeSlice(sliceType, numElements, numElements)
params := fieldParameters{} fp := fieldParameters{choice: params.choice}
offset := 0 offset := 0
for i := 0; i < numElements; i++ { 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 { if err != nil {
return 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. // We have unwrapped any explicit tagging at this point.
if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) || if (!matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) ||
(!matchAny && t.isCompound != compoundType) { (!matchAny && t.isCompound != compoundType)) && !params.choice {
// Tags don't match. Again, it could be an optional element. // Tags don't match. Again, it could be an optional element.
ok := setDefaultValue(v, params) ok := setDefaultValue(v, params)
if ok { if ok {
@ -913,6 +913,17 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
innerOffset := 0 innerOffset := 0
for i := 0; i < structType.NumField(); i++ { for i := 0; i < structType.NumField(); i++ {
field := structType.Field(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 { if i == 0 && field.Type == rawContentsType {
continue continue
} }
@ -932,7 +943,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
reflect.Copy(val, reflect.ValueOf(innerBytes)) reflect.Copy(val, reflect.ValueOf(innerBytes))
return return
} }
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem(), params)
if err1 == nil { if err1 == nil {
val.Set(newSlice) val.Set(newSlice)
} }

View File

@ -74,6 +74,7 @@ type tagAndLength struct {
type fieldParameters struct { type fieldParameters struct {
optional bool // true iff the field is OPTIONAL optional bool // true iff the field is OPTIONAL
explicit bool // true iff an EXPLICIT tag is in use. 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. application bool // true iff an APPLICATION tag is in use.
private bool // true iff a PRIVATE 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). 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 { if ret.tag == nil {
ret.tag = new(int) ret.tag = new(int)
} }
case part == "choice":
ret.choice = true
case part == "generalized": case part == "generalized":
ret.timeType = TagGeneralizedTime ret.timeType = TagGeneralizedTime
case part == "utc": case part == "utc":

View File

@ -480,7 +480,11 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
default: default:
m := make([]encoder, n1) m := make([]encoder, n1)
for i := 0; i < n1; i++ { 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 { if err != nil {
return nil, err return nil, err
} }
@ -495,6 +499,10 @@ func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error
} }
var fp fieldParameters var fp fieldParameters
fp.choice = params.choice
if params.choice && params.set {
fp.explicit = true
}
switch l := v.Len(); l { switch l := v.Len(); l {
case 0: case 0:
@ -640,6 +648,10 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
if params.explicit { if params.explicit {
t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound})) t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound}))
if params.choice {
t.tag = bytesEncoder(nil)
}
tt := new(taggedEncoder) tt := new(taggedEncoder)
tt.body = t 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})) t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound}))
if tag == TagSequence && params.choice {
t.tag = bytesEncoder(nil)
}
return t, nil return t, nil
} }