Skip to content

Commit

Permalink
fix: remove readOnlySlice
Browse files Browse the repository at this point in the history
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
  • Loading branch information
JeyJeyGao committed Aug 1, 2023
1 parent efa7575 commit 4fd944a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 174 deletions.
106 changes: 51 additions & 55 deletions internal/encoding/asn1/asn1.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,29 @@ package asn1
import (
"bytes"
"encoding/asn1"
"io"
)

// Common errors
var (
ErrEarlyEOF = asn1.SyntaxError{Msg: "early EOF"}
ErrUnsupportedLength = asn1.StructuralError{Msg: "length method not supported"}
ErrUnsupportedIndefinedLenth = asn1.StructuralError{Msg: "indefinite length not supported"}
ErrInvalidSlice = asn1.StructuralError{Msg: "invalid slice"}
ErrInvalidOffset = asn1.StructuralError{Msg: "invalid offset"}
ErrEarlyEOF = asn1.SyntaxError{Msg: "early EOF"}
ErrUnsupportedLen = asn1.StructuralError{Msg: "length method not supported"}
ErrUnsupportedIndefinedLen = asn1.StructuralError{Msg: "indefinite length not supported"}
ErrInvalidSlice = asn1.StructuralError{Msg: "invalid slice"}
ErrInvalidOffset = asn1.StructuralError{Msg: "invalid offset"}
)

// value represents an ASN.1 value.
type value interface {
// Encode encodes the value to the value writer in DER.
Encode(valueWriter) error
Encode(*bytes.Buffer) error

// EncodedLen returns the length in bytes of the encoded data.
EncodedLen() int
}

// ConvertToDER converts BER-encoded ASN.1 data structures to DER-encoded.
func ConvertToDER(ber []byte) ([]byte, error) {
v, err := decode(newReadOnlySlice(ber))
v, _, err := decode(ber)
if err != nil {
return nil, err
}
Expand All @@ -41,92 +40,89 @@ func ConvertToDER(ber []byte) ([]byte, error) {
}

// decode decodes BER-encoded ASN.1 data structures.
func decode(r readOnlySlice) (value, error) {
identifier, isPrimitiveValue, err := decodeIdentifier(r)
func decode(r []byte) (value, []byte, error) {
identifier, r, err := decodeIdentifier(r)
if err != nil {
return nil, err
return nil, nil, err
}
contentLength, err := decodeLength(r)
contentLength, r, err := decodeLength(r)
if err != nil {
return nil, err
return nil, nil, err
}
content, err := r.Slice(r.Offset(), r.Offset()+contentLength)
if err != nil {
return nil, err

if contentLength > len(r) {
return nil, nil, ErrEarlyEOF
}
if err = r.Seek(r.Offset() + contentLength); err != nil {
return nil, err
content := r[:contentLength]
r = r[contentLength:]

isPrimitive := identifier[0]&0x20 == 0
// primitive value
if isPrimitive {
return newPrimitiveValue(identifier, content), r, nil
}

if isPrimitiveValue {
return newPrimitiveValue(identifier, content), nil
// constructed value
v, err := newConstructedValue(identifier, content)
if err != nil {
return nil, nil, err
}
return newConstructedValue(identifier, content)
return v, r, nil
}

// decodeIdentifier decodes decodeIdentifier octets.
func decodeIdentifier(r readOnlySlice) (readOnlySlice, bool, error) {
b, err := r.ReadByte()
if err != nil {
return nil, false, err
func decodeIdentifier(r []byte) ([]byte, []byte, error) {
offset := 0
if len(r) < 1 {
return nil, nil, ErrEarlyEOF
}
isPrimitive := b&0x20 == 0
b := r[offset]
offset++

tagBytesCount := 1
// high-tag-number form
if b&0x1f == 0x1f {
for {
b, err = r.ReadByte()
if err != nil {
return nil, false, err
}
tagBytesCount++
if b&0x80 != 0 {
break
}
for offset < len(r) && r[offset]&0x80 == 0x80 {
offset++
}
}

identifier, err := r.Slice(r.Offset()-tagBytesCount, r.Offset())
if err != nil {
return nil, false, err
}
return identifier, isPrimitive, nil
return r[:offset], r[offset:], nil
}

// decodeLength decodes length octets.
// Indefinite length is not supported
func decodeLength(r io.ByteReader) (int, error) {
b, err := r.ReadByte()
if err != nil {
return 0, err
func decodeLength(r []byte) (int, []byte, error) {
offset := 0
if len(r) < 1 {
return 0, nil, ErrEarlyEOF
}
b := r[offset]
offset++
switch {
case b < 0x80:
// short form
return int(b), nil
return int(b), r[offset:], nil
case b == 0x80:
// Indefinite-length method is not supported.
return 0, ErrUnsupportedIndefinedLenth
return 0, nil, ErrUnsupportedIndefinedLen
}

// long form
n := int(b & 0x7f)
if n > 4 {
// length must fit the memory space of the int type.
return 0, ErrUnsupportedLength
return 0, nil, ErrUnsupportedLen
}
var length int
for i := 0; i < n; i++ {
b, err = r.ReadByte()
if err != nil {
return 0, err
if offset >= len(r) {
return 0, nil, ErrEarlyEOF
}
length = (length << 8) | int(b)
length = (length << 8) | int(r[offset])
offset++
}
if length < 0 {
// double check in case that length is over 31 bits.
return 0, ErrUnsupportedLength
return 0, nil, ErrUnsupportedLen
}
return length, nil
return length, r[offset:], nil
}
14 changes: 8 additions & 6 deletions internal/encoding/asn1/common.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package asn1

import "io"
import (
"bytes"
)

// encodeLength encodes length octets in DER.
func encodeLength(w io.ByteWriter, length int) error {
// encodeLen encodes length octets in DER.
func encodeLen(w *bytes.Buffer, length int) error {
// DER restriction: short form must be used for length less than 128
if length < 0x80 {
return w.WriteByte(byte(length))
}

// DER restriction: long form must be encoded in the minimum number of octets
lengthSize := encodedLengthSize(length)
lengthSize := encodedLenSize(length)
err := w.WriteByte(0x80 | byte(lengthSize-1))
if err != nil {
return err
Expand All @@ -23,8 +25,8 @@ func encodeLength(w io.ByteWriter, length int) error {
return nil
}

// encodedLengthSize gives the number of octets used for encoding the length.
func encodedLengthSize(length int) int {
// encodedLenSize gives the number of octets used for encoding the length.
func encodedLenSize(length int) int {
if length < 0x80 {
return 1
}
Expand Down
24 changes: 15 additions & 9 deletions internal/encoding/asn1/constructed.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package asn1

import "bytes"

// constructedValue represents a value in constructed encoding.
type constructedValue struct {
identifier readOnlySlice
identifier []byte
length int
members []value
}

// newConstructedValue builds the constructed value.
func newConstructedValue(identifier readOnlySlice, content readOnlySlice) (value, error) {
var members []value
func newConstructedValue(identifier []byte, content []byte) (value, error) {
var (
members []value
value value
err error
)
encodedLength := 0
for content.Offset() < content.Length() {
value, err := decode(content)
for len(content) > 0 {
value, content, err = decode(content)
if err != nil {
return nil, err
}
Expand All @@ -28,12 +34,12 @@ func newConstructedValue(identifier readOnlySlice, content readOnlySlice) (value
}

// Encode encodes the constructed value to the value writer in DER.
func (v constructedValue) Encode(w valueWriter) error {
_, err := w.ReadFrom(v.identifier)
func (v constructedValue) Encode(w *bytes.Buffer) error {
_, err := w.Write(v.identifier)
if err != nil {
return err
}
if err = encodeLength(w, v.length); err != nil {
if err = encodeLen(w, v.length); err != nil {
return err
}
for _, value := range v.members {
Expand All @@ -46,5 +52,5 @@ func (v constructedValue) Encode(w valueWriter) error {

// EncodedLen returns the length in bytes of the encoded data.
func (v constructedValue) EncodedLen() int {
return v.identifier.Length() + encodedLengthSize(v.length) + v.length
return len(v.identifier) + encodedLenSize(v.length) + v.length
}
9 changes: 0 additions & 9 deletions internal/encoding/asn1/io.go

This file was deleted.

18 changes: 10 additions & 8 deletions internal/encoding/asn1/primitive.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
package asn1

import "bytes"

// primitiveValue represents a value in primitive encoding.
type primitiveValue struct {
identifier readOnlySlice
content readOnlySlice
identifier []byte
content []byte
}

// newPrimitiveValue builds the primitive value.
func newPrimitiveValue(identifier readOnlySlice, content readOnlySlice) value {
func newPrimitiveValue(identifier []byte, content []byte) value {
return primitiveValue{
identifier: identifier,
content: content,
}
}

// Encode encodes the primitive value to the value writer in DER.
func (v primitiveValue) Encode(w valueWriter) error {
_, err := w.ReadFrom(v.identifier)
func (v primitiveValue) Encode(w *bytes.Buffer) error {
_, err := w.Write(v.identifier)
if err != nil {
return err
}
if err = encodeLength(w, v.content.Length()); err != nil {
if err = encodeLen(w, len(v.content)); err != nil {
return err
}
_, err = w.ReadFrom(v.content)
_, err = w.Write(v.content)
return err
}

// EncodedLen returns the length in bytes of the encoded data.
func (v primitiveValue) EncodedLen() int {
return v.identifier.Length() + encodedLengthSize(v.content.Length()) + v.content.Length()
return len(v.identifier) + encodedLenSize(len(v.content)) + len(v.content)
}
Loading

0 comments on commit 4fd944a

Please # to comment.