keycard-go/apdu/utils.go

80 lines
1.5 KiB
Go

package apdu
import (
"bytes"
"fmt"
"io"
)
// ErrTagNotFound is an error returned if a tag is not found in a TLV sequence.
type ErrTagNotFound struct {
tag uint8
}
// Error implements the error interface
func (e *ErrTagNotFound) Error() string {
return fmt.Sprintf("tag %x not found", e.tag)
}
// FindTag searches for a tag value within a TLV sequence.
func FindTag(raw []byte, tags ...uint8) ([]byte, error) {
return findTag(raw, 0, tags...)
}
// FindTagN searches for a tag value within a TLV sequence and returns the n occurrence
func FindTagN(raw []byte, n int, tags ...uint8) ([]byte, error) {
return findTag(raw, n, tags...)
}
func findTag(raw []byte, occurrence int, tags ...uint8) ([]byte, error) {
if len(tags) == 0 {
return raw, nil
}
target := tags[0]
buf := bytes.NewBuffer(raw)
var (
tag uint8
length uint8
err error
)
for {
tag, err = buf.ReadByte()
switch {
case err == io.EOF:
return []byte{}, &ErrTagNotFound{target}
case err != nil:
return nil, err
}
length, err = buf.ReadByte()
if err != nil {
return nil, err
}
data := make([]byte, length)
if length != 0 {
_, err = buf.Read(data)
if err != nil {
return nil, err
}
}
if tag == target {
// if it's the last tag in the search path, we start counting the occurrences
if len(tags) == 1 && occurrence > 0 {
occurrence--
continue
}
if len(tags) == 1 {
return data, nil
}
return findTag(data, occurrence, tags[1:]...)
}
}
}