Plan 9 from Bell Labs’s /usr/web/sources/contrib/ericvh/go-plan9/src/pkg/asn1/marshal.go

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.