Skip to content

Commit

Permalink
fix: remove recusive decoding
Browse files Browse the repository at this point in the history
Signed-off-by: Junjie Gao <[email protected]>
  • Loading branch information
JeyJeyGao committed Aug 2, 2023
1 parent 4fd944a commit 936ba2b
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 59 deletions.
126 changes: 102 additions & 24 deletions internal/encoding/asn1/asn1.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (

// Common errors
var (
ErrBytesAtTheEnd = asn1.StructuralError{Msg: "invalid bytes at the end of the BER data"}
ErrEarlyEOF = asn1.SyntaxError{Msg: "early EOF"}
ErrInvalidBerData = asn1.StructuralError{Msg: "invalid BER data"}
ErrInvalidOffset = asn1.StructuralError{Msg: "invalid offset"}
ErrInvalidSlice = asn1.StructuralError{Msg: "invalid slice"}
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.
Expand All @@ -28,7 +30,7 @@ type value interface {

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

// decode decodes BER-encoded ASN.1 data structures.
func decode(r []byte) (value, []byte, error) {
identifier, r, err := decodeIdentifier(r)
func decode(r []byte) (value, error) {
var (
identifier []byte
contentLen int
berValueLen int
isPrimitive bool
err error
)
// prepare the first value
identifier, contentLen, _, isPrimitive, r, err = decodeMetadata(r)
if err != nil {
return nil, nil, err
}
contentLength, r, err := decodeLength(r)
if err != nil {
return nil, nil, err
}

if contentLength > len(r) {
return nil, nil, ErrEarlyEOF
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 contentLen != len(r) {
return nil, ErrBytesAtTheEnd
}
return primitiveValue{
identifier: identifier,
content: r[:contentLen],
}, nil
}

// constructed value
v, err := newConstructedValue(identifier, content)
rootConstructed := constructedValue{
identifier: identifier,
expectedLen: contentLen,
}

// start deep-first decoding with stack
valueStack := []*constructedValue{&rootConstructed}
for len(valueStack) > 0 {
stackLen := len(valueStack)
// top
v := valueStack[stackLen-1]

if v.expectedLen < 0 {
return nil, ErrInvalidBerData
}

if v.expectedLen == 0 {
// calculate the length of the constructed value
for _, m := range v.members {
v.length += m.EncodedLen()
}

// pop the constructued value
valueStack = valueStack[:stackLen-1]
break
}

for v.expectedLen > 0 {
identifier, contentLen, berValueLen, isPrimitive, r, err = decodeMetadata(r)
if err != nil {
return nil, err
}
if isPrimitive {
// primitive value
pv := primitiveValue{
identifier: identifier,
content: r[:contentLen],
}
r = r[contentLen:]
v.expectedLen -= berValueLen
v.members = append(v.members, &pv)
} else {
// constructed value
cv := constructedValue{
identifier: identifier,
expectedLen: contentLen,
}
v.expectedLen -= berValueLen
v.members = append(v.members, &cv)
valueStack = append(valueStack, &cv)
// break to start decoding the new constructed value
break
}
}
}
if len(r) > 0 {
return nil, ErrBytesAtTheEnd
}
return rootConstructed, nil
}

func decodeMetadata(r []byte) ([]byte, int, int, bool, []byte, error) {
length := len(r)
identifier, r, err := decodeIdentifier(r)
if err != nil {
return nil, 0, 0, false, nil, err
}
contentLen, r, err := decodeLen(r)
if err != nil {
return nil, nil, err
return nil, 0, 0, false, nil, err
}

if contentLen > len(r) {
return nil, 0, 0, false, nil, ErrEarlyEOF
}
return v, r, nil
isPrimitive := identifier[0]&0x20 == 0
metadataLen := length - len(r)
berValueLen := metadataLen + contentLen
return identifier, contentLen, berValueLen, isPrimitive, r, nil
}

// decodeIdentifier decodes decodeIdentifier octets.
Expand All @@ -88,9 +166,9 @@ func decodeIdentifier(r []byte) ([]byte, []byte, error) {
return r[:offset], r[offset:], nil
}

// decodeLength decodes length octets.
// decodeLen decodes length octets.
// Indefinite length is not supported
func decodeLength(r []byte) (int, []byte, error) {
func decodeLen(r []byte) (int, []byte, error) {
offset := 0
if len(r) < 1 {
return 0, nil, ErrEarlyEOF
Expand Down
31 changes: 4 additions & 27 deletions internal/encoding/asn1/constructed.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,10 @@ import "bytes"

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

// newConstructedValue builds the constructed value.
func newConstructedValue(identifier []byte, content []byte) (value, error) {
var (
members []value
value value
err error
)
encodedLength := 0
for len(content) > 0 {
value, content, err = decode(content)
if err != nil {
return nil, err
}
members = append(members, value)
encodedLength += value.EncodedLen()
}

return constructedValue{
identifier: identifier,
length: encodedLength,
members: members,
}, nil
identifier []byte
expectedLen int
length int
members []value
}

// Encode encodes the constructed value to the value writer in DER.
Expand Down
8 changes: 0 additions & 8 deletions internal/encoding/asn1/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ type primitiveValue struct {
content []byte
}

// newPrimitiveValue builds the primitive 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 *bytes.Buffer) error {
_, err := w.Write(v.identifier)
Expand Down

0 comments on commit 936ba2b

Please sign in to comment.