-
Notifications
You must be signed in to change notification settings - Fork 69
/
sign_psbt.c
2559 lines (2157 loc) · 101 KB
/
sign_psbt.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
/*****************************************************************************
* Ledger App Bitcoin.
* (c) 2021 Ledger SAS.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
#include <stdint.h>
#include "lib_standard_app/crypto_helpers.h"
#include "../boilerplate/dispatcher.h"
#include "../boilerplate/sw.h"
#include "../common/bitvector.h"
#include "../common/merkle.h"
#include "../common/psbt.h"
#include "../common/read.h"
#include "../common/script.h"
#include "../common/varint.h"
#include "../common/wallet.h"
#include "../common/write.h"
#include "../commands.h"
#include "../constants.h"
#include "../crypto.h"
#include "../ui/display.h"
#include "../ui/menu.h"
#include "client_commands.h"
#include "lib/policy.h"
#include "lib/check_merkle_tree_sorted.h"
#include "lib/get_preimage.h"
#include "lib/get_merkleized_map.h"
#include "lib/get_merkleized_map_value.h"
#include "lib/get_merkle_leaf_element.h"
#include "lib/psbt_parse_rawtx.h"
#include "handlers.h"
#include "sign_psbt/compare_wallet_script_at_path.h"
#include "sign_psbt/extract_bip32_derivation.h"
#include "sign_psbt/update_hashes_with_map_value.h"
#include "../swap/swap_globals.h"
// common info that applies to either the current input or the current output
typedef struct {
merkleized_map_commitment_t map;
bool unexpected_pubkey_error; // Set to true if the pubkey in the keydata of
// PSBT_{IN,OUT}_BIP32_DERIVATION or
// PSBT_{IN,OUT}_TAP_BIP32_DERIVATION is not the correct length.
bool placeholder_found; // Set to true if a matching placeholder is found in the input info
bool is_change;
int address_index;
// For an output, its scriptPubKey
// for an input, the prevout's scriptPubKey (either from the non-witness-utxo, or from the
// witness-utxo)
uint8_t scriptPubKey[MAX_OUTPUT_SCRIPTPUBKEY_LEN];
size_t scriptPubKey_len;
} in_out_info_t;
typedef struct {
in_out_info_t in_out;
bool has_witnessUtxo;
bool has_nonWitnessUtxo;
bool has_redeemScript;
bool has_sighash_type;
uint64_t prevout_amount; // the value of the prevout of the current input
// we no longer need the script when we compute the taptree hash right before a taproot key-path
// spending; therefore, we reuse the same memory
union {
// the script used when signing, either from the witness utxo or the redeem script
uint8_t script[MAX_PREVOUT_SCRIPTPUBKEY_LEN];
uint8_t taptree_hash[32];
};
size_t script_len;
uint32_t sighash_type;
} input_info_t;
typedef struct {
in_out_info_t in_out;
uint64_t value;
} output_info_t;
typedef struct {
policy_node_key_placeholder_t placeholder;
int cur_index;
uint32_t fingerprint;
uint8_t key_derivation_length;
uint32_t key_derivation[MAX_BIP32_PATH_STEPS];
serialized_extended_pubkey_t pubkey;
bool is_tapscript; // true if signing with a BIP342 tapleaf script path spend
uint8_t tapleaf_hash[32]; // only used for tapscripts
} placeholder_info_t;
// Cache for partial hashes during segwit signing (avoid quadratic hashing for segwit transactions)
typedef struct {
uint8_t sha_prevouts[32];
uint8_t sha_amounts[32];
uint8_t sha_scriptpubkeys[32];
uint8_t sha_sequences[32];
uint8_t sha_outputs[32];
} segwit_hashes_t;
typedef struct {
uint32_t master_key_fingerprint;
uint32_t tx_version;
uint32_t locktime;
unsigned int n_inputs;
uint8_t inputs_root[32]; // merkle root of the vector of input maps commitments
unsigned int n_outputs;
uint8_t outputs_root[32]; // merkle root of the vector of output maps commitments
uint64_t inputs_total_amount;
// aggregate info on outputs
struct {
uint64_t total_amount; // amount of all the outputs (external + change)
uint64_t change_total_amount; // total amount of all change outputs
int n_change; // count of outputs compatible with change outputs
int n_external; // count of external outputs
} outputs;
bool is_wallet_canonical;
uint8_t protocol_version;
union {
uint8_t wallet_policy_map_bytes[MAX_WALLET_POLICY_BYTES];
policy_node_t wallet_policy_map;
};
int wallet_header_version;
uint8_t wallet_header_keys_info_merkle_root[32];
size_t wallet_header_n_keys;
// if any segwitv0 input is missing the non-witness-utxo, we show a warning
bool show_missing_nonwitnessutxo_warning;
// if any of the internal inputs has non-default sighash, we show a warning
bool show_nondefault_sighash_warning;
} sign_psbt_state_t;
/* BIP0341 tags for computing the tagged hashes when computing he sighash */
static const uint8_t BIP0341_sighash_tag[] = {'T', 'a', 'p', 'S', 'i', 'g', 'h', 'a', 's', 'h'};
/*
Current assumptions during signing:
1) exactly one of the keys in the wallet is internal (enforce during wallet registration)
2) all the keys in the wallet have a wildcard (that is, they end with '**'), with at most
4 derivation steps before it.
Assumption 2 simplifies the handling of pubkeys (and their paths) used for signing,
as all the internal keys will have a path that ends with /change/address_index (BIP44-style).
It would be possible to generalize to more complex scripts, but it makes it more difficult to detect
the right paths to identify internal inputs/outputs.
*/
// HELPER FUNCTIONS
// Updates the hash_context with the output of given index
// returns -1 on error. 0 on success.
static int hash_output_n(dispatcher_context_t *dc,
sign_psbt_state_t *st,
cx_hash_t *hash_context,
unsigned int index) {
if (index >= st->n_outputs) {
return -1;
}
// get this output's map
merkleized_map_commitment_t ith_map;
int res = call_get_merkleized_map(dc, st->outputs_root, st->n_outputs, index, &ith_map);
if (res < 0) {
return -1;
}
// get output's amount
uint8_t amount_raw[8];
if (8 != call_get_merkleized_map_value(dc,
&ith_map,
(uint8_t[]){PSBT_OUT_AMOUNT},
1,
amount_raw,
8)) {
return -1;
}
crypto_hash_update(hash_context, amount_raw, 8);
// get output's scriptPubKey
uint8_t out_script[MAX_OUTPUT_SCRIPTPUBKEY_LEN];
int out_script_len = call_get_merkleized_map_value(dc,
&ith_map,
(uint8_t[]){PSBT_OUT_SCRIPT},
1,
out_script,
sizeof(out_script));
if (out_script_len == -1) {
return -1;
}
crypto_hash_update_varint(hash_context, out_script_len);
crypto_hash_update(hash_context, out_script, out_script_len);
return 0;
}
// Updates the hash_context with the network serialization of all the outputs
// returns -1 on error. 0 on success.
static int hash_outputs(dispatcher_context_t *dc, sign_psbt_state_t *st, cx_hash_t *hash_context) {
for (unsigned int i = 0; i < st->n_outputs; i++) {
if (hash_output_n(dc, st, hash_context, i)) {
return -1;
}
}
return 0;
}
/*
Convenience function to get the amount and scriptpubkey from the non-witness-utxo of a certain
input in a PSBTv2.
If expected_prevout_hash is not NULL, the function fails if the txid computed from the
non-witness-utxo does not match the one pointed by expected_prevout_hash. Returns -1 on failure, 0
on success.
*/
static int __attribute__((noinline)) get_amount_scriptpubkey_from_psbt_nonwitness(
dispatcher_context_t *dc,
const merkleized_map_commitment_t *input_map,
uint64_t *amount,
uint8_t scriptPubKey[static MAX_PREVOUT_SCRIPTPUBKEY_LEN],
size_t *scriptPubKey_len,
const uint8_t *expected_prevout_hash) {
// If there is no witness-utxo, it must be the case that this is a legacy input.
// In this case, we can only retrieve the prevout amount and scriptPubKey by parsing
// the non-witness-utxo
// Read the prevout index
uint32_t prevout_n;
if (4 != call_get_merkleized_map_value_u32_le(dc,
input_map,
(uint8_t[]){PSBT_IN_OUTPUT_INDEX},
1,
&prevout_n)) {
return -1;
}
txid_parser_outputs_t parser_outputs;
// request non-witness utxo, and get the prevout's value and scriptpubkey
int res = call_psbt_parse_rawtx(dc,
input_map,
(uint8_t[]){PSBT_IN_NON_WITNESS_UTXO},
1,
prevout_n,
&parser_outputs);
if (res < 0) {
PRINTF("Parsing rawtx failed\n");
return -1;
}
// if expected_prevout_hash is given, check that it matches the txid obtained from the parser
if (expected_prevout_hash != NULL &&
memcmp(parser_outputs.txid, expected_prevout_hash, 32) != 0) {
PRINTF("Prevout hash did not match non-witness-utxo transaction hash\n");
return -1;
}
*amount = parser_outputs.vout_value;
*scriptPubKey_len = parser_outputs.vout_scriptpubkey_len;
memcpy(scriptPubKey, parser_outputs.vout_scriptpubkey, parser_outputs.vout_scriptpubkey_len);
return 0;
}
/*
Convenience function to get the amount and scriptpubkey from the witness-utxo of a certain input in
a PSBTv2.
Returns -1 on failure, 0 on success.
*/
static int __attribute__((noinline))
get_amount_scriptpubkey_from_psbt_witness(dispatcher_context_t *dc,
const merkleized_map_commitment_t *input_map,
uint64_t *amount,
uint8_t scriptPubKey[static MAX_PREVOUT_SCRIPTPUBKEY_LEN],
size_t *scriptPubKey_len) {
uint8_t raw_witnessUtxo[8 + 1 + MAX_PREVOUT_SCRIPTPUBKEY_LEN];
int wit_utxo_len = call_get_merkleized_map_value(dc,
input_map,
(uint8_t[]){PSBT_IN_WITNESS_UTXO},
1,
raw_witnessUtxo,
sizeof(raw_witnessUtxo));
if (wit_utxo_len < 0) {
return -1;
}
int wit_utxo_scriptPubkey_len = raw_witnessUtxo[8];
if (wit_utxo_len != 8 + 1 + wit_utxo_scriptPubkey_len) {
PRINTF("Length mismatch for witness utxo's scriptPubKey\n");
return -1;
}
uint8_t *wit_utxo_scriptPubkey = raw_witnessUtxo + 9;
uint64_t wit_utxo_prevout_amount = read_u64_le(&raw_witnessUtxo[0], 0);
*amount = wit_utxo_prevout_amount;
*scriptPubKey_len = wit_utxo_scriptPubkey_len;
memcpy(scriptPubKey, wit_utxo_scriptPubkey, wit_utxo_scriptPubkey_len);
return 0;
}
/*
Convenience function to get the amount and scriptpubkey of a certain input in a PSBTv2.
It first tries to obtain it from the witness-utxo field; in case of failure, it then obtains it
from the non-witness-utxo.
Returns -1 on failure, 0 on success.
*/
static int get_amount_scriptpubkey_from_psbt(
dispatcher_context_t *dc,
const merkleized_map_commitment_t *input_map,
uint64_t *amount,
uint8_t scriptPubKey[static MAX_PREVOUT_SCRIPTPUBKEY_LEN],
size_t *scriptPubKey_len) {
int ret = get_amount_scriptpubkey_from_psbt_witness(dc,
input_map,
amount,
scriptPubKey,
scriptPubKey_len);
if (ret >= 0) {
return ret;
}
return get_amount_scriptpubkey_from_psbt_nonwitness(dc,
input_map,
amount,
scriptPubKey,
scriptPubKey_len,
NULL);
}
// Convenience function to share common logic when processing all the
// PSBT_{IN|OUT}_{TAP}?_BIP32_DERIVATION fields.
static int read_change_and_index_from_psbt_bip32_derivation(
dispatcher_context_t *dc,
placeholder_info_t *placeholder_info,
in_out_info_t *in_out,
int psbt_key_type,
buffer_t *data,
const merkleized_map_commitment_t *map_commitment,
int index) {
uint8_t bip32_derivation_pubkey[33];
bool is_tap = psbt_key_type == PSBT_IN_TAP_BIP32_DERIVATION ||
psbt_key_type == PSBT_OUT_TAP_BIP32_DERIVATION;
int key_len = is_tap ? 32 : 33;
if (!buffer_read_bytes(data,
bip32_derivation_pubkey,
key_len) // read compressed pubkey or x-only pubkey
|| buffer_can_read(data, 1) // ...but should not be able to read more
) {
PRINTF("Unexpected pubkey length\n");
in_out->unexpected_pubkey_error = true;
return -1;
}
// get the corresponding value in the values Merkle tree,
// then fetch the bip32 path from the field
uint32_t fpt_der[1 + MAX_BIP32_PATH_STEPS];
int der_len = extract_bip32_derivation(dc,
psbt_key_type,
map_commitment->values_root,
map_commitment->size,
index,
fpt_der);
if (der_len < 0) {
PRINTF("Failed to read BIP32_DERIVATION\n");
return -1;
}
if (der_len < 2 || der_len > MAX_BIP32_PATH_STEPS) {
PRINTF("BIP32_DERIVATION path too long\n");
return -1;
}
// if this derivation path matches the internal placeholder,
// we use it to detect whether the current input is change or not,
// and store its address index
if (fpt_der[0] == placeholder_info->fingerprint &&
der_len == placeholder_info->key_derivation_length + 2) {
for (int i = 0; i < placeholder_info->key_derivation_length; i++) {
if (placeholder_info->key_derivation[i] != fpt_der[1 + i]) {
return 0;
}
}
uint32_t change = fpt_der[1 + der_len - 2];
uint32_t addr_index = fpt_der[1 + der_len - 1];
// check that we can indeed derive the same key from the current placeholder
serialized_extended_pubkey_t pubkey;
if (0 > bip32_CKDpub(&placeholder_info->pubkey, change, &pubkey)) return -1;
if (0 > bip32_CKDpub(&pubkey, addr_index, &pubkey)) return -1;
int pk_offset = is_tap ? 1 : 0;
if (memcmp(pubkey.compressed_pubkey + pk_offset, bip32_derivation_pubkey, key_len) != 0) {
return 0;
}
// check if the 'change' derivation step is indeed coherent with placeholder
if (change == placeholder_info->placeholder.num_first) {
in_out->is_change = false;
in_out->address_index = addr_index;
} else if (change == placeholder_info->placeholder.num_second) {
in_out->is_change = true;
in_out->address_index = addr_index;
} else {
return 0;
}
in_out->placeholder_found = true;
return 1;
}
return 0;
}
/**
* Verifies if a certain input/output is internal (that is, controlled by the wallet being used for
* signing). This uses the state of sign_psbt and is not meant as a general-purpose function;
* rather, it avoids some substantial code duplication and removes complexity from sign_psbt.
*
* @return 1 if the given input/output is internal; 0 if external; -1 on error.
*/
static int is_in_out_internal(dispatcher_context_t *dispatcher_context,
const sign_psbt_state_t *state,
const in_out_info_t *in_out_info,
bool is_input) {
// If we did not find any info about the pubkey associated to the placeholder we're considering,
// then it's external
if (!in_out_info->placeholder_found) {
return 0;
}
if (!is_input && in_out_info->is_change != 1) {
// unlike for inputs, we only consider outputs internal if they are on the change path
return 0;
}
return compare_wallet_script_at_path(dispatcher_context,
in_out_info->is_change,
in_out_info->address_index,
&state->wallet_policy_map,
state->wallet_header_version,
state->wallet_header_keys_info_merkle_root,
state->wallet_header_n_keys,
in_out_info->scriptPubKey,
in_out_info->scriptPubKey_len);
}
static bool __attribute__((noinline))
init_global_state(dispatcher_context_t *dc, sign_psbt_state_t *st) {
LOG_PROCESSOR(__FILE__, __LINE__, __func__);
merkleized_map_commitment_t global_map;
if (!buffer_read_varint(&dc->read_buffer, &global_map.size)) {
SEND_SW(dc, SW_WRONG_DATA_LENGTH);
return false;
}
if (!buffer_read_bytes(&dc->read_buffer, global_map.keys_root, 32) ||
!buffer_read_bytes(&dc->read_buffer, global_map.values_root, 32)) {
SEND_SW(dc, SW_WRONG_DATA_LENGTH);
return false;
}
// we already know n_inputs and n_outputs, so we skip reading from the global map
uint64_t n_inputs_u64;
if (!buffer_read_varint(&dc->read_buffer, &n_inputs_u64) ||
!buffer_read_bytes(&dc->read_buffer, st->inputs_root, 32)) {
SEND_SW(dc, SW_WRONG_DATA_LENGTH);
return false;
}
if (n_inputs_u64 > MAX_N_INPUTS_CAN_SIGN) {
PRINTF("At most %d inputs are supported\n", MAX_N_INPUTS_CAN_SIGN);
SEND_SW(dc, SW_NOT_SUPPORTED);
return false;
}
st->n_inputs = (unsigned int) n_inputs_u64;
uint64_t n_outputs_u64;
if (!buffer_read_varint(&dc->read_buffer, &n_outputs_u64) ||
!buffer_read_bytes(&dc->read_buffer, st->outputs_root, 32)) {
SEND_SW(dc, SW_WRONG_DATA_LENGTH);
return false;
}
st->n_outputs = (unsigned int) n_outputs_u64;
policy_map_wallet_header_t wallet_header;
uint8_t wallet_hmac[32];
uint8_t wallet_id[32];
if (!buffer_read_bytes(&dc->read_buffer, wallet_id, 32) ||
!buffer_read_bytes(&dc->read_buffer, wallet_hmac, 32)) {
SEND_SW(dc, SW_WRONG_DATA_LENGTH);
return false;
}
{ // process global map
// Check integrity of the global map
if (call_check_merkle_tree_sorted(dc, global_map.keys_root, (size_t) global_map.size) < 0) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
uint8_t raw_result[9]; // max size for a varint
int result_len;
// Read tx version
result_len = call_get_merkleized_map_value(dc,
&global_map,
(uint8_t[]){PSBT_GLOBAL_TX_VERSION},
1,
raw_result,
sizeof(raw_result));
if (result_len != 4) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
st->tx_version = read_u32_le(raw_result, 0);
// Read fallback locktime.
// Unlike BIP-0370 recommendation, we use the fallback locktime as-is, ignoring each input's
// preferred height/block locktime. If that's relevant, the client must set the fallback
// locktime to the appropriate value before calling sign_psbt.
result_len = call_get_merkleized_map_value(dc,
&global_map,
(uint8_t[]){PSBT_GLOBAL_FALLBACK_LOCKTIME},
1,
raw_result,
sizeof(raw_result));
if (result_len == -1) {
st->locktime = 0;
} else if (result_len != 4) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
} else {
st->locktime = read_u32_le(raw_result, 0);
}
}
uint8_t hmac_or =
0; // the binary OR of all the hmac bytes (so == 0 iff the hmac is identically 0)
for (int i = 0; i < 32; i++) {
hmac_or = hmac_or | wallet_hmac[i];
}
if (hmac_or != 0) {
// Verify hmac
if (!check_wallet_hmac(wallet_id, wallet_hmac)) {
PRINTF("Incorrect hmac\n");
SEND_SW(dc, SW_SIGNATURE_FAIL);
return false;
}
st->is_wallet_canonical = false;
} else {
st->is_wallet_canonical = true;
}
{
// Fetch the serialized wallet policy from the client
uint8_t serialized_wallet_policy[MAX_WALLET_POLICY_SERIALIZED_LENGTH];
int serialized_wallet_policy_len = call_get_preimage(dc,
wallet_id,
serialized_wallet_policy,
sizeof(serialized_wallet_policy));
if (serialized_wallet_policy_len < 0) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
buffer_t serialized_wallet_policy_buf =
buffer_create(serialized_wallet_policy, serialized_wallet_policy_len);
uint8_t policy_map_descriptor[MAX_DESCRIPTOR_TEMPLATE_LENGTH];
if (0 > read_and_parse_wallet_policy(dc,
&serialized_wallet_policy_buf,
&wallet_header,
policy_map_descriptor,
st->wallet_policy_map_bytes,
sizeof(st->wallet_policy_map_bytes))) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
st->wallet_header_version = wallet_header.version;
memcpy(st->wallet_header_keys_info_merkle_root,
wallet_header.keys_info_merkle_root,
sizeof(wallet_header.keys_info_merkle_root));
st->wallet_header_n_keys = wallet_header.n_keys;
if (st->is_wallet_canonical) {
// verify that the policy is indeed a canonical one that is allowed by default
if (st->wallet_header_n_keys != 1) {
PRINTF("Non-standard policy, it should only have 1 key\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
int address_type = get_policy_address_type(&st->wallet_policy_map);
if (address_type == -1) {
PRINTF("Non-standard policy, and no hmac provided\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// Based on the address type, we set the expected bip44 purpose for this canonical
// wallet
int bip44_purpose = get_bip44_purpose(address_type);
if (bip44_purpose < 0) {
SEND_SW(dc, SW_BAD_STATE);
return false;
}
// We check that the pubkey has indeed 3 derivation steps, and it follows bip44
// standards We skip checking that we can indeed deriva the same pubkey (no security
// risk here, as the xpub itself isn't really used for the canonical wallet policies).
policy_map_key_info_t key_info;
{
char key_info_str[MAX_POLICY_KEY_INFO_LEN];
int key_info_len =
call_get_merkle_leaf_element(dc,
st->wallet_header_keys_info_merkle_root,
st->wallet_header_n_keys,
0,
(uint8_t *) key_info_str,
sizeof(key_info_str));
if (key_info_len == -1) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
buffer_t key_info_buffer = buffer_create(key_info_str, key_info_len);
if (parse_policy_map_key_info(&key_info_buffer,
&key_info,
st->wallet_header_version) == -1) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
}
uint32_t coin_types[2] = {BIP44_COIN_TYPE, BIP44_COIN_TYPE_2};
if (key_info.master_key_derivation_len != 3 ||
!is_pubkey_path_standard(key_info.master_key_derivation,
key_info.master_key_derivation_len,
bip44_purpose,
coin_types,
2)) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
}
}
// Swap feature: check that wallet is canonical
if (G_swap_state.called_from_swap && !st->is_wallet_canonical) {
PRINTF("Must be a canonical wallet for swap feature\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// If it's not a canonical wallet, ask the user for confirmation, and abort if they deny
if (!st->is_wallet_canonical && !ui_authorize_wallet_spend(dc, wallet_header.name)) {
SEND_SW(dc, SW_DENY);
ui_post_processing_confirm_wallet_spend(dc, false);
return false;
}
st->master_key_fingerprint = crypto_get_master_key_fingerprint();
if (!st->is_wallet_canonical) {
ui_post_processing_confirm_wallet_spend(dc, true);
}
return true;
}
static bool __attribute__((noinline))
fill_placeholder_info_if_internal(dispatcher_context_t *dc,
sign_psbt_state_t *st,
placeholder_info_t *placeholder_info) {
policy_map_key_info_t key_info;
{
uint8_t key_info_str[MAX_POLICY_KEY_INFO_LEN];
int key_info_len = call_get_merkle_leaf_element(dc,
st->wallet_header_keys_info_merkle_root,
st->wallet_header_n_keys,
placeholder_info->placeholder.key_index,
key_info_str,
sizeof(key_info_str));
if (key_info_len < 0) {
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
}
// Make a sub-buffer for the pubkey info
buffer_t key_info_buffer = buffer_create(key_info_str, key_info_len);
if (parse_policy_map_key_info(&key_info_buffer, &key_info, st->wallet_header_version) ==
-1) {
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
}
}
uint32_t fpr = read_u32_be(key_info.master_key_fingerprint, 0);
if (fpr != st->master_key_fingerprint) {
return false;
}
{
// it could be a collision on the fingerprint; we verify that we can actually generate
// the same pubkey
char pubkey_derived[MAX_SERIALIZED_PUBKEY_LENGTH + 1];
int serialized_pubkey_len =
get_serialized_extended_pubkey_at_path(key_info.master_key_derivation,
key_info.master_key_derivation_len,
BIP32_PUBKEY_VERSION,
pubkey_derived,
&placeholder_info->pubkey);
if (serialized_pubkey_len == -1) {
SEND_SW(dc, SW_BAD_STATE);
return false;
}
if (strncmp(key_info.ext_pubkey, pubkey_derived, MAX_SERIALIZED_PUBKEY_LENGTH) != 0) {
return false;
}
placeholder_info->key_derivation_length = key_info.master_key_derivation_len;
for (int i = 0; i < key_info.master_key_derivation_len; i++) {
placeholder_info->key_derivation[i] = key_info.master_key_derivation[i];
}
placeholder_info->fingerprint = read_u32_be(key_info.master_key_fingerprint, 0);
}
return true;
}
// finds the first placeholder that corresponds to an internal key
static bool find_first_internal_key_placeholder(dispatcher_context_t *dc,
sign_psbt_state_t *st,
placeholder_info_t *placeholder_info) {
placeholder_info->cur_index = 0;
// find and parse our registered key info in the wallet
while (true) {
int n_key_placeholders = get_key_placeholder_by_index(&st->wallet_policy_map,
placeholder_info->cur_index,
NULL,
&placeholder_info->placeholder);
if (n_key_placeholders < 0) {
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
}
if (placeholder_info->cur_index >= n_key_placeholders) {
// all keys have been processed
break;
}
if (fill_placeholder_info_if_internal(dc, st, placeholder_info)) {
return true;
}
// Not an internal key, move on
++placeholder_info->cur_index;
}
PRINTF("No internal key found in wallet policy");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
typedef struct {
placeholder_info_t *placeholder_info;
input_info_t *input;
} input_keys_callback_data_t;
/**
* Callback to process all the keys of the current input map.
* Keeps track if the current input has a witness_utxo and/or a redeemScript.
*/
static void input_keys_callback(dispatcher_context_t *dc,
input_keys_callback_data_t *callback_data,
const merkleized_map_commitment_t *map_commitment,
int i,
buffer_t *data) {
size_t data_len = data->size - data->offset;
if (data_len >= 1) {
uint8_t key_type;
buffer_read_u8(data, &key_type);
if (key_type == PSBT_IN_WITNESS_UTXO) {
callback_data->input->has_witnessUtxo = true;
} else if (key_type == PSBT_IN_NON_WITNESS_UTXO) {
callback_data->input->has_nonWitnessUtxo = true;
} else if (key_type == PSBT_IN_REDEEM_SCRIPT) {
callback_data->input->has_redeemScript = true;
} else if (key_type == PSBT_IN_SIGHASH_TYPE) {
callback_data->input->has_sighash_type = true;
} else if ((key_type == PSBT_IN_BIP32_DERIVATION ||
key_type == PSBT_IN_TAP_BIP32_DERIVATION) &&
!callback_data->input->in_out.placeholder_found) {
if (0 >
read_change_and_index_from_psbt_bip32_derivation(dc,
callback_data->placeholder_info,
&callback_data->input->in_out,
key_type,
data,
map_commitment,
i)) {
callback_data->input->in_out.unexpected_pubkey_error = true;
}
}
}
}
static bool __attribute__((noinline))
preprocess_inputs(dispatcher_context_t *dc,
sign_psbt_state_t *st,
uint8_t internal_inputs[static BITVECTOR_REAL_SIZE(MAX_N_INPUTS_CAN_SIGN)]) {
LOG_PROCESSOR(__FILE__, __LINE__, __func__);
memset(internal_inputs, 0, BITVECTOR_REAL_SIZE(MAX_N_INPUTS_CAN_SIGN));
placeholder_info_t placeholder_info;
memset(&placeholder_info, 0, sizeof(placeholder_info));
if (!find_first_internal_key_placeholder(dc, st, &placeholder_info)) return false;
// process each input
for (unsigned int cur_input_index = 0; cur_input_index < st->n_inputs; cur_input_index++) {
input_info_t input;
memset(&input, 0, sizeof(input));
input_keys_callback_data_t callback_data = {.input = &input,
.placeholder_info = &placeholder_info};
int res = call_get_merkleized_map_with_callback(
dc,
(void *) &callback_data,
st->inputs_root,
st->n_inputs,
cur_input_index,
(merkle_tree_elements_callback_t) input_keys_callback,
&input.in_out.map);
if (res < 0) {
PRINTF("Failed to process input map\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
if (input.in_out.unexpected_pubkey_error) {
PRINTF("Unexpected pubkey length\n"); // only compressed pubkeys are supported
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// either witness utxo or non-witness utxo (or both) must be present.
if (!input.has_nonWitnessUtxo && !input.has_witnessUtxo) {
PRINTF("No witness utxo nor non-witness utxo present in input.\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// validate non-witness utxo (if present) and witness utxo (if present)
if (input.has_nonWitnessUtxo) {
uint8_t prevout_hash[32];
// check if the prevout_hash of the transaction matches the computed one from the
// non-witness utxo
if (0 > call_get_merkleized_map_value(dc,
&input.in_out.map,
(uint8_t[]){PSBT_IN_PREVIOUS_TXID},
1,
prevout_hash,
sizeof(prevout_hash))) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// request non-witness utxo, and get the prevout's value and scriptpubkey
if (0 > get_amount_scriptpubkey_from_psbt_nonwitness(dc,
&input.in_out.map,
&input.prevout_amount,
input.in_out.scriptPubKey,
&input.in_out.scriptPubKey_len,
prevout_hash)) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
st->inputs_total_amount += input.prevout_amount;
}
if (input.has_witnessUtxo) {
size_t wit_utxo_scriptPubkey_len;
uint8_t wit_utxo_scriptPubkey[MAX_PREVOUT_SCRIPTPUBKEY_LEN];
uint64_t wit_utxo_prevout_amount;
if (0 > get_amount_scriptpubkey_from_psbt_witness(dc,
&input.in_out.map,
&wit_utxo_prevout_amount,
wit_utxo_scriptPubkey,
&wit_utxo_scriptPubkey_len)) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
};
if (input.has_nonWitnessUtxo) {
// we already know the scriptPubKey, but we double check that it matches
if (input.in_out.scriptPubKey_len != wit_utxo_scriptPubkey_len ||
memcmp(input.in_out.scriptPubKey,
wit_utxo_scriptPubkey,
wit_utxo_scriptPubkey_len) != 0 ||
input.prevout_amount != wit_utxo_prevout_amount) {
PRINTF(
"scriptPubKey or amount in non-witness utxo doesn't match with witness "
"utxo\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
} else {
// we extract the scriptPubKey and prevout amount from the witness utxo
st->inputs_total_amount += wit_utxo_prevout_amount;
input.prevout_amount = wit_utxo_prevout_amount;
input.in_out.scriptPubKey_len = wit_utxo_scriptPubkey_len;
memcpy(input.in_out.scriptPubKey, wit_utxo_scriptPubkey, wit_utxo_scriptPubkey_len);
}
}
// check if the input is internal; if not, continue
int is_internal = is_in_out_internal(dc, st, &input.in_out, true);
if (is_internal < 0) {
PRINTF("Error checking if input %d is internal\n", cur_input_index);
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
} else if (is_internal == 0) {
PRINTF("INPUT %d is external\n", cur_input_index);
continue;
}
bitvector_set(internal_inputs, cur_input_index, 1);
int segwit_version = get_policy_segwit_version(&st->wallet_policy_map);
// For legacy inputs, the non-witness utxo must be present
if (segwit_version == -1 && !input.has_nonWitnessUtxo) {
PRINTF("Non-witness utxo missing for legacy input\n");
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
// For segwitv0 inputs, the non-witness utxo _should_ be present; we show a warning
// to the user otherwise, but we continue nonetheless on approval
if (segwit_version == 0 && !input.has_nonWitnessUtxo) {