github.com/status-im/status-go@v1.1.0/account/generator/path_decoder.go (about)

     1  package generator
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strconv"
     7  	"strings"
     8  )
     9  
    10  type startingPoint int
    11  
    12  const (
    13  	tokenMaster    = 0x6D // char m
    14  	tokenSeparator = 0x2F // char /
    15  	tokenHardened  = 0x27 // char '
    16  	tokenDot       = 0x2E // char .
    17  
    18  	hardenedStart = 0x80000000 // 2^31
    19  )
    20  
    21  const (
    22  	startingPointMaster startingPoint = iota + 1
    23  	startingPointCurrent
    24  	startingPointParent
    25  )
    26  
    27  type parseFunc = func() error
    28  
    29  type pathDecoder struct {
    30  	s                    string
    31  	r                    *strings.Reader
    32  	f                    parseFunc
    33  	pos                  int
    34  	path                 []uint32
    35  	start                startingPoint
    36  	currentToken         string
    37  	currentTokenHardened bool
    38  }
    39  
    40  func newPathDecoder(path string) (*pathDecoder, error) {
    41  	d := &pathDecoder{
    42  		s: path,
    43  		r: strings.NewReader(path),
    44  	}
    45  
    46  	if err := d.reset(); err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	return d, nil
    51  }
    52  
    53  func (d *pathDecoder) reset() error {
    54  	_, err := d.r.Seek(0, io.SeekStart)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	d.pos = 0
    60  	d.start = startingPointCurrent
    61  	d.f = d.parseStart
    62  	d.path = make([]uint32, 0)
    63  	d.resetCurrentToken()
    64  
    65  	return nil
    66  }
    67  
    68  func (d *pathDecoder) resetCurrentToken() {
    69  	d.currentToken = ""
    70  	d.currentTokenHardened = false
    71  }
    72  
    73  func (d *pathDecoder) parse() (startingPoint, []uint32, error) {
    74  	for {
    75  		err := d.f()
    76  		if err != nil {
    77  			if err == io.EOF {
    78  				err = nil
    79  			} else {
    80  				err = fmt.Errorf("error parsing derivation path %s; at position %d, %s", d.s, d.pos, err.Error())
    81  			}
    82  
    83  			return d.start, d.path, err
    84  		}
    85  	}
    86  }
    87  
    88  func (d *pathDecoder) readByte() (byte, error) {
    89  	b, err := d.r.ReadByte()
    90  	if err != nil {
    91  		return b, err
    92  	}
    93  
    94  	d.pos++
    95  
    96  	return b, nil
    97  }
    98  
    99  func (d *pathDecoder) unreadByte() error {
   100  	err := d.r.UnreadByte()
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	d.pos--
   106  
   107  	return nil
   108  }
   109  
   110  func (d *pathDecoder) parseStart() error {
   111  	b, err := d.readByte()
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	if b == tokenMaster {
   117  		d.start = startingPointMaster
   118  		d.f = d.parseSeparator
   119  		return nil
   120  	}
   121  
   122  	if b == tokenDot {
   123  		b2, err := d.readByte()
   124  		if err != nil {
   125  			return err
   126  		}
   127  
   128  		if b2 == tokenDot {
   129  			d.f = d.parseSeparator
   130  			d.start = startingPointParent
   131  			return nil
   132  		}
   133  
   134  		d.f = d.parseSeparator
   135  		d.start = startingPointCurrent
   136  		return d.unreadByte()
   137  	}
   138  
   139  	d.f = d.parseSegment
   140  
   141  	return d.unreadByte()
   142  }
   143  
   144  func (d *pathDecoder) saveSegment() error {
   145  	if len(d.currentToken) > 0 {
   146  		i, err := strconv.ParseUint(d.currentToken, 10, 32)
   147  		if err != nil {
   148  			return err
   149  		}
   150  
   151  		if i >= hardenedStart {
   152  			d.pos -= len(d.currentToken) - 1
   153  			return fmt.Errorf("index must be lower than 2^31, got %d", i)
   154  		}
   155  
   156  		if d.currentTokenHardened {
   157  			i += hardenedStart
   158  		}
   159  
   160  		d.path = append(d.path, uint32(i))
   161  	}
   162  
   163  	d.f = d.parseSegment
   164  	d.resetCurrentToken()
   165  
   166  	return nil
   167  }
   168  
   169  func (d *pathDecoder) parseSeparator() error {
   170  	b, err := d.readByte()
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	if b == tokenSeparator {
   176  		return d.saveSegment()
   177  	}
   178  
   179  	return fmt.Errorf("expected %s, got %s", string(rune(tokenSeparator)), string(rune(b)))
   180  }
   181  
   182  func (d *pathDecoder) parseSegment() error {
   183  	b, err := d.readByte()
   184  	if err == io.EOF {
   185  		if len(d.currentToken) == 0 {
   186  			return fmt.Errorf("expected number, got EOF")
   187  		}
   188  
   189  		if newErr := d.saveSegment(); newErr != nil {
   190  			return newErr
   191  		}
   192  
   193  		return err
   194  	}
   195  
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	if len(d.currentToken) > 0 && b == tokenSeparator {
   201  		return d.saveSegment()
   202  	}
   203  
   204  	if len(d.currentToken) > 0 && b == tokenHardened {
   205  		d.currentTokenHardened = true
   206  		d.f = d.parseSeparator
   207  		return nil
   208  	}
   209  
   210  	if b < 0x30 || b > 0x39 {
   211  		return fmt.Errorf("expected number, got %s", string(b))
   212  	}
   213  
   214  	d.currentToken = fmt.Sprintf("%s%s", d.currentToken, string(b))
   215  
   216  	return nil
   217  }
   218  
   219  func decodePath(str string) (startingPoint, []uint32, error) {
   220  	d, err := newPathDecoder(str)
   221  	if err != nil {
   222  		return 0, nil, err
   223  	}
   224  
   225  	return d.parse()
   226  }