Skip to content
This repository has been archived by the owner on Aug 18, 2023. It is now read-only.

ber.go: Protocol Validation and Defense #3

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 16 additions & 18 deletions ber.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"errors"
)

var encodeIndent = 0

type asn1Object interface {
EncodeTo(writer *bytes.Buffer) error
}
Expand All @@ -18,15 +16,13 @@ type asn1Structured struct {

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())
Expand Down Expand Up @@ -67,10 +63,6 @@ func ber2der(ber []byte) ([]byte, error) {
}
obj.EncodeTo(out)

// if offset < len(ber) {
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
//}

return out.Bytes(), nil
}

Expand Down Expand Up @@ -149,14 +141,14 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) {
for ber[offset] >= 0x80 {
tag = tag*128 + ber[offset] - 0x80
offset++
if offset > berLen {
if offset >= berLen {
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
}
}
// jvehent 20170227: this doesn't appear to be used anywhere...
//tag = tag*128 + ber[offset] - 0x80
offset++
if offset > berLen {
if offset >= berLen {
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
}
}
Expand All @@ -172,7 +164,10 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) {
var length int
l := ber[offset]
offset++
if offset > berLen {
if l >= 0x80 && offset >= berLen {
// if indefinite or multibyte length, we need to verify there is at least one more byte available
// otherwise we need to be flexible here for length == 0 conditions
// validation that the length is available is done after the length is correctly parsed
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
}
indefinite := false
Expand All @@ -184,17 +179,20 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) {
if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
return nil, 0, errors.New("ber2der: BER tag length is negative")
}
if (int)(ber[offset]) == 0x0 {
if offset + numberOfBytes > berLen {
// == condition is not checked here, this allows for a more descreptive error when the parsed length is
// compared with the remaining available bytes (`contentEnd > berLen`)
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
}
if (int)(ber[offset]) == 0x0 && (numberOfBytes == 1 || ber[offset+1] <= 0x7F) {
// `numberOfBytes == 1` is an important conditional to avoid a potential out of bounds panic with `ber[offset+1]`
return nil, 0, errors.New("ber2der: BER tag length has leading zero")
}
debugprint("--> (compute length) indicator byte: %x\n", l)
debugprint("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
//debugprint("--> (compute length) length bytes: %x\n", ber[offset:offset+numberOfBytes])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was keeping this commented out on purpose. The debugprint does no action without manual code changes, and I feel like the slice index lookup is not worth general usage. What do you think?

for i := 0; i < numberOfBytes; i++ {
length = length*256 + (int)(ber[offset])
offset++
if offset > berLen {
return nil, 0, errors.New("ber2der: cannot move offset forward, end of ber data reached")
}
}
} else if l == 0x80 {
indefinite = true
Expand All @@ -206,12 +204,12 @@ func readObject(ber []byte, offset int) (asn1Object, int, error) {
}
//fmt.Printf("--> length : %d\n", length)
contentEnd := offset + length
if contentEnd > len(ber) {
if contentEnd > berLen {
return nil, 0, errors.New("ber2der: BER tag length is more than available data")
}
debugprint("--> content start : %d\n", offset)
debugprint("--> content end : %d\n", contentEnd)
debugprint("--> content : % X\n", ber[offset:contentEnd])
//debugprint("--> content : %x\n", ber[offset:contentEnd])
var obj asn1Object
if indefinite && kind == 0 {
return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
Expand Down
7 changes: 6 additions & 1 deletion ber_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ func TestBer2Der_Negatives(t *testing.T) {
Input []byte
ErrorContains string
}{
{[]byte{0x30, 0x85}, "tag length too long"},
{[]byte{}, "input ber is empty"},
{[]byte{0x30}, "cannot move offset forward, end of ber data reached"},
{[]byte{0x30, 0x08}, "BER tag length is more than available dat"},
{[]byte{0x30, 0x81}, "cannot move offset forward, end of ber data reached"},
{[]byte{0x30, 0x81, 0x00}, "BER tag length has leading zero"},
{[]byte{0x30, 0x85, 0x00}, "tag length too long"},
{[]byte{0x30, 0x84, 0x80, 0x0, 0x0, 0x0}, "length is negative"},
{[]byte{0x30, 0x82, 0x0, 0x1}, "length has leading zero"},
{[]byte{0x30, 0x80, 0x1, 0x2, 0x1, 0x2}, "Invalid BER format"},
Expand Down