summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/asn1
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/encoding/asn1')
-rw-r--r--src/pkg/encoding/asn1/asn1.go43
-rw-r--r--src/pkg/encoding/asn1/asn1_test.go75
-rw-r--r--src/pkg/encoding/asn1/marshal.go57
-rw-r--r--src/pkg/encoding/asn1/marshal_test.go9
4 files changed, 175 insertions, 9 deletions
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index 992356c26..ec7f91c1b 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -23,6 +23,7 @@ import (
"fmt"
"math/big"
"reflect"
+ "strconv"
"time"
)
@@ -197,6 +198,19 @@ func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
return true
}
+func (oi ObjectIdentifier) String() string {
+ var s string
+
+ for i, v := range oi {
+ if i > 0 {
+ s += "."
+ }
+ s += strconv.Itoa(v)
+ }
+
+ return s
+}
+
// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
// returns it. An object identifier is a sequence of variable length integers
// that are assigned in a hierarchy.
@@ -451,11 +465,17 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
if err != nil {
return
}
- // We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
- // that a sequence of them can be parsed into a []string.
- if t.tag == tagGeneralString {
+ switch t.tag {
+ case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
+ // We pretend that various other string types are
+ // PRINTABLE STRINGs so that a sequence of them can be
+ // parsed into a []string.
t.tag = tagPrintableString
+ case tagGeneralizedTime, tagUTCTime:
+ // Likewise, both time types are treated the same.
+ t.tag = tagUTCTime
}
+
if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
err = StructuralError{"sequence tag mismatch"}
return
@@ -632,6 +652,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
universalTag = tagGeneralizedTime
}
+ if params.set {
+ universalTag = tagSet
+ }
+
expectedClass := classUniversal
expectedTag := universalTag
@@ -852,13 +876,20 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
//
// The following tags on struct fields have special meaning to Unmarshal:
//
-// optional marks the field as ASN.1 OPTIONAL
-// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
-// default:x sets the default value for optional integer fields
+// application specifies that a APPLICATION tag is used
+// default:x sets the default value for optional integer fields
+// explicit specifies that an additional, explicit tag wraps the implicit one
+// optional marks the field as ASN.1 OPTIONAL
+// set causes a SET, rather than a SEQUENCE type to be expected
+// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
//
// If the type of the first field of a structure is RawContent then the raw
// ASN1 contents of the struct will be stored in it.
//
+// If the type name of a slice element ends with "SET" then it's treated as if
+// the "set" tag was set on it. This can be used with nested slices where a
+// struct tag cannot be given.
+//
// Other ASN.1 types are not supported; if it encounters them,
// Unmarshal returns a parse error.
func Unmarshal(b []byte, val interface{}) (rest []byte, err error) {
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index f68804ebf..b553f78e0 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -6,6 +6,7 @@ package asn1
import (
"bytes"
+ "fmt"
"math/big"
"reflect"
"testing"
@@ -171,6 +172,12 @@ func TestBitStringAt(t *testing.T) {
if bs.At(9) != 1 {
t.Error("#4: Failed")
}
+ if bs.At(-1) != 0 {
+ t.Error("#5: Failed")
+ }
+ if bs.At(17) != 0 {
+ t.Error("#6: Failed")
+ }
}
type bitStringRightAlignTest struct {
@@ -225,6 +232,10 @@ func TestObjectIdentifier(t *testing.T) {
}
}
}
+
+ if s := ObjectIdentifier([]int{1, 2, 3, 4}).String(); s != "1.2.3.4" {
+ t.Errorf("bad ObjectIdentifier.String(). Got %s, want 1.2.3.4", s)
+ }
}
type timeTest struct {
@@ -238,6 +249,7 @@ var utcTestData = []timeTest{
{"910506164540+0730", true, time.Date(1991, 05, 06, 16, 45, 40, 0, time.FixedZone("", 7*60*60+30*60))},
{"910506234540Z", true, time.Date(1991, 05, 06, 23, 45, 40, 0, time.UTC)},
{"9105062345Z", true, time.Date(1991, 05, 06, 23, 45, 0, 0, time.UTC)},
+ {"5105062345Z", true, time.Date(1951, 05, 06, 23, 45, 0, 0, time.UTC)},
{"a10506234540Z", false, time.Time{}},
{"91a506234540Z", false, time.Time{}},
{"9105a6234540Z", false, time.Time{}},
@@ -389,6 +401,10 @@ type TestBigInt struct {
X *big.Int
}
+type TestSet struct {
+ Ints []int `asn1:"set"`
+}
+
var unmarshalTestData = []struct {
in []byte
out interface{}
@@ -408,6 +424,7 @@ var unmarshalTestData = []struct {
{[]byte{0x01, 0x01, 0xff}, newBool(true)},
{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
+ {[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
}
func TestUnmarshal(t *testing.T) {
@@ -509,6 +526,38 @@ func TestRawStructs(t *testing.T) {
}
}
+type oiEqualTest struct {
+ first ObjectIdentifier
+ second ObjectIdentifier
+ same bool
+}
+
+var oiEqualTests = []oiEqualTest{
+ {
+ ObjectIdentifier{1, 2, 3},
+ ObjectIdentifier{1, 2, 3},
+ true,
+ },
+ {
+ ObjectIdentifier{1},
+ ObjectIdentifier{1, 2, 3},
+ false,
+ },
+ {
+ ObjectIdentifier{1, 2, 3},
+ ObjectIdentifier{10, 11, 12},
+ false,
+ },
+}
+
+func TestObjectIdentifierEqual(t *testing.T) {
+ for _, o := range oiEqualTests {
+ if s := o.first.Equal(o.second); s != o.same {
+ t.Errorf("ObjectIdentifier.Equal: got: %t want: %t", s, o.same)
+ }
+ }
+}
+
var derEncodedSelfSignedCert = Certificate{
TBSCertificate: TBSCertificate{
Version: 0,
@@ -737,3 +786,29 @@ var derEncodedPaypalNULCertBytes = []byte{
0xc8, 0x64, 0x8c, 0xb5, 0x50, 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43,
0x96, 0x07, 0xa8, 0xbb,
}
+
+var stringSliceTestData = [][]string{
+ {"foo", "bar"},
+ {"foo", "\\bar"},
+ {"foo", "\"bar\""},
+ {"foo", "åäö"},
+}
+
+func TestStringSlice(t *testing.T) {
+ for _, test := range stringSliceTestData {
+ bs, err := Marshal(test)
+ if err != nil {
+ t.Error(err)
+ }
+
+ var res []string
+ _, err = Unmarshal(bs, &res)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if fmt.Sprintf("%v", res) != fmt.Sprintf("%v", test) {
+ t.Errorf("incorrect marshal/unmarshal; %v != %v", res, test)
+ }
+ }
+}
diff --git a/src/pkg/encoding/asn1/marshal.go b/src/pkg/encoding/asn1/marshal.go
index ed17e41a5..e26fe59b3 100644
--- a/src/pkg/encoding/asn1/marshal.go
+++ b/src/pkg/encoding/asn1/marshal.go
@@ -295,8 +295,23 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
return out.WriteByte(byte('0' + v%10))
}
+func marshalFourDigits(out *forkableWriter, v int) (err error) {
+ var bytes [4]byte
+ for i := range bytes {
+ bytes[3-i] = '0' + byte(v%10)
+ v /= 10
+ }
+ _, err = out.Write(bytes[:])
+ return
+}
+
+func outsideUTCRange(t time.Time) bool {
+ year := t.Year()
+ return year < 1950 || year >= 2050
+}
+
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
- year, month, day := t.Date()
+ year := t.Year()
switch {
case 1950 <= year && year < 2000:
@@ -310,6 +325,24 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
return
}
+ return marshalTimeCommon(out, t)
+}
+
+func marshalGeneralizedTime(out *forkableWriter, t time.Time) (err error) {
+ year := t.Year()
+ if year < 0 || year > 9999 {
+ return StructuralError{"cannot represent time as GeneralizedTime"}
+ }
+ if err = marshalFourDigits(out, year); err != nil {
+ return
+ }
+
+ return marshalTimeCommon(out, t)
+}
+
+func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
+ _, month, day := t.Date()
+
err = marshalTwoDigits(out, int(month))
if err != nil {
return
@@ -378,7 +411,12 @@ func stripTagAndLength(in []byte) []byte {
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
switch value.Type() {
case timeType:
- return marshalUTCTime(out, value.Interface().(time.Time))
+ t := value.Interface().(time.Time)
+ if outsideUTCRange(t) {
+ return marshalGeneralizedTime(out, t)
+ } else {
+ return marshalUTCTime(out, t)
+ }
case bitStringType:
return marshalBitString(out, value.Interface().(BitString))
case objectIdentifierType:
@@ -504,7 +542,8 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
return StructuralError{"explicit string type given to non-string member"}
}
- if tag == tagPrintableString {
+ switch tag {
+ case tagPrintableString:
if params.stringType == 0 {
// This is a string without an explicit string type. We'll use
// a PrintableString if the character set in the string is
@@ -521,6 +560,10 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
} else {
tag = params.stringType
}
+ case tagUTCTime:
+ if outsideUTCRange(v.Interface().(time.Time)) {
+ tag = tagGeneralizedTime
+ }
}
if params.set {
@@ -568,6 +611,14 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
}
// Marshal returns the ASN.1 encoding of val.
+//
+// In addition to the struct tags recognised by Unmarshal, the following can be
+// used:
+//
+// ia5: causes strings to be marshaled as ASN.1, IA5 strings
+// omitempty: causes empty slices to be skipped
+// printable: causes strings to be marshaled as ASN.1, PrintableString strings.
+// utf8: causes strings to be marshaled as ASN.1, UTF8 strings
func Marshal(val interface{}) ([]byte, error) {
var out bytes.Buffer
v := reflect.ValueOf(val)
diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index 763c86da2..a15acbed0 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -67,6 +67,14 @@ type marshalTest struct {
out string // hex encoded
}
+func farFuture() time.Time {
+ t, err := time.Parse(time.RFC3339, "2100-04-05T12:01:01Z")
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
var marshalTests = []marshalTest{
{10, "02010a"},
{127, "02017f"},
@@ -83,6 +91,7 @@ var marshalTests = []marshalTest{
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
{time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
+ {farFuture(), "180f32313030303430353132303130315a"},
{BitString{[]byte{0x80}, 1}, "03020780"},
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},