diff options
Diffstat (limited to 'src/pkg/asn1')
-rw-r--r-- | src/pkg/asn1/Makefile | 2 | ||||
-rw-r--r-- | src/pkg/asn1/asn1.go | 194 | ||||
-rw-r--r-- | src/pkg/asn1/asn1_test.go | 193 | ||||
-rw-r--r-- | src/pkg/asn1/common.go | 5 | ||||
-rw-r--r-- | src/pkg/asn1/marshal.go | 40 | ||||
-rw-r--r-- | src/pkg/asn1/marshal_test.go | 55 |
6 files changed, 248 insertions, 241 deletions
diff --git a/src/pkg/asn1/Makefile b/src/pkg/asn1/Makefile index 40b76b849..6b7770e82 100644 --- a/src/pkg/asn1/Makefile +++ b/src/pkg/asn1/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=asn1 GOFILES=\ diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go index bba8a0fe2..d06b1d4d7 100644 --- a/src/pkg/asn1/asn1.go +++ b/src/pkg/asn1/asn1.go @@ -150,6 +150,20 @@ func parseBitString(bytes []byte) (ret BitString, err os.Error) { // An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. type ObjectIdentifier []int +// Equal returns true iff oi and other represent the same identifier. +func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { + if len(oi) != len(other) { + return false + } + for i := 0; i < len(oi); i++ { + if oi[i] != other[i] { + return false + } + } + + return true +} + // parseObjectIdentifier parses an OBJECT IDENTIFER from the given bytes and // returns it. An object identifer is a sequence of variable length integers // that are assigned in a hierarachy. @@ -179,6 +193,17 @@ func parseObjectIdentifier(bytes []byte) (s []int, err os.Error) { return } +// ENUMERATED + +// An Enumerated is represented as a plain int. +type Enumerated int + + +// FLAG + +// A Flag accepts any data and is set to true if present. +type Flag bool + // parseBase128Int parses a base-128 encoded int from the given offset in the // given byte array. It returns the value and the new offset. func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) { @@ -202,101 +227,20 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Erro // UTCTime -func isDigit(b byte) bool { return '0' <= b && b <= '9' } - -// twoDigits returns the value of two, base 10 digits. -func twoDigits(bytes []byte, max int) (int, bool) { - for i := 0; i < 2; i++ { - if !isDigit(bytes[i]) { - return 0, false - } - } - value := (int(bytes[0])-'0')*10 + int(bytes[1]-'0') - if value > max { - return 0, false - } - return value, true -} - -// parseUTCTime parses the UTCTime from the given byte array and returns the -// resulting time. func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { - // A UTCTime can take the following formats: - // - // 1111111 - // 01234567890123456 - // - // YYMMDDhhmmZ - // YYMMDDhhmm+hhmm - // YYMMDDhhmm-hhmm - // YYMMDDhhmmssZ - // YYMMDDhhmmss+hhmm - // YYMMDDhhmmss-hhmm - if len(bytes) < 11 { - err = SyntaxError{"UTCTime too short"} - return - } - ret = new(time.Time) - - var ok1, ok2, ok3, ok4, ok5 bool - year, ok1 := twoDigits(bytes[0:2], 99) - // RFC 5280, section 5.1.2.4 says that years 2050 or later use another date - // scheme. - if year >= 50 { - ret.Year = 1900 + int64(year) - } else { - ret.Year = 2000 + int64(year) - } - ret.Month, ok2 = twoDigits(bytes[2:4], 12) - ret.Day, ok3 = twoDigits(bytes[4:6], 31) - ret.Hour, ok4 = twoDigits(bytes[6:8], 23) - ret.Minute, ok5 = twoDigits(bytes[8:10], 59) - if !ok1 || !ok2 || !ok3 || !ok4 || !ok5 { - goto Error - } - bytes = bytes[10:] - switch bytes[0] { - case '0', '1', '2', '3', '4', '5', '6': - if len(bytes) < 3 { - goto Error - } - ret.Second, ok1 = twoDigits(bytes[0:2], 60) // 60, not 59, because of leap seconds. - if !ok1 { - goto Error - } - bytes = bytes[2:] - } - if len(bytes) == 0 { - goto Error - } - switch bytes[0] { - case 'Z': - if len(bytes) != 1 { - goto Error - } + s := string(bytes) + ret, err = time.Parse("0601021504Z0700", s) + if err == nil { return - case '-', '+': - if len(bytes) != 5 { - goto Error - } - hours, ok1 := twoDigits(bytes[1:3], 12) - minutes, ok2 := twoDigits(bytes[3:5], 59) - if !ok1 || !ok2 { - goto Error - } - sign := 1 - if bytes[0] == '-' { - sign = -1 - } - ret.ZoneOffset = sign * (60 * (hours*60 + minutes)) - default: - goto Error } + ret, err = time.Parse("060102150405Z0700", s) return +} -Error: - err = SyntaxError{"invalid UTCTime"} - return +// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// and returns the resulting time. +func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { + return time.Parse("20060102150405Z0700", string(bytes)) } // PrintableString @@ -346,11 +290,20 @@ func parseIA5String(bytes []byte) (ret string, err os.Error) { return } +// T61String + +// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// byte array and returns it. +func parseT61String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + // A RawValue represents an undecoded ASN.1 object. type RawValue struct { Class, Tag int IsCompound bool Bytes []byte + FullBytes []byte // includes the tag and length } // RawContent is used to signal that the undecoded, DER data needs to be @@ -462,6 +415,8 @@ func parseSequenceOf(bytes []byte, sliceType *reflect.SliceType, elemType reflec var ( bitStringType = reflect.Typeof(BitString{}) objectIdentifierType = reflect.Typeof(ObjectIdentifier{}) + enumeratedType = reflect.Typeof(Enumerated(0)) + flagType = reflect.Typeof(Flag(false)) timeType = reflect.Typeof(&time.Time{}) rawValueType = reflect.Typeof(RawValue{}) rawContentsType = reflect.Typeof(RawContent(nil)) @@ -499,7 +454,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam err = SyntaxError{"data truncated"} return } - result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length]} + result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]} offset += t.length v.(*reflect.StructValue).Set(reflect.NewValue(result).(*reflect.StructValue)) return @@ -525,6 +480,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam result, err = parsePrintableString(innerBytes) case tagIA5String: result, err = parseIA5String(innerBytes) + case tagT61String: + result, err = parseT61String(innerBytes) case tagInteger: result, err = parseInt64(innerBytes) case tagBitString: @@ -559,9 +516,20 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam return } if params.explicit { - if t.class == classContextSpecific && t.tag == *params.tag && t.isCompound { - t, offset, err = parseTagAndLength(bytes, offset) - if err != nil { + if t.class == classContextSpecific && t.tag == *params.tag && (t.length == 0 || t.isCompound) { + if t.length > 0 { + t, offset, err = parseTagAndLength(bytes, offset) + if err != nil { + return + } + } else { + if fieldType != flagType { + err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + return + } + + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) return } } else { @@ -584,6 +552,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam universalTag = tagIA5String } + // Special case for time: UTCTime and GeneralizedTime both map to the + // Go type time.Time. + if universalTag == tagUTCTime && t.tag == tagGeneralizedTime { + universalTag = tagGeneralizedTime + } + expectedClass := classUniversal expectedTag := universalTag @@ -617,7 +591,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam sliceValue := v.(*reflect.SliceValue) sliceValue.Set(reflect.MakeSlice(sliceValue.Type().(*reflect.SliceType), len(newSlice), len(newSlice))) if err1 == nil { - reflect.ArrayCopy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) + reflect.Copy(sliceValue, reflect.NewValue(newSlice).(reflect.ArrayOrSliceValue)) } err = err1 return @@ -631,12 +605,30 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam return case timeType: ptrValue := v.(*reflect.PtrValue) - time, err1 := parseUTCTime(innerBytes) + var time *time.Time + var err1 os.Error + if universalTag == tagUTCTime { + time, err1 = parseUTCTime(innerBytes) + } else { + time, err1 = parseGeneralizedTime(innerBytes) + } if err1 == nil { ptrValue.Set(reflect.NewValue(time).(*reflect.PtrValue)) } err = err1 return + case enumeratedType: + parsedInt, err1 := parseInt(innerBytes) + enumValue := v.(*reflect.IntValue) + if err1 == nil { + enumValue.Set(int64(parsedInt)) + } + err = err1 + return + case flagType: + flagValue := v.(*reflect.BoolValue) + flagValue.Set(true) + return } switch val := v.(type) { case *reflect.BoolValue: @@ -691,7 +683,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam sliceType := fieldType.(*reflect.SliceType) if sliceType.Elem().Kind() == reflect.Uint8 { val.Set(reflect.MakeSlice(sliceType, len(innerBytes), len(innerBytes))) - reflect.ArrayCopy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) + reflect.Copy(val, reflect.NewValue(innerBytes).(reflect.ArrayOrSliceValue)) return } newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem()) @@ -707,6 +699,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam v, err = parsePrintableString(innerBytes) case tagIA5String: v, err = parseIA5String(innerBytes) + case tagT61String: + v, err = parseT61String(innerBytes) default: err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)} } @@ -753,6 +747,10 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // An ASN.1 OBJECT IDENTIFIER can be written to an // ObjectIdentifier. // +// An ASN.1 ENUMERATED can be written to an Enumerated. +// +// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a *time.Time. +// // An ASN.1 PrintableString or IA5String can be written to a string. // // Any of the above ASN.1 values can be written to an interface{}. @@ -777,7 +775,7 @@ 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(val interface{}, b []byte) (rest []byte, err os.Error) { +func Unmarshal(b []byte, val interface{}) (rest []byte, err os.Error) { v := reflect.NewValue(val).(*reflect.PtrValue).Elem() offset, err := parseField(v, b, 0, fieldParameters{}) if err != nil { diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go index b5bce93b7..34b5f1ecd 100644 --- a/src/pkg/asn1/asn1_test.go +++ b/src/pkg/asn1/asn1_test.go @@ -18,16 +18,16 @@ type int64Test struct { } var int64TestData = []int64Test{ - int64Test{[]byte{0x00}, true, 0}, - int64Test{[]byte{0x7f}, true, 127}, - int64Test{[]byte{0x00, 0x80}, true, 128}, - int64Test{[]byte{0x01, 0x00}, true, 256}, - int64Test{[]byte{0x80}, true, -128}, - int64Test{[]byte{0xff, 0x7f}, true, -129}, - int64Test{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, - int64Test{[]byte{0xff}, true, -1}, - int64Test{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, - int64Test{[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, + {[]byte{0x00}, true, 0}, + {[]byte{0x7f}, true, 127}, + {[]byte{0x00, 0x80}, true, 128}, + {[]byte{0x01, 0x00}, true, 256}, + {[]byte{0x80}, true, -128}, + {[]byte{0xff, 0x7f}, true, -129}, + {[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, true, -1}, + {[]byte{0xff}, true, -1}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, true, -9223372036854775808}, + {[]byte{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, false, 0}, } func TestParseInt64(t *testing.T) { @@ -50,12 +50,12 @@ type bitStringTest struct { } var bitStringTestData = []bitStringTest{ - bitStringTest{[]byte{}, false, []byte{}, 0}, - bitStringTest{[]byte{0x00}, true, []byte{}, 0}, - bitStringTest{[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, - bitStringTest{[]byte{0x07, 0x01}, false, []byte{}, 0}, - bitStringTest{[]byte{0x07, 0x40}, false, []byte{}, 0}, - bitStringTest{[]byte{0x08, 0x00}, false, []byte{}, 0}, + {[]byte{}, false, []byte{}, 0}, + {[]byte{0x00}, true, []byte{}, 0}, + {[]byte{0x07, 0x00}, true, []byte{0x00}, 1}, + {[]byte{0x07, 0x01}, false, []byte{}, 0}, + {[]byte{0x07, 0x40}, false, []byte{}, 0}, + {[]byte{0x08, 0x00}, false, []byte{}, 0}, } func TestBitString(t *testing.T) { @@ -95,12 +95,12 @@ type bitStringRightAlignTest struct { } var bitStringRightAlignTests = []bitStringRightAlignTest{ - bitStringRightAlignTest{[]byte{0x80}, 1, []byte{0x01}}, - bitStringRightAlignTest{[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, - bitStringRightAlignTest{[]byte{}, 0, []byte{}}, - bitStringRightAlignTest{[]byte{0xce}, 8, []byte{0xce}}, - bitStringRightAlignTest{[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, - bitStringRightAlignTest{[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, + {[]byte{0x80}, 1, []byte{0x01}}, + {[]byte{0x80, 0x80}, 9, []byte{0x01, 0x01}}, + {[]byte{}, 0, []byte{}}, + {[]byte{0xce}, 8, []byte{0xce}}, + {[]byte{0xce, 0x47}, 16, []byte{0xce, 0x47}}, + {[]byte{0x34, 0x50}, 12, []byte{0x03, 0x45}}, } func TestBitStringRightAlign(t *testing.T) { @@ -120,11 +120,11 @@ type objectIdentifierTest struct { } var objectIdentifierTestData = []objectIdentifierTest{ - objectIdentifierTest{[]byte{}, false, []int{}}, - objectIdentifierTest{[]byte{85}, true, []int{2, 5}}, - objectIdentifierTest{[]byte{85, 0x02}, true, []int{2, 5, 2}}, - objectIdentifierTest{[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, - objectIdentifierTest{[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, + {[]byte{}, false, []int{}}, + {[]byte{85}, true, []int{2, 5}}, + {[]byte{85, 0x02}, true, []int{2, 5, 2}}, + {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, + {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, } func TestObjectIdentifier(t *testing.T) { @@ -147,23 +147,23 @@ type timeTest struct { out *time.Time } -var timeTestData = []timeTest{ - timeTest{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, - timeTest{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, - timeTest{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, ""}}, - timeTest{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, ""}}, - timeTest{"a10506234540Z", false, nil}, - timeTest{"91a506234540Z", false, nil}, - timeTest{"9105a6234540Z", false, nil}, - timeTest{"910506a34540Z", false, nil}, - timeTest{"910506334a40Z", false, nil}, - timeTest{"91050633444aZ", false, nil}, - timeTest{"910506334461Z", false, nil}, - timeTest{"910506334400Za", false, nil}, +var utcTestData = []timeTest{ + {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}}, + {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}}, + {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}}, + {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}}, + {"a10506234540Z", false, nil}, + {"91a506234540Z", false, nil}, + {"9105a6234540Z", false, nil}, + {"910506a34540Z", false, nil}, + {"910506334a40Z", false, nil}, + {"91050633444aZ", false, nil}, + {"910506334461Z", false, nil}, + {"910506334400Za", false, nil}, } -func TestTime(t *testing.T) { - for i, test := range timeTestData { +func TestUTCTime(t *testing.T) { + for i, test := range utcTestData { ret, err := parseUTCTime([]byte(test.in)) if (err == nil) != test.ok { t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) @@ -176,6 +176,27 @@ func TestTime(t *testing.T) { } } +var generalizedTimeTestData = []timeTest{ + {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}}, + {"20100102030405", false, nil}, + {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}}, + {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}}, +} + +func TestGeneralizedTime(t *testing.T) { + for i, test := range generalizedTimeTestData { + ret, err := parseGeneralizedTime([]byte(test.in)) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if err == nil { + if !reflect.DeepEqual(test.out, ret) { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } + } +} + type tagAndLengthTest struct { in []byte ok bool @@ -183,18 +204,18 @@ type tagAndLengthTest struct { } var tagAndLengthData = []tagAndLengthTest{ - tagAndLengthTest{[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, - tagAndLengthTest{[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, - tagAndLengthTest{[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, - tagAndLengthTest{[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, - tagAndLengthTest{[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, - tagAndLengthTest{[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, - tagAndLengthTest{[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, - tagAndLengthTest{[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, - tagAndLengthTest{[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, - tagAndLengthTest{[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, - tagAndLengthTest{[]byte{0x1f, 0x85}, false, tagAndLength{}}, - tagAndLengthTest{[]byte{0x30, 0x80}, false, tagAndLength{}}, + {[]byte{0x80, 0x01}, true, tagAndLength{2, 0, 1, false}}, + {[]byte{0xa0, 0x01}, true, tagAndLength{2, 0, 1, true}}, + {[]byte{0x02, 0x00}, true, tagAndLength{0, 2, 0, false}}, + {[]byte{0xfe, 0x00}, true, tagAndLength{3, 30, 0, true}}, + {[]byte{0x1f, 0x01, 0x00}, true, tagAndLength{0, 1, 0, false}}, + {[]byte{0x1f, 0x81, 0x00, 0x00}, true, tagAndLength{0, 128, 0, false}}, + {[]byte{0x1f, 0x81, 0x80, 0x01, 0x00}, true, tagAndLength{0, 0x4001, 0, false}}, + {[]byte{0x00, 0x81, 0x01}, true, tagAndLength{0, 0, 1, false}}, + {[]byte{0x00, 0x82, 0x01, 0x00}, true, tagAndLength{0, 0, 256, false}}, + {[]byte{0x00, 0x83, 0x01, 0x00}, false, tagAndLength{}}, + {[]byte{0x1f, 0x85}, false, tagAndLength{}}, + {[]byte{0x30, 0x80}, false, tagAndLength{}}, } func TestParseTagAndLength(t *testing.T) { @@ -223,17 +244,17 @@ func newString(s string) *string { return &s } func newBool(b bool) *bool { return &b } var parseFieldParametersTestData []parseFieldParametersTest = []parseFieldParametersTest{ - parseFieldParametersTest{"", fieldParameters{}}, - parseFieldParametersTest{"ia5", fieldParameters{stringType: tagIA5String}}, - parseFieldParametersTest{"printable", fieldParameters{stringType: tagPrintableString}}, - parseFieldParametersTest{"optional", fieldParameters{optional: true}}, - parseFieldParametersTest{"explicit", fieldParameters{explicit: true, tag: new(int)}}, - parseFieldParametersTest{"optional,explicit", fieldParameters{optional: true, explicit: true, tag: new(int)}}, - parseFieldParametersTest{"default:42", fieldParameters{defaultValue: newInt64(42)}}, - parseFieldParametersTest{"tag:17", fieldParameters{tag: newInt(17)}}, - parseFieldParametersTest{"optional,explicit,default:42,tag:17", fieldParameters{optional: true, explicit: true, defaultValue: newInt64(42), tag: newInt(17)}}, - parseFieldParametersTest{"optional,explicit,default:42,tag:17,rubbish1", fieldParameters{true, true, newInt64(42), newInt(17), 0, false}}, - parseFieldParametersTest{"set", fieldParameters{set: true}}, + {"", fieldParameters{}}, + {"ia5", fieldParameters{stringType: tagIA5String}}, + {"printable", fieldParameters{stringType: tagPrintableString}}, + {"optional", fieldParameters{optional: true}}, + {"explicit", fieldParameters{explicit: 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}}, + {"set", fieldParameters{set: true}}, } func TestParseFieldParameters(t *testing.T) { @@ -269,20 +290,20 @@ type TestElementsAfterString struct { } var unmarshalTestData []unmarshalTest = []unmarshalTest{ - unmarshalTest{[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, - unmarshalTest{[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, - unmarshalTest{[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, - unmarshalTest{[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, - unmarshalTest{[]byte{0x02, 0x01, 0x10}, newInt(16)}, - unmarshalTest{[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, - unmarshalTest{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, - unmarshalTest{[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test")}}, - unmarshalTest{[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}}}, - unmarshalTest{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, - unmarshalTest{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, - unmarshalTest{[]byte{0x01, 0x01, 0x00}, newBool(false)}, - unmarshalTest{[]byte{0x01, 0x01, 0x01}, newBool(true)}, - unmarshalTest{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, + {[]byte{0x02, 0x01, 0x42}, newInt(0x42)}, + {[]byte{0x30, 0x08, 0x06, 0x06, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d}, &TestObjectIdentifierStruct{[]int{1, 2, 840, 113549}}}, + {[]byte{0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0}, &BitString{[]byte{110, 93, 192}, 18}}, + {[]byte{0x30, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &[]int{1, 2, 3}}, + {[]byte{0x02, 0x01, 0x10}, newInt(16)}, + {[]byte{0x13, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, newString("test")}, + {[]byte{0x16, 0x04, 't', 'e', 's', 't'}, &RawValue{0, 22, false, []byte("test"), []byte("\x16\x04test")}}, + {[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}}, + {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, + {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, + {[]byte{0x01, 0x01, 0x00}, newBool(false)}, + {[]byte{0x01, 0x01, 0x01}, newBool(true)}, + {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, } func TestUnmarshal(t *testing.T) { @@ -291,7 +312,7 @@ func TestUnmarshal(t *testing.T) { zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()) pv.(*reflect.PtrValue).PointTo(zv) val := pv.Interface() - _, err := Unmarshal(val, test.in) + _, err := Unmarshal(test.in, val) if err != nil { t.Errorf("Unmarshal failed at index %d %v", i, err) } @@ -342,11 +363,11 @@ type PublicKeyInfo struct { func TestCertificate(t *testing.T) { // This is a minimal, self-signed certificate that should parse correctly. var cert Certificate - if _, err := Unmarshal(&cert, derEncodedSelfSignedCertBytes); err != nil { + if _, err := Unmarshal(derEncodedSelfSignedCertBytes, &cert); err != nil { t.Errorf("Unmarshal failed: %v", err) } if !reflect.DeepEqual(cert, derEncodedSelfSignedCert) { - t.Errorf("Bad result:\ngot: %+v\nwant: %+v\n", cert, derEncodedSelfSignedCert) + t.Errorf("Bad result:\ngot: %+v\nwant: %+v", cert, derEncodedSelfSignedCert) } } @@ -355,7 +376,7 @@ func TestCertificateWithNUL(t *testing.T) { // NUL isn't a permitted character in a PrintableString. var cert Certificate - if _, err := Unmarshal(&cert, derEncodedPaypalNULCertBytes); err == nil { + if _, err := Unmarshal(derEncodedPaypalNULCertBytes, &cert); err == nil { t.Error("Unmarshal succeeded, should not have") } } @@ -369,7 +390,7 @@ func TestRawStructs(t *testing.T) { var s rawStructTest input := []byte{0x30, 0x03, 0x02, 0x01, 0x50} - rest, err := Unmarshal(&s, input) + rest, err := Unmarshal(input, &s) if len(rest) != 0 { t.Errorf("incomplete parse: %x", rest) return @@ -389,7 +410,7 @@ func TestRawStructs(t *testing.T) { var derEncodedSelfSignedCert = Certificate{ TBSCertificate: TBSCertificate{ Version: 0, - SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, + SerialNumber: RawValue{Class: 0, Tag: 2, IsCompound: false, Bytes: []uint8{0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}, FullBytes: []byte{2, 9, 0x0, 0x8c, 0xc3, 0x37, 0x92, 0x10, 0xec, 0x2c, 0x98}}, SignatureAlgorithm: AlgorithmIdentifier{Algorithm: ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}}, Issuer: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, @@ -399,7 +420,7 @@ var derEncodedSelfSignedCert = Certificate{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, }, - Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: ""}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: ""}}, + Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, Subject: RDNSequence{ RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go index 14fa30426..4a5eca145 100644 --- a/src/pkg/asn1/common.go +++ b/src/pkg/asn1/common.go @@ -24,11 +24,14 @@ const ( tagBitString = 3 tagOctetString = 4 tagOID = 6 + tagEnum = 10 tagSequence = 16 tagSet = 17 tagPrintableString = 19 + tagT61String = 20 tagIA5String = 22 tagUTCTime = 23 + tagGeneralizedTime = 24 ) const ( @@ -121,6 +124,8 @@ func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) { return tagBitString, false, true case timeType: return tagUTCTime, false, true + case enumeratedType: + return tagEnum, false, true } switch t := t.(type) { case *reflect.BoolType: diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go index d4f8f782d..24548714b 100644 --- a/src/pkg/asn1/marshal.go +++ b/src/pkg/asn1/marshal.go @@ -96,19 +96,6 @@ func marshalBase128Int(out *forkableWriter, n int64) (err os.Error) { return nil } -func base128Length(i int) (numBytes int) { - if i == 0 { - return 1 - } - - for i > 0 { - numBytes++ - i >>= 7 - } - - return -} - func marshalInt64(out *forkableWriter, i int64) (err os.Error) { n := int64Length(i) @@ -123,11 +110,14 @@ func marshalInt64(out *forkableWriter, i int64) (err os.Error) { } func int64Length(i int64) (numBytes int) { - if i == 0 { - return 1 + numBytes = 1 + + for i > 127 { + numBytes++ + i >>= 8 } - for i > 0 { + for i < -128 { numBytes++ i >>= 8 } @@ -478,25 +468,15 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) return nil } -// Marshal serialises val as an ASN.1 structure and writes the result to out. -// In the case of an error, no output is produced. -func Marshal(out io.Writer, val interface{}) os.Error { +// Marshal returns the ASN.1 encoding of val. +func Marshal(val interface{}) ([]byte, os.Error) { + var out bytes.Buffer v := reflect.NewValue(val) f := newForkableWriter() err := marshalField(f, v, fieldParameters{}) if err != nil { - return err - } - _, err = f.writeTo(out) - return err -} - -// MarshalToMemory performs the same actions as Marshal, but returns the result -// as a byte slice. -func MarshalToMemory(val interface{}) ([]byte, os.Error) { - var out bytes.Buffer - if err := Marshal(&out, val); err != nil { return nil, err } + _, err = f.writeTo(&out) return out.Bytes(), nil } diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go index 67878f9bb..85eafc9e4 100644 --- a/src/pkg/asn1/marshal_test.go +++ b/src/pkg/asn1/marshal_test.go @@ -58,40 +58,43 @@ type marshalTest struct { } var marshalTests = []marshalTest{ - marshalTest{10, "02010a"}, - marshalTest{intStruct{64}, "3003020140"}, - marshalTest{twoIntStruct{64, 65}, "3006020140020141"}, - marshalTest{nestedStruct{intStruct{127}}, "3005300302017f"}, - marshalTest{[]byte{1, 2, 3}, "0403010203"}, - marshalTest{implicitTagTest{64}, "3003850140"}, - marshalTest{explicitTagTest{64}, "3005a503020140"}, - marshalTest{time.SecondsToUTC(0), "170d3730303130313030303030305a"}, - marshalTest{time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, - marshalTest{setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, - marshalTest{BitString{[]byte{0x80}, 1}, "03020780"}, - marshalTest{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, - marshalTest{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, - marshalTest{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, - marshalTest{"test", "130474657374"}, - marshalTest{ia5StringTest{"test"}, "3006160474657374"}, - marshalTest{printableStringTest{"test"}, "3006130474657374"}, - marshalTest{printableStringTest{"test*"}, "30071305746573742a"}, - marshalTest{rawContentsStruct{nil, 64}, "3003020140"}, - marshalTest{rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, - marshalTest{RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, - marshalTest{testSET([]int{10}), "310302010a"}, + {10, "02010a"}, + {127, "02017f"}, + {128, "02020080"}, + {-128, "020180"}, + {-129, "0202ff7f"}, + {intStruct{64}, "3003020140"}, + {twoIntStruct{64, 65}, "3006020140020141"}, + {nestedStruct{intStruct{127}}, "3005300302017f"}, + {[]byte{1, 2, 3}, "0403010203"}, + {implicitTagTest{64}, "3003850140"}, + {explicitTagTest{64}, "3005a503020140"}, + {time.SecondsToUTC(0), "170d3730303130313030303030305a"}, + {time.SecondsToUTC(1258325776), "170d3039313131353232353631365a"}, + {setPST(time.SecondsToUTC(1258325776)), "17113039313131353232353631362d30383030"}, + {BitString{[]byte{0x80}, 1}, "03020780"}, + {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, + {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, + {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, + {"test", "130474657374"}, + {ia5StringTest{"test"}, "3006160474657374"}, + {printableStringTest{"test"}, "3006130474657374"}, + {printableStringTest{"test*"}, "30071305746573742a"}, + {rawContentsStruct{nil, 64}, "3003020140"}, + {rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"}, + {RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"}, + {testSET([]int{10}), "310302010a"}, } func TestMarshal(t *testing.T) { for i, test := range marshalTests { - buf := bytes.NewBuffer(nil) - err := Marshal(buf, test.in) + data, err := Marshal(test.in) if err != nil { t.Errorf("#%d failed: %s", i, err) } out, _ := hex.DecodeString(test.out) - if bytes.Compare(out, buf.Bytes()) != 0 { - t.Errorf("#%d got: %x want %x", i, buf.Bytes(), out) + if bytes.Compare(out, data) != 0 { + t.Errorf("#%d got: %x want %x", i, data, out) } } } |