170 lines
2.6 KiB
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()
|
|
}
|