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 }