keycard-go/derivationpath/derivationpath.go

215 lines
3.5 KiB
Go
Raw Normal View History

2019-03-15 00:01:57 +00:00
package derivationpath
2019-03-14 23:37:29 +00:00
import (
"fmt"
"io"
"strconv"
"strings"
)
type StartingPoint int
const (
tokenMaster = 0x6D // char m
tokenSeparator = 0x2F // char /
tokenHardened = 0x27 // char '
tokenDot = 0x2E // char .
hardenedStart = 0x80000000 // 2^31
)
const (
StartingPointMaster StartingPoint = iota + 1
StartingPointCurrent
StartingPointParent
)
type parseFunc = func() error
type parser struct {
r *strings.Reader
f parseFunc
pos int
path []uint32
start StartingPoint
currentToken string
currentTokenHardened bool
}
func newParser(path string) *parser {
p := &parser{
r: strings.NewReader(path),
}
p.reset()
return p
}
func (p *parser) reset() {
p.r.Seek(0, io.SeekStart)
p.pos = 0
p.start = StartingPointCurrent
p.f = p.parseStart
p.path = make([]uint32, 0)
p.resetCurrentToken()
}
func (p *parser) resetCurrentToken() {
p.currentToken = ""
p.currentTokenHardened = false
}
func (p *parser) parse() (StartingPoint, []uint32, error) {
for {
err := p.f()
if err != nil {
if err == io.EOF {
err = nil
} else {
err = fmt.Errorf("at position %d, %s", p.pos, err.Error())
}
return p.start, p.path, err
}
}
return p.start, p.path, nil
}
func (p *parser) readByte() (byte, error) {
b, err := p.r.ReadByte()
if err != nil {
return b, err
}
p.pos++
return b, nil
}
func (p *parser) unreadByte() error {
err := p.r.UnreadByte()
if err != nil {
return err
}
p.pos--
return nil
}
func (p *parser) parseStart() error {
b, err := p.readByte()
if err != nil {
return err
}
if b == tokenMaster {
p.start = StartingPointMaster
p.f = p.parseSeparator
return nil
}
if b == tokenDot {
b2, err := p.readByte()
if err != nil {
return err
}
if b2 == tokenDot {
p.f = p.parseSeparator
p.start = StartingPointParent
return nil
}
p.f = p.parseSeparator
p.start = StartingPointCurrent
return p.unreadByte()
}
p.f = p.parseSegment
return p.unreadByte()
}
func (p *parser) saveSegment() error {
if len(p.currentToken) > 0 {
i, err := strconv.ParseUint(p.currentToken, 10, 32)
if err != nil {
return err
}
if i >= hardenedStart {
p.pos -= len(p.currentToken) - 1
return fmt.Errorf("index must be lower than 2^31, got %d", i)
}
if p.currentTokenHardened {
i += hardenedStart
}
p.path = append(p.path, uint32(i))
}
p.f = p.parseSegment
p.resetCurrentToken()
return nil
}
func (p *parser) parseSeparator() error {
b, err := p.readByte()
if err != nil {
return err
}
if b == tokenSeparator {
return p.saveSegment()
}
return fmt.Errorf("expected %s, got %s", string(tokenSeparator), string(b))
}
func (p *parser) parseSegment() error {
b, err := p.readByte()
if err == io.EOF {
if len(p.currentToken) == 0 {
return fmt.Errorf("expected number, got EOF")
}
if newErr := p.saveSegment(); newErr != nil {
return newErr
}
return err
}
if err != nil {
return err
}
if len(p.currentToken) > 0 && b == tokenSeparator {
return p.saveSegment()
}
if len(p.currentToken) > 0 && b == tokenHardened {
p.currentTokenHardened = true
p.f = p.parseSeparator
return nil
}
if b < 0x30 || b > 0x39 {
return fmt.Errorf("expected number, got %s", string(b))
}
p.currentToken = fmt.Sprintf("%s%s", p.currentToken, string(b))
return nil
}
func Parse(str string) (StartingPoint, []uint32, error) {
p := newParser(str)
return p.parse()
}