summaryrefslogtreecommitdiff
path: root/src/pkg/asn1/marshal.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/asn1/marshal.go')
-rw-r--r--src/pkg/asn1/marshal.go400
1 files changed, 400 insertions, 0 deletions
diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go
new file mode 100644
index 000000000..2e9aa1439
--- /dev/null
+++ b/src/pkg/asn1/marshal.go
@@ -0,0 +1,400 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package asn1
+
+import (
+ "bytes";
+ "fmt";
+ "io";
+ "os";
+ "reflect";
+ "strings";
+ "time";
+)
+
+// A forkableWriter is an in-memory buffer that can be
+// 'forked' to create new forkableWriters that bracket the
+// original. After
+// pre, post := w.fork();
+// the overall sequence of bytes represented is logically w+pre+post.
+type forkableWriter struct {
+ *bytes.Buffer;
+ pre, post *forkableWriter;
+}
+
+func newForkableWriter() *forkableWriter {
+ return &forkableWriter{bytes.NewBuffer(nil), nil, nil}
+}
+
+func (f *forkableWriter) fork() (pre, post *forkableWriter) {
+ f.pre = newForkableWriter();
+ f.post = newForkableWriter();
+ return f.pre, f.post;
+}
+
+func (f *forkableWriter) Len() (l int) {
+ l += f.Buffer.Len();
+ if f.pre != nil {
+ l += f.pre.Len()
+ }
+ if f.post != nil {
+ l += f.post.Len()
+ }
+ return;
+}
+
+func (f *forkableWriter) writeTo(out io.Writer) (n int, err os.Error) {
+ n, err = out.Write(f.Bytes());
+ if err != nil {
+ return
+ }
+
+ var nn int;
+
+ if f.pre != nil {
+ nn, err = f.pre.writeTo(out);
+ n += nn;
+ if err != nil {
+ return
+ }
+ }
+
+ if f.pre != nil {
+ nn, err = f.post.writeTo(out);
+ n += nn;
+ }
+ return;
+}
+
+func marshalBase128Int(out *forkableWriter, i int64) (err os.Error) {
+ if i == 0 {
+ err = out.WriteByte(0);
+ return;
+ }
+
+ for i > 0 {
+ next := i >> 7;
+ o := byte(i & 0x7f);
+ if next > 0 {
+ o |= 0x80
+ }
+ err = out.WriteByte(o);
+ if err != nil {
+ return
+ }
+ i = next;
+ }
+
+ return nil;
+}
+
+func base128Length(i int) (numBytes int) {
+ if i == 0 {
+ return 1
+ }
+
+ for i > 0 {
+ numBytes++;
+ i >>= 7;
+ }
+
+ return;
+}
+
+func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err os.Error) {
+ b := uint8(t.class) << 6;
+ if t.isCompound {
+ b |= 0x20
+ }
+ if t.tag >= 31 {
+ b |= 0x1f;
+ err = out.WriteByte(b);
+ if err != nil {
+ return
+ }
+ err = marshalBase128Int(out, int64(t.tag));
+ if err != nil {
+ return
+ }
+ } else {
+ b |= uint8(t.tag);
+ err = out.WriteByte(b);
+ if err != nil {
+ return
+ }
+ }
+
+ if t.length >= 128 {
+ err = out.WriteByte(byte(base128Length(t.length)));
+ if err != nil {
+ return
+ }
+ err = marshalBase128Int(out, int64(t.length));
+ if err != nil {
+ return
+ }
+ } else {
+ err = out.WriteByte(byte(t.length));
+ if err != nil {
+ return
+ }
+ }
+
+ return nil;
+}
+
+func marshalBitString(out *forkableWriter, b BitString) (err os.Error) {
+ paddingBits := byte((8 - b.BitLength%8) % 8);
+ err = out.WriteByte(paddingBits);
+ if err != nil {
+ return
+ }
+ _, err = out.Write(b.Bytes);
+ return;
+}
+
+func marshalObjectIdentifier(out *forkableWriter, oid []int) (err os.Error) {
+ if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 {
+ return StructuralError{"invalid object identifier"}
+ }
+
+ err = out.WriteByte(byte(oid[0]*40 + oid[1]));
+ if err != nil {
+ return
+ }
+ for i := 2; i < len(oid); i++ {
+ err = marshalBase128Int(out, int64(oid[i]));
+ if err != nil {
+ return
+ }
+ }
+
+ return;
+}
+
+func marshalPrintableString(out *forkableWriter, s string) (err os.Error) {
+ b := strings.Bytes(s);
+ for _, c := range b {
+ if !isPrintable(c) {
+ return StructuralError{"PrintableString contains invalid character"}
+ }
+ }
+
+ _, err = out.Write(b);
+ return;
+}
+
+func marshalIA5String(out *forkableWriter, s string) (err os.Error) {
+ b := strings.Bytes(s);
+ for _, c := range b {
+ if c > 127 {
+ return StructuralError{"IA5String contains invalid character"}
+ }
+ }
+
+ _, err = out.Write(b);
+ return;
+}
+
+func marshalTwoDigits(out *forkableWriter, v int) (err os.Error) {
+ err = out.WriteByte(byte('0' + (v/10)%10));
+ if err != nil {
+ return
+ }
+ return out.WriteByte(byte('0' + v%10));
+}
+
+func marshalUTCTime(out *forkableWriter, t *time.Time) (err os.Error) {
+ switch {
+ case 1950 <= t.Year && t.Year < 2000:
+ err = marshalTwoDigits(out, int(t.Year-1900))
+ case 2000 <= t.Year && t.Year < 2050:
+ err = marshalTwoDigits(out, int(t.Year-2000))
+ default:
+ return StructuralError{"Cannot represent time as UTCTime"}
+ }
+
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, t.Month);
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, t.Day);
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, t.Hour);
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, t.Minute);
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, t.Second);
+ if err != nil {
+ return
+ }
+
+ switch {
+ case t.ZoneOffset/60 == 0:
+ err = out.WriteByte('Z');
+ return;
+ case t.ZoneOffset > 0:
+ err = out.WriteByte('+')
+ case t.ZoneOffset < 0:
+ err = out.WriteByte('-')
+ }
+
+ if err != nil {
+ return
+ }
+
+ offsetMinutes := t.ZoneOffset / 60;
+ if offsetMinutes < 0 {
+ offsetMinutes = -offsetMinutes
+ }
+
+ err = marshalTwoDigits(out, offsetMinutes/60);
+ if err != nil {
+ return
+ }
+
+ err = marshalTwoDigits(out, offsetMinutes%60);
+ return;
+}
+
+func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err os.Error) {
+ switch value.Type() {
+ case timeType:
+ return marshalUTCTime(out, value.Interface().(*time.Time))
+ case bitStringType:
+ return marshalBitString(out, value.Interface().(BitString))
+ case objectIdentifierType:
+ return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
+ }
+
+ switch v := value.(type) {
+ case *reflect.BoolValue:
+ if v.Get() {
+ return out.WriteByte(1)
+ } else {
+ return out.WriteByte(0)
+ }
+ case *reflect.IntValue:
+ return marshalBase128Int(out, int64(v.Get()))
+ case *reflect.Int64Value:
+ return marshalBase128Int(out, v.Get())
+ case *reflect.StructValue:
+ t := v.Type().(*reflect.StructType);
+ for i := 0; i < t.NumField(); i++ {
+ err = marshalField(out, v.Field(i), parseFieldParameters(t.Field(i).Tag));
+ if err != nil {
+ return
+ }
+ }
+ return;
+ case *reflect.SliceValue:
+ sliceType := v.Type().(*reflect.SliceType);
+ if _, ok := sliceType.Elem().(*reflect.Uint8Type); ok {
+ bytes := make([]byte, v.Len());
+ for i := 0; i < v.Len(); i++ {
+ bytes[i] = v.Elem(i).(*reflect.Uint8Value).Get()
+ }
+ _, err = out.Write(bytes);
+ return;
+ }
+
+ var params fieldParameters;
+ for i := 0; i < v.Len(); i++ {
+ err = marshalField(out, v.Elem(i), params);
+ if err != nil {
+ return
+ }
+ }
+ return;
+ case *reflect.StringValue:
+ if params.stringType == tagIA5String {
+ return marshalIA5String(out, v.Get())
+ } else {
+ return marshalPrintableString(out, v.Get())
+ }
+ return;
+ }
+
+ return StructuralError{"unknown Go type"};
+}
+
+func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err os.Error) {
+ tag, isCompound, ok := getUniversalType(v.Type());
+ if !ok {
+ err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())};
+ return;
+ }
+ class := classUniversal;
+
+ if params.stringType != 0 {
+ if tag != tagPrintableString {
+ return StructuralError{"Explicit string type given to non-string member"}
+ }
+ tag = params.stringType;
+ }
+
+ tags, body := out.fork();
+
+ err = marshalBody(body, v, params);
+ if err != nil {
+ return
+ }
+
+ bodyLen := body.Len();
+
+ var explicitTag *forkableWriter;
+ if params.explicit {
+ explicitTag, tags = tags.fork()
+ }
+
+ if !params.explicit && params.tag != nil {
+ // implicit tag.
+ tag = *params.tag;
+ class = classContextSpecific;
+ }
+
+ err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound});
+ if err != nil {
+ return
+ }
+
+ if params.explicit {
+ err = marshalTagAndLength(explicitTag, tagAndLength{
+ class: classContextSpecific,
+ tag: *params.tag,
+ length: bodyLen + tags.Len(),
+ isCompound: true,
+ })
+ }
+
+ 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 {
+ v := reflect.NewValue(val);
+ f := newForkableWriter();
+ err := marshalField(f, v, fieldParameters{});
+ if err != nil {
+ return err
+ }
+ _, err = f.writeTo(out);
+ return err;
+}