236 lines
4.8 KiB
Go
236 lines
4.8 KiB
Go
package pkcs7
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
var encodeIndent = 0
|
|
|
|
type asn1Object interface {
|
|
EncodeTo(writer *bytes.Buffer) error
|
|
TagBytes() []byte
|
|
}
|
|
|
|
type asn1Structured struct {
|
|
tagBytes []byte
|
|
content []asn1Object
|
|
}
|
|
|
|
func (s asn1Structured) TagBytes() []byte {
|
|
return s.tagBytes
|
|
}
|
|
|
|
func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
|
|
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
|
|
encodeIndent++
|
|
inner := new(bytes.Buffer)
|
|
for _, obj := range s.content {
|
|
err := obj.EncodeTo(inner)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
encodeIndent--
|
|
out.Write(s.tagBytes)
|
|
encodeLength(out, inner.Len())
|
|
out.Write(inner.Bytes())
|
|
return nil
|
|
}
|
|
|
|
type asn1Primitive struct {
|
|
tagBytes []byte
|
|
length int
|
|
content []byte
|
|
}
|
|
|
|
func (s asn1Primitive) TagBytes() []byte {
|
|
return s.tagBytes
|
|
}
|
|
|
|
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
|
|
_, err := out.Write(p.tagBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = encodeLength(out, p.length); err != nil {
|
|
return err
|
|
}
|
|
//fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
|
|
//fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
|
|
out.Write(p.content)
|
|
|
|
return nil
|
|
}
|
|
|
|
func ber2der(data []byte) ([]byte, error) {
|
|
out := new(bytes.Buffer)
|
|
|
|
obj, err := readObject(bytes.NewReader(data))
|
|
if err != nil && err != io.EOF {
|
|
return nil, err
|
|
}
|
|
if obj == nil {
|
|
return nil, fmt.Errorf("error to parse BER")
|
|
}
|
|
obj.EncodeTo(out)
|
|
|
|
return out.Bytes(), nil
|
|
}
|
|
|
|
// encodes lengths that are longer than 127 into string of bytes
|
|
func marshalLongLength(out *bytes.Buffer, i int) (err error) {
|
|
n := lengthLength(i)
|
|
|
|
for ; n > 0; n-- {
|
|
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// computes the byte length of an encoded length value
|
|
func lengthLength(i int) (numBytes int) {
|
|
numBytes = 1
|
|
for i > 255 {
|
|
numBytes++
|
|
i >>= 8
|
|
}
|
|
return
|
|
}
|
|
|
|
// encodes the length in DER format
|
|
// If the length fits in 7 bits, the value is encoded directly.
|
|
//
|
|
// Otherwise, the number of bytes to encode the length is first determined.
|
|
// This number is likely to be 4 or less for a 32bit length. This number is
|
|
// added to 0x80. The length is encoded in big endian encoding follow after
|
|
//
|
|
// Examples:
|
|
// length | byte 1 | bytes n
|
|
// 0 | 0x00 | -
|
|
// 120 | 0x78 | -
|
|
// 200 | 0x81 | 0xC8
|
|
// 500 | 0x82 | 0x01 0xF4
|
|
//
|
|
func encodeLength(out *bytes.Buffer, length int) (err error) {
|
|
if length >= 128 {
|
|
l := lengthLength(length)
|
|
err = out.WriteByte(0x80 | byte(l))
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = marshalLongLength(out, length)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
err = out.WriteByte(byte(length))
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func readObject(r *bytes.Reader) (obj asn1Object, err error) {
|
|
var tagB byte
|
|
if tagB, err = r.ReadByte(); err != nil {
|
|
return
|
|
}
|
|
|
|
primitive := tagB&0x20 == 0
|
|
|
|
var l byte
|
|
if l, err = r.ReadByte(); err != nil {
|
|
return nil, fmt.Errorf("end of ber data reached")
|
|
}
|
|
length := (int)(l & 0x7F)
|
|
if l > 0x80 {
|
|
numberOfBytes := length
|
|
length = 0
|
|
if numberOfBytes > 4 { // int is only guaranteed to be 32bit
|
|
return nil, errors.New("ber2der: BER tag length too long")
|
|
}
|
|
for i := 0; i < numberOfBytes; i++ {
|
|
var sl byte
|
|
if sl, err = r.ReadByte(); err != nil {
|
|
return nil, fmt.Errorf("length is more than available data")
|
|
}
|
|
if i == 0 {
|
|
if numberOfBytes == 4 && (int)(sl) > 0x7F {
|
|
return nil, errors.New("ber2der: BER tag length is negative")
|
|
}
|
|
if 0x0 == (int)(sl) {
|
|
return nil, errors.New("ber2der: BER tag length has leading zero")
|
|
}
|
|
}
|
|
length = length*256 + (int)(sl)
|
|
}
|
|
}
|
|
|
|
if primitive {
|
|
p := asn1Primitive{
|
|
tagBytes: []byte{tagB},
|
|
length: length,
|
|
content: make([]byte, length),
|
|
}
|
|
if length > 0 {
|
|
_, err = r.Read(p.content)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Invalid BER format")
|
|
}
|
|
obj = p
|
|
return
|
|
} else {
|
|
subObjects := make([]asn1Object, 0)
|
|
var sobj asn1Object
|
|
if length > 0 {
|
|
content := make([]byte, length)
|
|
n, err := r.Read(content)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != length {
|
|
return nil, fmt.Errorf("length is more than available data")
|
|
}
|
|
r = bytes.NewReader(content)
|
|
}
|
|
for {
|
|
sobj, err = readObject(r)
|
|
if err != nil && err != io.EOF {
|
|
return
|
|
}
|
|
if err == io.EOF && length >= 0 {
|
|
err = nil
|
|
break
|
|
}
|
|
if sobj == nil {
|
|
break
|
|
}
|
|
if po, ok := sobj.(asn1Primitive); ok {
|
|
if po.length == 0 && po.tagBytes[0] == 0 && length == 0 {
|
|
break
|
|
}
|
|
}
|
|
subObjects = append(subObjects, sobj)
|
|
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
obj = asn1Structured{
|
|
tagBytes: []byte{tagB},
|
|
content: subObjects,
|
|
}
|
|
return
|
|
}
|
|
return nil, io.EOF
|
|
}
|