4242#include "ecc.h"
4343
4444
45- typedef enum BIP44_LEVELS {
46- BIP44_LEVEL_PURPOSE ,
47- BIP44_LEVEL_COIN_TYPE ,
48- BIP44_LEVEL_ACCOUNT ,
49- BIP44_LEVEL_CHANGE ,
50- BIP44_LEVEL_ADDRESS ,
51- } BIP44_LEVELS ;
52-
53-
5445extern const uint8_t MEM_PAGE_ERASE [MEM_PAGE_LEN ];
5546extern const uint8_t MEM_PAGE_ERASE_FE [MEM_PAGE_LEN ];
5647extern const uint16_t MEM_PAGE_ERASE_2X [MEM_PAGE_LEN ];
@@ -149,18 +140,46 @@ int wallet_create(const char *passphrase, const char *entropy_in)
149140}
150141
151142
152- /*
153- Returns DBB_OK if successful and keypath is whitelisted
154- Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted
155- Returns DBB_ERROR if could not generate a key
156- */
157- int wallet_generate_key (HDNode * node , const char * keypath , const uint8_t * privkeymaster ,
158- const uint8_t * chaincode )
143+ int wallet_check_bip44_keypath_prefix (const uint32_t
144+ keypath0 [BIP44_KEYPATH_ADDRESS_DEPTH ],
145+ const uint32_t keypath1 [BIP44_KEYPATH_ADDRESS_DEPTH ])
146+ {
147+ // Check that purpose, coin type, and account indices are the same
148+ if (keypath0 [BIP44_LEVEL_PURPOSE ] != keypath1 [BIP44_LEVEL_PURPOSE ] ||
149+ keypath0 [BIP44_LEVEL_ACCOUNT ] != keypath1 [BIP44_LEVEL_ACCOUNT ] ||
150+ keypath0 [BIP44_LEVEL_COIN_TYPE ] != keypath1 [BIP44_LEVEL_COIN_TYPE ]) {
151+ return DBB_ERROR ;
152+ }
153+ return DBB_OK ;
154+ }
155+
156+
157+ int wallet_check_bip44_change_keypath (const uint32_t utxo [BIP44_KEYPATH_ADDRESS_DEPTH ],
158+ const uint32_t change [BIP44_KEYPATH_ADDRESS_DEPTH ])
159+ {
160+ // Check the change keypath's change level
161+ if (change [BIP44_LEVEL_CHANGE ] != 1 ) {
162+ return DBB_ERROR ;
163+ }
164+ // Check that the change keypath address level is within range
165+ if (change [BIP44_LEVEL_ADDRESS ] > BIP44_ADDRESS_MAX ) {
166+ return DBB_ERROR ;
167+ }
168+ // Check that purpose, coin type, and account indices are the same
169+ return wallet_check_bip44_keypath_prefix (utxo , change );
170+ }
171+
172+
173+ int wallet_parse_bip44_keypath (HDNode * node ,
174+ uint32_t keypath_array [BIP44_KEYPATH_ADDRESS_DEPTH ],
175+ uint32_t * depth , const char * keypath , const uint8_t * privkeymaster ,
176+ const uint8_t * chaincodemaster )
159177{
160178 static char delim [] = "/" ;
161179 static char prime [] = "phH\'" ;
162180 static char digits [] = "0123456789" ;
163181 uint64_t idx = 0 ;
182+ * depth = 0 ;
164183
165184 char * kp = strdup (keypath );
166185 if (!kp ) {
@@ -175,19 +194,21 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
175194 goto err ;
176195 }
177196
178- node -> depth = 0 ;
179- node -> child_num = 0 ;
180- node -> fingerprint = 0 ;
181- memcpy (node -> chain_code , chaincode , 32 );
182- memcpy (node -> private_key , privkeymaster , 32 );
183- hdnode_fill_public_key (node );
197+ if (node && privkeymaster && chaincodemaster ) {
198+ node -> depth = 0 ;
199+ node -> child_num = 0 ;
200+ node -> fingerprint = 0 ;
201+ memcpy (node -> chain_code , chaincodemaster , 32 );
202+ memcpy (node -> private_key , privkeymaster , 32 );
203+ hdnode_fill_public_key (node );
204+ }
184205
185206 char * pch = strtok (kp + 2 , delim );
186207 if (pch == NULL ) {
187208 goto err ;
188209 }
189210 uint8_t path_level = 0 ;
190- bool has_prime = false, whitelisted_keypath = true ;
211+ bool has_prime = false;
191212 while (pch != NULL ) {
192213 size_t i = 0 ;
193214 bool is_prime = false;
@@ -207,73 +228,34 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
207228 goto err ;
208229 }
209230 idx = strtoull (pch , NULL , 10 );
210- if (idx > UINT32_MAX ) {
231+ if (idx >= BIP44_PRIME ) {
211232 goto err ;
212233 }
213234
214- if (is_prime ) {
215- if (hdnode_private_ckd_prime (node , idx ) != DBB_OK ) {
216- goto err ;
217- }
218- } else {
219- // Note: if `idx` >= 0x80000000, prime derivation still occurs
220- // even if the `keypath` does not have a `prime` marker, following
221- // the BIP32 standard.
222- if (hdnode_private_ckd (node , idx ) != DBB_OK ) {
223- goto err ;
235+ if (node && privkeymaster && chaincodemaster ) {
236+ if (is_prime ) {
237+ if (hdnode_private_ckd_prime (node , idx ) != DBB_OK ) {
238+ goto err ;
239+ }
240+ } else {
241+ if (hdnode_private_ckd (node , idx ) != DBB_OK ) {
242+ goto err ;
243+ }
224244 }
225245 }
226246
227- // Check if the keypath is whitelisted
228- if (whitelisted_keypath ) {
229- switch (path_level ) {
230- case (BIP44_LEVEL_PURPOSE ):
231- if (is_prime != BIP44_PURPOSE_HARDENED || (
232- idx != BIP44_PURPOSE_P2PKH &&
233- idx != BIP44_PURPOSE_P2WPKH_P2SH &&
234- idx != BIP44_PURPOSE_P2WPKH
235- )) {
236- whitelisted_keypath = false;
237- }
238- break ;
239- case (BIP44_LEVEL_COIN_TYPE ):
240- if (is_prime != BIP44_COIN_TYPE_HARDENED || (
241- idx != BIP44_COIN_TYPE_BTC &&
242- idx != BIP44_COIN_TYPE_LTC &&
243- idx != BIP44_COIN_TYPE_TESTNET
244- )) {
245- whitelisted_keypath = false;
246- }
247- break ;
248- case (BIP44_LEVEL_ACCOUNT ):
249- if (is_prime != BIP44_ACCOUNT_HARDENED || idx > BIP44_ACCOUNT_MAX ) {
250- whitelisted_keypath = false;
251- }
252- break ;
253- case (BIP44_LEVEL_CHANGE ):
254- if (is_prime != BIP44_CHANGE_HARDENED || idx > BIP44_CHANGE_MAX ) {
255- whitelisted_keypath = false;
256- }
257- break ;
258- case (BIP44_LEVEL_ADDRESS ):
259- if (is_prime != BIP44_ADDRESS_HARDENED || idx > BIP44_ADDRESS_MAX ) {
260- whitelisted_keypath = false;
261- }
262- break ;
263- default :
264- whitelisted_keypath = false;
265- }
247+ if (path_level < BIP44_KEYPATH_ADDRESS_DEPTH ) {
248+ keypath_array [path_level ] = idx + (is_prime ? BIP44_PRIME : 0 );
266249 }
250+
267251 pch = strtok (NULL , delim );
268252 path_level ++ ;
253+ * depth = path_level ;
269254 }
270255 if (!has_prime ) {
271256 goto err ;
272257 }
273258 free (kp );
274- if (!whitelisted_keypath ) {
275- return DBB_WARN_KEYPATH ;
276- }
277259 return DBB_OK ;
278260
279261err :
@@ -282,6 +264,61 @@ int wallet_generate_key(HDNode *node, const char *keypath, const uint8_t *privke
282264}
283265
284266
267+ /*
268+ Returns DBB_OK if successful and keypath is whitelisted
269+ Returns DBB_WARN_KEYPATH if successful but keypath is not whitelisted
270+ Returns DBB_ERROR if could not generate a key
271+ */
272+ int wallet_generate_key (HDNode * node , const char * keypath , const uint8_t * privkeymaster ,
273+ const uint8_t * chaincodemaster )
274+ {
275+ uint32_t keypath_array [BIP44_KEYPATH_ADDRESS_DEPTH ] = {0 };
276+ uint32_t depth = 0 ;
277+ if (wallet_parse_bip44_keypath (node , keypath_array , & depth , keypath ,
278+ privkeymaster , chaincodemaster ) != DBB_OK ) {
279+ return DBB_ERROR ;
280+ }
281+
282+ // Check if the keypath is whitelisted
283+ uint32_t idx ;
284+ idx = keypath_array [BIP44_LEVEL_PURPOSE ];
285+ if (idx != (BIP44_PURPOSE_P2PKH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0 )) &&
286+ idx != (BIP44_PURPOSE_P2WPKH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0 )) &&
287+ idx != (BIP44_PURPOSE_P2WPKH_P2SH + (BIP44_PURPOSE_HARDENED ? BIP44_PRIME : 0 ))) {
288+ return DBB_WARN_KEYPATH ;
289+ }
290+
291+ idx = keypath_array [BIP44_LEVEL_COIN_TYPE ];
292+ if (idx != (BIP44_COIN_TYPE_BTC + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0 )) &&
293+ idx != (BIP44_COIN_TYPE_LTC + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0 )) &&
294+ idx != (BIP44_COIN_TYPE_TESTNET + (BIP44_COIN_TYPE_HARDENED ? BIP44_PRIME : 0 ))) {
295+ return DBB_WARN_KEYPATH ;
296+ }
297+
298+ idx = keypath_array [BIP44_LEVEL_ACCOUNT ];
299+ if (idx > (BIP44_ACCOUNT_MAX + (BIP44_ACCOUNT_HARDENED ? BIP44_PRIME : 0 )) ||
300+ idx < (BIP44_ACCOUNT_HARDENED ? BIP44_PRIME : 0 )) {
301+ return DBB_WARN_KEYPATH ;
302+ }
303+
304+ idx = keypath_array [BIP44_LEVEL_CHANGE ];
305+ if (idx > BIP44_CHANGE_MAX ) {
306+ return DBB_WARN_KEYPATH ;
307+ }
308+
309+ idx = keypath_array [BIP44_LEVEL_ADDRESS ];
310+ if (idx > BIP44_ADDRESS_MAX ) {
311+ return DBB_WARN_KEYPATH ;
312+ }
313+
314+ if (node -> depth != BIP44_KEYPATH_ADDRESS_DEPTH ) {
315+ return DBB_WARN_KEYPATH ;
316+ }
317+
318+ return DBB_OK ;
319+ }
320+
321+
285322int wallet_generate_node (const char * passphrase , const char * entropy , HDNode * node )
286323{
287324 int ret ;
0 commit comments