dt_automate/vendor/github.com/unidoc/pkcs7/ber.go
2025-02-19 18:30:19 +08:00

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
}