keycard-go/types/metadata.go

170 lines
2.6 KiB
Go

package types
import (
"bytes"
"container/list"
"errors"
"io"
"github.com/status-im/keycard-go/apdu"
)
type Metadata struct {
name string
paths *list.List
}
func EmptyMetadata() *Metadata {
return &Metadata{"", list.New()}
}
func NewMetadata(name string, paths []uint32) (*Metadata, error) {
m := EmptyMetadata()
if err := m.SetName(name); err != nil {
return nil, err
}
for i := 0; i < len(paths); i++ {
m.AddPath(paths[i])
}
return m, nil
}
func ParseMetadata(data []byte) (*Metadata, error) {
buf := bytes.NewBuffer(data)
header, err := buf.ReadByte()
if err != nil {
return nil, err
}
version := header >> 5
if version != 1 {
return nil, errors.New("invalid version")
}
namelen := int(header & 0x1f)
cardName := string(buf.Next(namelen))
list := list.New()
for {
start, err := apdu.ParseLength(buf)
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
count, err := apdu.ParseLength(buf)
if err != nil {
return nil, err
}
for i := start; i <= (start + count); i++ {
insertOrderedNoDups(list, i)
}
}
return &Metadata{cardName, list}, nil
}
func insertOrderedNoDups(list *list.List, num uint32) {
le := list.Back()
for le != nil {
val := le.Value.(uint32)
if num > val {
break
} else if num == val {
return
}
le = le.Prev()
}
if le == nil {
list.PushFront(num)
} else {
list.InsertAfter(num, le)
}
}
func (m *Metadata) Name() string {
return m.name
}
func (m *Metadata) SetName(name string) error {
if len(name) > 20 {
return errors.New("name longer than 20 chars")
}
m.name = name
return nil
}
func (m *Metadata) Paths() []uint32 {
listlen := m.paths.Len()
paths := make([]uint32, listlen)
e := m.paths.Front()
for i := 0; i < listlen; i++ {
paths[i] = e.Value.(uint32)
e = e.Next()
}
return paths
}
func (m *Metadata) AddPath(path uint32) {
insertOrderedNoDups(m.paths, path)
}
func (m *Metadata) RemovePath(path uint32) {
for le := m.paths.Front(); le != nil; le = le.Next() {
if path == le.Value.(uint32) {
m.paths.Remove(le)
return
}
}
}
func (m *Metadata) Serialize() []byte {
buf := new(bytes.Buffer)
buf.WriteByte(0x20 | byte(len(m.name)))
buf.WriteString(m.name)
le := m.paths.Front()
if le == nil {
return buf.Bytes()
}
start := le.Value.(uint32)
len := uint32(0)
for le = le.Next(); le != nil; le = le.Next() {
w := le.Value.(uint32)
if w == (start + len + 1) {
len++
} else {
apdu.WriteLength(buf, start)
apdu.WriteLength(buf, len)
start = w
len = 0
}
}
apdu.WriteLength(buf, start)
apdu.WriteLength(buf, len)
return buf.Bytes()
}