// 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; }