blob: 6d1f78bcc10b13fbbde55cf31ae64913bed3d911 [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 (
"big"
"bytes"
"fmt"
"io"
"os"
"reflect"
"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, n int64) (err os.Error) {
if n == 0 {
err = out.WriteByte(0)
return
}
l := 0
for i := n; i > 0; i >>= 7 {
l++
}
for i := l - 1; i >= 0; i-- {
o := byte(n >> uint(i*7))
o &= 0x7f
if i != 0 {
o |= 0x80
}
err = out.WriteByte(o)
if err != nil {
return
}
}
return nil
}
func marshalInt64(out *forkableWriter, i int64) (err os.Error) {
n := int64Length(i)
for ; n > 0; n-- {
err = out.WriteByte(byte(i >> uint((n-1)*8)))
if err != nil {
return
}
}
return nil
}
func int64Length(i int64) (numBytes int) {
numBytes = 1
for i > 127 {
numBytes++
i >>= 8
}
for i < -128 {
numBytes++
i >>= 8
}
return
}
func marshalBigInt(out *forkableWriter, n *big.Int) (err os.Error) {
if n.Sign() < 0 {
// A negative number has to be converted to two's-complement
// form. So we'll subtract 1 and invert. If the
// most-significant-bit isn't set then we'll need to pad the
// beginning with 0xff in order to keep the number negative.
nMinus1 := new(big.Int).Neg(n)
nMinus1.Sub(nMinus1, bigOne)
bytes := nMinus1.Bytes()
for i := range bytes {
bytes[i] ^= 0xff
}
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
err = out.WriteByte(0xff)
if err != nil {
return
}
}
_, err = out.Write(bytes)
} else if n.Sign() == 0 {
// Zero is written as a single 0 zero rather than no bytes.
err = out.WriteByte(0x00)
} else {
bytes := n.Bytes()
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
// We'll have to pad this with 0x00 in order to stop it
// looking like a negative number.
err = out.WriteByte(0)
if err != nil {
return
}
}
_, err = out.Write(bytes)
}
return
}
func marshalLength(out *forkableWriter, i int) (err os.Error) {
n := lengthLength(i)
for ; n > 0; n-- {
err = out.WriteByte(byte(i >> uint((n-1)*8)))
if err != nil {
return
}
}
return nil
}
func lengthLength(i int) (numBytes int) {
numBytes = 1
for i > 255 {
numBytes++
i >>= 8
}
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 {
l := lengthLength(t.length)
err = out.WriteByte(0x80 | byte(l))
if err != nil {
return
}
err = marshalLength(out, 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 := []byte(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 := []byte(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 stripTagAndLength(in []byte) []byte {
_, offset, err := parseTagAndLength(in, 0)
if err != nil {
return in
}
return in[offset:]
}
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))
case bigIntType:
return marshalBigInt(out, value.Interface().(*big.Int))
}
switch v := value; v.Kind() {
case reflect.Bool:
if v.Bool() {
return out.WriteByte(255)
} else {
return out.WriteByte(0)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return marshalInt64(out, int64(v.Int()))
case reflect.Struct:
t := v.Type()
startingField := 0
// If the first element of the structure is a non-empty
// RawContents, then we don't bother serializing the rest.
if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
s := v.Field(0)
if s.Len() > 0 {
bytes := make([]byte, s.Len())
for i := 0; i < s.Len(); i++ {
bytes[i] = uint8(s.Index(i).Uint())
}
/* The RawContents will contain the tag and
* length fields but we'll also be writing
* those ourselves, so we strip them out of
* bytes */
_, err = out.Write(stripTagAndLength(bytes))
return
} else {
startingField = 1
}
}
for i := startingField; i < t.NumField(); i++ {
var pre *forkableWriter
pre, out = out.fork()
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
if err != nil {
return
}
}
return
case reflect.Slice:
sliceType := v.Type()
if sliceType.Elem().Kind() == reflect.Uint8 {
bytes := make([]byte, v.Len())
for i := 0; i < v.Len(); i++ {
bytes[i] = uint8(v.Index(i).Uint())
}
_, err = out.Write(bytes)
return
}
var params fieldParameters
for i := 0; i < v.Len(); i++ {
var pre *forkableWriter
pre, out = out.fork()
err = marshalField(pre, v.Index(i), params)
if err != nil {
return
}
}
return
case reflect.String:
if params.stringType == tagIA5String {
return marshalIA5String(out, v.String())
} else {
return marshalPrintableString(out, v.String())
}
return
}
return StructuralError{"unknown Go type"}
}
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err os.Error) {
// If the field is an interface{} then recurse into it.
if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
return marshalField(out, v.Elem(), params)
}
if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
return
}
if v.Type() == rawValueType {
rv := v.Interface().(RawValue)
if len(rv.FullBytes) != 0 {
_, err = out.Write(rv.FullBytes)
} else {
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
if err != nil {
return
}
_, err = out.Write(rv.Bytes)
}
return
}
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
}
if params.set {
if tag != tagSequence {
return StructuralError{"Non sequence tagged as set"}
}
tag = tagSet
}
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 returns the ASN.1 encoding of val.
func Marshal(val interface{}) ([]byte, os.Error) {
var out bytes.Buffer
v := reflect.ValueOf(val)
f := newForkableWriter()
err := marshalField(f, v, fieldParameters{})
if err != nil {
return nil, err
}
_, err = f.writeTo(&out)
return out.Bytes(), nil
}