Add support for ASN.1 "choice"
This commit is contained in:
parent
5ca2e7da6f
commit
a52e547fc7
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.DS_Store
|
25
asn1/asn1.go
25
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)
|
||||
}
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user