blob: 40a52b6624b68bca62fd029cc7e01729ce38d48f [file] [log] [blame]
// 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) {
if f.pre != nil || f.post != nil {
panic("have already forked")
}
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.post != 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++ {
var pre *forkableWriter
pre, out = out.fork()
err = marshalField(pre, 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
}