-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathwallet.c
3478 lines (3274 loc) · 95.2 KB
/
wallet.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/** \file wallet.c
*
* \brief Manages the storage and generation of Bitcoin addresses.
*
* Addresses are stored in wallets, which can be
* "loaded" or "unloaded". A loaded wallet can have operations (eg. new
* address) performed on it, whereas an unloaded wallet can only sit dormant.
* Addresses aren't actually physically stored in non-volatile storage;
* rather a seed for a deterministic private key generation algorithm is
* stored and private keys are generated when they are needed. This means
* that obtaining an address is a slow operation (requiring a point
* multiply), so the host should try to remember all public keys and
* addresses. The advantage of not storing addresses is that very little
* non-volatile storage space is needed per wallet.
*
* Wallets can be encrypted or unencrypted. Actually, technically, all
* wallets are encrypted. However, wallets marked as "unencrypted" are
* encrypted using an encryption key consisting of all zeroes. This purely
* semantic definition was done to avoid having to insert special cases
* everytime encrypted storage needed to be accessed.
*
* This file is licensed as described by the file LICENCE.
*/
#ifdef TEST
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#endif // #ifdef TEST
#ifdef TEST_WALLET
#include "test_helpers.h"
#endif // #ifdef TEST_WALLET
#include <stddef.h>
#include "common.h"
#include "endian.h"
#include "wallet.h"
#include "prandom.h"
#include "sha256.h"
#include "ripemd160.h"
#include "ecdsa.h"
#include "hwinterface.h"
#include "xex.h"
#include "bignum256.h"
#include "storage_common.h"
#include "hmac_sha512.h"
#include "pbkdf2.h"
/** Length of the checksum field of a wallet record. This is 32 since SHA-256
* is used to calculate the checksum and the output of SHA-256 is 32 bytes
* long. */
#define CHECKSUM_LENGTH 32
/** Structure of the unencrypted portion of a wallet record. */
struct WalletRecordUnencryptedStruct
{
/** Wallet version. Should be one of #WalletVersion. */
uint32_t version;
/** Reserved for future use. Set to all zeroes. */
uint8_t reserved[4];
/** Name of the wallet. This is purely for the sake of the host; the
* name isn't ever used or parsed by the functions in this file. */
uint8_t name[NAME_LENGTH];
/** Wallet universal unique identifier (UUID). One way for the host to
* identify a wallet. */
uint8_t uuid[UUID_LENGTH];
};
/** Structure of the encrypted portion of a wallet record. */
struct WalletRecordEncryptedStruct
{
/** Number of addresses in this wallet. */
uint32_t num_addresses;
/** Random padding. This is random to try and thwart known-plaintext
* attacks. */
uint8_t padding[8];
/** Reserved for future use. Set to all zeroes. */
uint8_t reserved[4];
/** Seed for deterministic private key generator. */
uint8_t seed[SEED_LENGTH];
/** SHA-256 of everything except this. */
uint8_t checksum[CHECKSUM_LENGTH];
};
/** Structure of a wallet record. */
typedef struct WalletRecordStruct
{
/** Unencrypted portion. See #WalletRecordUnencryptedStruct for fields.
* \warning readWalletRecord() and writeCurrentWalletRecord() both assume
* that this occurs before the encrypted portion.
*/
struct WalletRecordUnencryptedStruct unencrypted;
/** Encrypted portion. See #WalletRecordEncryptedStruct for fields. */
struct WalletRecordEncryptedStruct encrypted;
} WalletRecord;
/** The most recent error to occur in a function in this file,
* or #WALLET_NO_ERROR if no error occurred in the most recent function
* call. See #WalletErrorsEnum for possible values. */
static WalletErrors last_error;
/** This will be false if a wallet is not currently loaded. This will be true
* if a wallet is currently loaded. */
static bool wallet_loaded;
/** Whether the currently loaded wallet is a hidden wallet. If
* #wallet_loaded is false (i.e. no wallet is loaded), then the meaning of
* this variable is undefined. */
static bool is_hidden_wallet;
/** This will only be valid if a wallet is loaded. It contains a cache of the
* currently loaded wallet record. If #wallet_loaded is false (i.e. no wallet
* is loaded), then the contents of this variable are undefined. */
static WalletRecord current_wallet;
/** The address in non-volatile memory where the currently loaded wallet
* record is. If #wallet_loaded is false (i.e. no wallet is loaded), then the
* contents of this variable are undefined. */
static uint32_t wallet_nv_address;
/** Cache of number of wallets that can fit in non-volatile storage. This will
* be 0 if a value hasn't been calculated yet. This is set by
* getNumberOfWallets(). */
static uint32_t num_wallets;
#ifdef TEST
/** The file to perform test non-volatile I/O on. */
FILE *wallet_test_file;
#endif // #ifdef TEST
/** Find out what the most recent error which occurred in any wallet function
* was. If no error occurred in the most recent wallet function that was
* called, this will return #WALLET_NO_ERROR.
* \return See #WalletErrorsEnum for possible values.
*/
WalletErrors walletGetLastError(void)
{
return last_error;
}
#ifdef TEST
void initWalletTest(void)
{
wallet_test_file = fopen("wallet_test.bin", "w+b");
if (wallet_test_file == NULL)
{
printf("Could not open \"wallet_test.bin\" for writing\n");
exit(1);
}
}
#endif // #ifdef TEST
#ifdef TEST_WALLET
/** Maximum of addresses which can be stored in storage area - for testing
* only. This should actually be the capacity of the wallet, since one
* of the tests is to see what happens when the wallet is full. */
#define MAX_TESTING_ADDRESSES 7
/** Set this to true to stop sanitiseNonVolatileStorage() from
* updating the persistent entropy pool. This is necessary for some test
* cases which check where sanitiseNonVolatileStorage() writes; updates
* of the entropy pool would appear as spurious writes to those test cases.
*/
static bool suppress_set_entropy_pool;
#endif // #ifdef TEST_WALLET
/** Calculate the checksum (SHA-256 hash) of the current wallet's contents.
* \param hash The resulting SHA-256 hash will be written here. This must
* be a byte array with space for #CHECKSUM_LENGTH bytes.
* \return See #NonVolatileReturnEnum.
*/
static void calculateWalletChecksum(uint8_t *hash)
{
uint8_t *ptr;
unsigned int i;
HashState hs;
sha256Begin(&hs);
ptr = (uint8_t *)¤t_wallet;
for (i = 0; i < sizeof(WalletRecord); i++)
{
// Skip checksum when calculating the checksum.
if (i == offsetof(WalletRecord, encrypted.checksum))
{
i += sizeof(current_wallet.encrypted.checksum);
}
if (i < sizeof(WalletRecord))
{
sha256WriteByte(&hs, ptr[i]);
}
}
sha256Finish(&hs);
writeHashToByteArray(hash, &hs, true);
}
/** Load contents of non-volatile memory into a #WalletRecord structure. This
* doesn't care if there is or isn't actually a wallet at the specified
* address.
* \param wallet_record Where to load the wallet record into.
* \param address The address in non-volatile memory to read from.
* \return See #WalletErrors.
*/
static WalletErrors readWalletRecord(WalletRecord *wallet_record, uint32_t address)
{
uint32_t unencrypted_size;
uint32_t encrypted_size;
unencrypted_size = sizeof(wallet_record->unencrypted);
encrypted_size = sizeof(wallet_record->encrypted);
// Before doing any reading, do some sanity checks. These ensure that the
// size of the unencrypted and encrypted portions are an integer multiple
// of the AES block size.
if (((unencrypted_size % 16) != 0) || ((encrypted_size % 16) != 0))
{
return WALLET_INVALID_OPERATION;
}
if (nonVolatileRead(
(uint8_t *)&(wallet_record->unencrypted),
PARTITION_ACCOUNTS,
address + offsetof(WalletRecord, unencrypted),
unencrypted_size) != NV_NO_ERROR)
{
return WALLET_READ_ERROR;
}
if (encryptedNonVolatileRead(
(uint8_t *)&(wallet_record->encrypted),
PARTITION_ACCOUNTS,
address + offsetof(WalletRecord, encrypted),
encrypted_size) != NV_NO_ERROR)
{
return WALLET_READ_ERROR;
}
return WALLET_NO_ERROR;
}
/** Store contents of #current_wallet into non-volatile memory. This will also
* call nonVolatileFlush(), since that's usually what's wanted anyway.
* \param address The address in non-volatile memory to write to.
* \return See #WalletErrors.
*/
static WalletErrors writeCurrentWalletRecord(uint32_t address)
{
if (nonVolatileWrite(
(uint8_t *)&(current_wallet.unencrypted),
PARTITION_ACCOUNTS,
address + offsetof(WalletRecord, unencrypted),
sizeof(current_wallet.unencrypted)) != NV_NO_ERROR)
{
return WALLET_WRITE_ERROR;
}
if (encryptedNonVolatileWrite(
(uint8_t *)&(current_wallet.encrypted),
PARTITION_ACCOUNTS,
address + sizeof(current_wallet.unencrypted),
sizeof(current_wallet.encrypted)) != NV_NO_ERROR)
{
return WALLET_WRITE_ERROR;
}
if (nonVolatileFlush() != NV_NO_ERROR)
{
return WALLET_WRITE_ERROR;
}
return WALLET_NO_ERROR;
}
/** Using the specified password and UUID (as the salt), derive an encryption
* key and begin using it.
*
* This needs to be in wallet.c because there are situations (creating and
* restoring a wallet) when the wallet UUID is not known before the beginning
* of the appropriate function call.
* \param uuid Byte array containing the wallet UUID. This must be
* exactly #UUID_LENGTH bytes long.
* \param password Password to use in key derivation.
* \param password_length Length of password, in bytes. Use 0 to specify no
* password (i.e. wallet is unencrypted).
*/
static void deriveAndSetEncryptionKey(const uint8_t *uuid, const uint8_t *password, const unsigned int password_length)
{
uint8_t derived_key[SHA512_HASH_LENGTH];
if (sizeof(derived_key) < WALLET_ENCRYPTION_KEY_LENGTH)
{
fatalError(); // this should never happen
}
if (password_length > 0)
{
pbkdf2(derived_key, password, password_length, uuid, UUID_LENGTH);
setEncryptionKey(derived_key);
}
else
{
// No password i.e. wallet is unencrypted.
memset(derived_key, 0, sizeof(derived_key));
setEncryptionKey(derived_key);
}
}
/** Initialise a wallet (load it if it's there).
* \param wallet_spec The wallet number of the wallet to load.
* \param password Password to use to derive wallet encryption key.
* \param password_length Length of password, in bytes. Use 0 to specify no
* password (i.e. wallet is unencrypted).
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors initWallet(uint32_t wallet_spec, const uint8_t *password, const unsigned int password_length)
{
WalletErrors r;
uint8_t hash[CHECKSUM_LENGTH];
uint8_t uuid[UUID_LENGTH];
if (uninitWallet() != WALLET_NO_ERROR)
{
return last_error; // propagate error code
}
if (getNumberOfWallets() == 0)
{
return last_error; // propagate error code
}
if (wallet_spec >= num_wallets)
{
last_error = WALLET_INVALID_WALLET_NUM;
return last_error;
}
wallet_nv_address = wallet_spec * sizeof(WalletRecord);
if (nonVolatileRead(uuid, PARTITION_ACCOUNTS, wallet_nv_address + offsetof(WalletRecord, unencrypted.uuid), UUID_LENGTH) != NV_NO_ERROR)
{
last_error = WALLET_READ_ERROR;
return last_error;
}
deriveAndSetEncryptionKey(uuid, password, password_length);
r = readWalletRecord(¤t_wallet, wallet_nv_address);
if (r != WALLET_NO_ERROR)
{
last_error = r;
return last_error;
}
if (current_wallet.unencrypted.version == VERSION_NOTHING_THERE)
{
is_hidden_wallet = true;
}
else if ((current_wallet.unencrypted.version == VERSION_UNENCRYPTED)
|| (current_wallet.unencrypted.version == VERSION_IS_ENCRYPTED))
{
is_hidden_wallet = false;
}
else
{
last_error = WALLET_NOT_THERE;
return last_error;
}
// Calculate checksum and check that it matches.
calculateWalletChecksum(hash);
if (bigCompareVariableSize(current_wallet.encrypted.checksum, hash, CHECKSUM_LENGTH) != BIGCMP_EQUAL)
{
last_error = WALLET_NOT_THERE;
return last_error;
}
wallet_loaded = true;
last_error = WALLET_NO_ERROR;
return last_error;
}
/** Unload wallet, so that it cannot be used until initWallet() is called.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors uninitWallet(void)
{
clearParentPublicKeyCache();
wallet_loaded = false;
is_hidden_wallet = false;
wallet_nv_address = 0;
memset(¤t_wallet, 0, sizeof(WalletRecord));
last_error = WALLET_NO_ERROR;
return last_error;
}
#ifdef TEST_WALLET
void logVersionFieldWrite(uint32_t address);
#endif // #ifdef TEST_WALLET
/** Sanitise (clear) a selected area of non-volatile storage.
* \param partition The partition the area is contained in. Must be one
* of #NVPartitions.
* \param start The first address within the partition which will be cleared.
* Must be a multiple of 4.
* \param length The number of bytes to clear. Must be a multiple of 4.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
static WalletErrors sanitiseNonVolatileStorage(NVPartitions partition, uint32_t start, uint32_t length)
{
uint8_t buffer[32];
uint8_t pool_state[ENTROPY_POOL_LENGTH];
uint32_t address;
uint32_t bytes_written;
uint32_t bytes_to_write;
NonVolatileReturn r;
uint8_t pass;
if (getEntropyPool(pool_state))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
// The following check guards all occurrences of (address + length + offset)
// from integer overflow, for all reasonable values of "offset".
if ((start > 0x10000000) || (length > 0x10000000))
{
// address might overflow.
last_error = WALLET_BAD_ADDRESS;
return last_error;
}
// The "must be a multiple of 4" checks are there so that version fields
// (which are 4 bytes long) are always either completely cleared or not
// touched at all.
if (((start % 4) != 0) || ((length % 4) != 0))
{
// start and length not multiples of 4.
last_error = WALLET_BAD_ADDRESS;
return last_error;
}
// 4 pass format: all 0s, all 1s, random, random. This ensures that
// every bit is cleared at least once, set at least once and ends up
// in an unpredictable state.
// It is crucial that the last pass is random for two reasons:
// 1. A new device UUID is written, if necessary.
// 2. Hidden wallets are actually plausibly deniable.
for (pass = 0; pass < 4; pass++)
{
address = start;
bytes_written = 0;
while (bytes_written < length)
{
if (pass == 0)
{
memset(buffer, 0, sizeof(buffer));
}
else if (pass == 1)
{
memset(buffer, 0xff, sizeof(buffer));
}
else
{
if (getRandom256TemporaryPool(buffer, pool_state))
{
// Before returning, attempt to write the persistent
// entropy pool state back into non-volatile memory.
// The return value of setEntropyPool() is ignored because
// if a failure occurs, then WALLET_RNG_FAILURE is a
// suitable return value anyway.
#ifdef TEST_WALLET
if (!suppress_set_entropy_pool)
#endif // #ifdef TEST_WALLET
{
setEntropyPool(pool_state);
}
last_error = WALLET_RNG_FAILURE;
return last_error;
}
}
bytes_to_write = length - bytes_written;
if (bytes_to_write > sizeof(buffer))
{
bytes_to_write = sizeof(buffer);
}
if (bytes_to_write > 0)
{
r = nonVolatileWrite(buffer, partition, address, bytes_to_write);
if (r != NV_NO_ERROR)
{
last_error = WALLET_WRITE_ERROR;
return last_error;
}
}
address += bytes_to_write;
bytes_written += bytes_to_write;
} // end while (bytes_written < length)
// After each pass, flush write buffers to ensure that
// non-volatile memory is actually overwritten.
r = nonVolatileFlush();
if (r != NV_NO_ERROR)
{
last_error = WALLET_WRITE_ERROR;
return last_error;
}
} // end for (pass = 0; pass < 4; pass++)
#ifdef TEST_WALLET
if (!suppress_set_entropy_pool)
#endif // #ifdef TEST_WALLET
{
// Write back persistent entropy pool state.
if (setEntropyPool(pool_state))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
}
// At this point the selected area is now filled with random data.
// Some functions in this file expect non-random data in certain locations.
// If the selected area includes the device UUID, then a new device
// UUID needs to be written. But if the selected area includes the
// device UUID, then it will be overwritten with random data in the
// above loop. Thus no additional work is needed.
// Write VERSION_NOTHING_THERE to all possible locations of the
// version field. This ensures that a wallet won't accidentally
// (1 in 2 ^ 31 chance) be recognised as a valid wallet by
// getWalletInfo().
if (partition == PARTITION_ACCOUNTS)
{
address = start;
address /= sizeof(WalletRecord);
address *= sizeof(WalletRecord);
address += offsetof(WalletRecord, unencrypted.version);
// address is now rounded down to the first possible address where
// the version field of a wallet could be stored.
memset(buffer, 0, sizeof(uint32_t));
while ((address + sizeof(uint32_t)) <= (start + length))
{
// An additional range check against start is needed because the
// initial value of address is rounded down; thus it could be
// rounded down below start.
if (address >= start)
{
r = nonVolatileWrite(buffer, partition, address, sizeof(uint32_t));
if (r == NV_NO_ERROR)
{
r = nonVolatileFlush();
}
else if (r != NV_NO_ERROR)
{
last_error = WALLET_WRITE_ERROR;
return last_error;
}
#ifdef TEST_WALLET
if (r == NV_NO_ERROR)
{
logVersionFieldWrite(address);
}
#endif // #ifdef TEST_WALLET
}
address += sizeof(WalletRecord);
} // end while ((address + sizeof(uint32_t)) <= (start + length))
} // end if (partition == PARTITION_ACCOUNTS)
last_error = WALLET_NO_ERROR;
return last_error;
}
/** Sanitise (clear) the entire contents of a partition.
* \param partition The partition to clear. Must be one of #NVPartitions.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
static WalletErrors sanitisePartition(NVPartitions partition)
{
uint32_t size;
if (nonVolatileGetSize(&size, partition) != NV_NO_ERROR)
{
last_error = WALLET_BAD_ADDRESS;
return last_error;
}
last_error = sanitiseNonVolatileStorage(partition, 0, size);
return last_error;
}
/** Sanitise (clear) all partitions.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors sanitiseEverything(void)
{
last_error = sanitisePartition(PARTITION_GLOBAL);
if (last_error == WALLET_NO_ERROR)
{
last_error = sanitisePartition(PARTITION_ACCOUNTS);
}
return last_error;
}
/** Computes wallet version of current wallet. This is in its own function
* because it's used by both newWallet() and changeEncryptionKey().
* \return See #WalletErrors.
*/
static WalletErrors updateWalletVersion(void)
{
if (is_hidden_wallet)
{
// Hidden wallets should not ever have their version fields updated;
// that would give away their presence.
return WALLET_INVALID_OPERATION;
}
if (isEncryptionKeyNonZero())
{
current_wallet.unencrypted.version = VERSION_IS_ENCRYPTED;
}
else
{
current_wallet.unencrypted.version = VERSION_UNENCRYPTED;
}
return WALLET_NO_ERROR;
}
/** Delete a wallet, so that it's contents can no longer be retrieved from
* non-volatile storage.
* \param wallet_spec The wallet number of the wallet to delete. The wallet
* doesn't have to "exist"; calling this function for a
* non-existent wallet will clear the non-volatile space
* associated with it. This is useful for deleting a
* hidden wallet.
* \warning This is irreversible; the only way to access the wallet after
* deletion is to restore a backup.
*/
WalletErrors deleteWallet(uint32_t wallet_spec)
{
uint32_t address;
if (getNumberOfWallets() == 0)
{
return last_error; // propagate error code
}
if (wallet_spec >= num_wallets)
{
last_error = WALLET_INVALID_WALLET_NUM;
return last_error;
}
// Always unload current wallet, just in case the current wallet is the
// one being deleted.
if (uninitWallet() != WALLET_NO_ERROR)
{
return last_error; // propagate error code
}
address = wallet_spec * sizeof(WalletRecord);
last_error = sanitiseNonVolatileStorage(PARTITION_ACCOUNTS, address, sizeof(WalletRecord));
return last_error;
}
/** Create new wallet. A brand new wallet contains no addresses and should
* have a unique, unpredictable deterministic private key generation seed.
* \param wallet_spec The wallet number of the new wallet.
* \param name Should point to #NAME_LENGTH bytes (padded with spaces if
* necessary) containing the desired name of the wallet.
* \param use_seed If this is true, then the contents of seed will be
* used as the deterministic private key generation seed.
* If this is false, then the contents of seed will be
* ignored.
* \param seed The deterministic private key generation seed to use in the
* new wallet. This should be a byte array of length #SEED_LENGTH
* bytes. This parameter will be ignored if use_seed is false.
* \param make_hidden Whether to make the new wallet a hidden wallet.
* \param password Password to use to derive wallet encryption key.
* \param password_length Length of password, in bytes. Use 0 to specify no
* password (i.e. wallet is unencrypted).
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred. If this returns #WALLET_NO_ERROR, then the
* wallet will also be loaded.
* \warning This will erase the current one.
*/
WalletErrors newWallet(uint32_t wallet_spec, uint8_t *name, bool use_seed, uint8_t *seed, bool make_hidden, const uint8_t *password, const unsigned int password_length)
{
uint8_t random_buffer[32];
uint8_t uuid[UUID_LENGTH];
WalletErrors r;
if (uninitWallet() != WALLET_NO_ERROR)
{
return last_error; // propagate error code
}
if (getNumberOfWallets() == 0)
{
return last_error; // propagate error code
}
if (wallet_spec >= num_wallets)
{
last_error = WALLET_INVALID_WALLET_NUM;
return last_error;
}
wallet_nv_address = wallet_spec * sizeof(WalletRecord);
// Check for existing wallet.
r = readWalletRecord(¤t_wallet, wallet_nv_address);
if (r != WALLET_NO_ERROR)
{
last_error = r;
return last_error;
}
if (current_wallet.unencrypted.version != VERSION_NOTHING_THERE)
{
last_error = WALLET_ALREADY_EXISTS;
return last_error;
}
if (make_hidden)
{
// The creation of a hidden wallet is supposed to be discreet, so
// all unencrypted fields should be left untouched. This forces us to
// use the existing UUID.
memcpy(uuid, current_wallet.unencrypted.uuid, UUID_LENGTH);
}
else
{
// Generate wallet UUID now, because it is needed to derive the wallet
// encryption key.
if (getRandom256(random_buffer))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
memcpy(uuid, random_buffer, UUID_LENGTH);
}
deriveAndSetEncryptionKey(uuid, password, password_length);
// Update unencrypted fields of current_wallet.
if (!make_hidden)
{
r = updateWalletVersion();
if (r != WALLET_NO_ERROR)
{
last_error = r;
return last_error;
}
memset(current_wallet.unencrypted.reserved, 0, sizeof(current_wallet.unencrypted.reserved));
memcpy(current_wallet.unencrypted.name, name, NAME_LENGTH);
memcpy(current_wallet.unencrypted.uuid, uuid, UUID_LENGTH);
}
// Update encrypted fields of current_wallet.
current_wallet.encrypted.num_addresses = 0;
if (getRandom256(random_buffer))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
memcpy(current_wallet.encrypted.padding, random_buffer, sizeof(current_wallet.encrypted.padding));
memset(current_wallet.encrypted.reserved, 0, sizeof(current_wallet.encrypted.reserved));
if (use_seed)
{
memcpy(current_wallet.encrypted.seed, seed, SEED_LENGTH);
}
else
{
if (getRandom256(random_buffer))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
memcpy(current_wallet.encrypted.seed, random_buffer, 32);
if (getRandom256(random_buffer))
{
last_error = WALLET_RNG_FAILURE;
return last_error;
}
memcpy(&(current_wallet.encrypted.seed[32]), random_buffer, 32);
}
calculateWalletChecksum(current_wallet.encrypted.checksum);
r = writeCurrentWalletRecord(wallet_nv_address);
if (r != WALLET_NO_ERROR)
{
last_error = r;
return last_error;
}
last_error = initWallet(wallet_spec, password, password_length);
return last_error;
}
/** Generate a new address using the deterministic private key generator.
* \param out_address The new address will be written here (if everything
* goes well). This must be a byte array with space for
* 20 bytes.
* \param out_public_key The public key corresponding to the new address will
* be written here (if everything goes well).
* \return The address handle of the new address on success,
* or #BAD_ADDRESS_HANDLE if an error occurred.
* Use walletGetLastError() to get more detail about an error.
*/
AddressHandle makeNewAddress(uint8_t *out_address, PointAffine *out_public_key)
{
WalletErrors r;
if (!wallet_loaded)
{
last_error = WALLET_NOT_LOADED;
return BAD_ADDRESS_HANDLE;
}
#ifdef TEST_WALLET
if (current_wallet.encrypted.num_addresses >= MAX_TESTING_ADDRESSES)
#else
if (current_wallet.encrypted.num_addresses >= MAX_ADDRESSES)
#endif // #ifdef TEST_WALLET
{
last_error = WALLET_FULL;
return BAD_ADDRESS_HANDLE;
}
(current_wallet.encrypted.num_addresses)++;
calculateWalletChecksum(current_wallet.encrypted.checksum);
r = writeCurrentWalletRecord(wallet_nv_address);
if (r != WALLET_NO_ERROR)
{
last_error = r;
return BAD_ADDRESS_HANDLE;
}
last_error = getAddressAndPublicKey(out_address, out_public_key, current_wallet.encrypted.num_addresses);
if (last_error != WALLET_NO_ERROR)
{
return BAD_ADDRESS_HANDLE;
}
else
{
return current_wallet.encrypted.num_addresses;
}
}
/** Given an address handle, use the deterministic private key
* generator to generate the address and public key associated
* with that address handle.
* \param out_address The address will be written here (if everything
* goes well). This must be a byte array with space for
* 20 bytes.
* \param out_public_key The public key corresponding to the address will
* be written here (if everything goes well).
* \param ah The address handle to obtain the address/public key of.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors getAddressAndPublicKey(uint8_t *out_address, PointAffine *out_public_key, AddressHandle ah)
{
uint8_t buffer[32];
uint8_t serialised[ECDSA_MAX_SERIALISE_SIZE];
uint8_t serialised_size;
HashState hs;
WalletErrors r;
uint8_t i;
if (!wallet_loaded)
{
last_error = WALLET_NOT_LOADED;
return last_error;
}
if (current_wallet.encrypted.num_addresses == 0)
{
last_error = WALLET_EMPTY;
return last_error;
}
if ((ah == 0) || (ah > current_wallet.encrypted.num_addresses) || (ah == BAD_ADDRESS_HANDLE))
{
last_error = WALLET_INVALID_HANDLE;
return last_error;
}
// Calculate private key.
r = getPrivateKey(buffer, ah);
if (r != WALLET_NO_ERROR)
{
last_error = r;
return r;
}
// Calculate public key.
setToG(out_public_key);
pointMultiply(out_public_key, buffer);
// Calculate address.
serialised_size = ecdsaSerialise(serialised, out_public_key, true);
if (serialised_size < 2)
{
// Somehow, the public ended up as the point at infinity.
last_error = WALLET_INVALID_HANDLE;
return last_error;
}
sha256Begin(&hs);
for (i = 0; i < serialised_size; i++)
{
sha256WriteByte(&hs, serialised[i]);
}
sha256Finish(&hs);
writeHashToByteArray(buffer, &hs, true);
ripemd160Begin(&hs);
for (i = 0; i < 32; i++)
{
ripemd160WriteByte(&hs, buffer[i]);
}
ripemd160Finish(&hs);
writeHashToByteArray(buffer, &hs, true);
memcpy(out_address, buffer, 20);
last_error = WALLET_NO_ERROR;
return last_error;
}
/** Get the master public key of the currently loaded wallet. Every public key
* (and address) in a wallet can be derived from the master public key and
* chain code. However, even with posession of the master public key, all
* private keys are still secret.
* \param out_public_key The master public key will be written here.
* \param out_chain_code The chain code will be written here. This must be a
* byte array with space for 32 bytes.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors getMasterPublicKey(PointAffine *out_public_key, uint8_t *out_chain_code)
{
uint8_t local_seed[SEED_LENGTH]; // need a local copy to modify
BigNum256 k_par;
if (!wallet_loaded)
{
last_error = WALLET_NOT_LOADED;
return last_error;
}
memcpy(local_seed, current_wallet.encrypted.seed, SEED_LENGTH);
memcpy(out_chain_code, &(local_seed[32]), 32);
k_par = (BigNum256)local_seed;
swapEndian256(k_par); // since seed is big-endian
setFieldToN();
bigModulo(k_par, k_par); // just in case
setToG(out_public_key);
pointMultiply(out_public_key, k_par);
last_error = WALLET_NO_ERROR;
return last_error;
}
/** Get the current number of addresses in a wallet.
* \return The current number of addresses on success, or 0 if an error
* occurred. Use walletGetLastError() to get more detail about
* an error.
*/
uint32_t getNumAddresses(void)
{
if (!wallet_loaded)
{
last_error = WALLET_NOT_LOADED;
return 0;
}
if (current_wallet.encrypted.num_addresses == 0)
{
last_error = WALLET_EMPTY;
return 0;
}
else
{
last_error = WALLET_NO_ERROR;
return current_wallet.encrypted.num_addresses;
}
}
/** Given an address handle, use the deterministic private key
* generator to generate the private key associated with that address handle.
* \param out The private key will be written here (if everything goes well).
* This must be a byte array with space for 32 bytes.
* \param ah The address handle to obtain the private key of.
* \return #WALLET_NO_ERROR on success, or one of #WalletErrorsEnum if an
* error occurred.
*/
WalletErrors getPrivateKey(uint8_t *out, AddressHandle ah)
{
if (!wallet_loaded)
{
last_error = WALLET_NOT_LOADED;
return last_error;
}
if (current_wallet.encrypted.num_addresses == 0)
{
last_error = WALLET_EMPTY;
return last_error;
}
if ((ah == 0) || (ah > current_wallet.encrypted.num_addresses) || (ah == BAD_ADDRESS_HANDLE))
{
last_error = WALLET_INVALID_HANDLE;
return last_error;
}
if (generateDeterministic256(out, current_wallet.encrypted.seed, ah))
{
// This should never happen.
last_error = WALLET_RNG_FAILURE;
return last_error;
}
last_error = WALLET_NO_ERROR;
return last_error;
}
/** Change the encryption key of a wallet.
* \param password Password to use to derive wallet encryption key.
* \param password_length Length of password, in bytes. Use 0 to specify no