2
2
#include "../gui.h"
3
3
#include "../jade_assert.h"
4
4
#include "../jade_tasks.h"
5
+ #include "../jade_wally_verify.h"
6
+ #include "../keychain.h"
5
7
#include "../process.h"
6
8
#include "../serial.h"
7
9
#include "../ui.h"
8
10
#include "../utils/malloc_ext.h"
11
+ #include "../utils/network.h"
9
12
#include "usbhmsc.h"
13
+ #include <ctype.h>
10
14
#include <dirent.h>
11
15
#include <errno.h>
12
16
#include <stdbool.h>
15
19
#include <sys/stat.h>
16
20
#include <sys/types.h>
17
21
22
+ #include <wally_psbt.h>
23
+
18
24
gui_activity_t * make_usb_connect_activity (const char * title );
19
25
void await_qr_help_activity (const char * url );
20
26
27
+ // PSBT serialisation functions
28
+ bool deserialise_psbt (const uint8_t * bytes , size_t bytes_len , struct wally_psbt * * psbt_out );
29
+ bool serialise_psbt (const struct wally_psbt * psbt , uint8_t * * output , size_t * output_len );
30
+ int sign_psbt (const char * network , struct wally_psbt * psbt , const char * * errmsg );
31
+
21
32
#define MAX_FILENAME_SIZE 256
22
33
#define MAX_FILE_ENTRIES 64
23
34
35
+ #define MIN_PSBT_FILE_SIZE 32
36
+ #define MAX_PSBT_FILE_SIZE MAX_INPUT_MSG_SIZE
37
+
24
38
static const char FW_SUFFIX [] = "_fw.bin" ;
25
39
static const char HASH_SUFFIX [] = ".hash" ;
26
40
41
+ static const char PSBT_SUFFIX [] = ".psbt" ;
42
+ static const char PSBT_TXT_SUFFIX [] = ".psbt.txt" ;
43
+
27
44
#define STR_ENDSWITH (str , str_len , suffix , suffix_len ) \
28
45
(str && str_len > suffix_len && str[str_len] == '\0' && !memcmp(str + str_len - suffix_len, suffix, suffix_len))
29
46
@@ -76,6 +93,24 @@ static size_t read_file_to_buffer(const char* filename, uint8_t* buffer, size_t
76
93
return bytes_read ;
77
94
}
78
95
96
+ static size_t write_buffer_to_file (const char * filename , const uint8_t * buffer , size_t buf_len )
97
+ {
98
+ JADE_ASSERT (filename );
99
+ JADE_ASSERT (buffer );
100
+ JADE_ASSERT (buf_len );
101
+
102
+ FILE * fp = fopen (filename , "wb" );
103
+ if (fp == NULL ) {
104
+ return 0 ;
105
+ }
106
+
107
+ const size_t bytes_written = fwrite (buffer , 1 , buf_len , fp );
108
+ fclose (fp );
109
+
110
+ JADE_ASSERT (bytes_written == buf_len );
111
+ return bytes_written ;
112
+ }
113
+
79
114
// Display list of files, and return if user selects one
80
115
// Must be passed predicate to filter filenames
81
116
// NOTE: 'extra_path' (input) is relative to the mount point, but 'filename' (output) will be the full path including
@@ -668,3 +703,145 @@ bool usbstorage_firmware_ota(const char* extra_path)
668
703
const bool is_async = true;
669
704
return handle_usbstorage_action ("Firmware Upgrade" , initiate_usb_ota , extra_path , is_async );
670
705
}
706
+
707
+ // Sign PSBT
708
+
709
+ static bool is_psbt_binary_file (const char * path , const char * filename , const size_t filename_len )
710
+ {
711
+ return STR_ENDSWITH (filename , filename_len , PSBT_SUFFIX , strlen (PSBT_SUFFIX ));
712
+ }
713
+
714
+ static bool is_psbt_txt_file (const char * path , const char * filename , const size_t filename_len )
715
+ {
716
+ return STR_ENDSWITH (filename , filename_len , PSBT_TXT_SUFFIX , strlen (PSBT_TXT_SUFFIX ));
717
+ }
718
+
719
+ static bool is_psbt_file (const char * path , const char * filename , const size_t filename_len )
720
+ {
721
+ return is_psbt_binary_file (path , filename , filename_len ) || is_psbt_txt_file (path , filename , filename_len );
722
+ }
723
+
724
+ static bool sign_usb_psbt (const char * extra_path )
725
+ {
726
+ // extra_path is optional
727
+
728
+ char filename [MAX_FILENAME_SIZE ];
729
+ if (!select_file_from_filtered_list ("Select PSBT" , extra_path , is_psbt_file , filename , sizeof (filename ))) {
730
+ return false;
731
+ }
732
+
733
+ // Sanity check file size
734
+ size_t psbt_len = get_file_size (filename );
735
+ if (psbt_len < MIN_PSBT_FILE_SIZE ) {
736
+ const char * message [] = { "Invalid PSBT file" };
737
+ await_error_activity (message , 1 );
738
+ return false;
739
+ }
740
+ if (psbt_len > MAX_PSBT_FILE_SIZE ) {
741
+ const char * message [] = { "PSBT file too large" };
742
+ await_error_activity (message , 1 );
743
+ return false;
744
+ }
745
+
746
+ bool retval = false;
747
+ struct wally_psbt * psbt = NULL ;
748
+ const size_t filename_len = strlen (filename );
749
+ const bool b64 = is_psbt_txt_file ("" , filename , filename_len );
750
+
751
+ uint8_t * psbt_bytes = JADE_MALLOC_PREFER_SPIRAM (psbt_len );
752
+ if (b64 ) {
753
+ // Load from base64 text file
754
+ char * const psbt64 = JADE_MALLOC_PREFER_SPIRAM (psbt_len + 1 );
755
+ const size_t bytes_read = read_file_to_buffer (filename , (uint8_t * )psbt64 , psbt_len );
756
+ JADE_ASSERT (bytes_read == psbt_len );
757
+
758
+ // Add trailing terminator and trim any trailing whitespace
759
+ do {
760
+ psbt64 [psbt_len ] = '\0' ;
761
+ } while (isspace ((unsigned char )(psbt64 [-- psbt_len ])));
762
+
763
+ size_t written = 0 ;
764
+ const int wret = wally_base64_to_bytes (psbt64 , 0 , psbt_bytes , psbt_len , & written );
765
+ free (psbt64 );
766
+
767
+ if (wret != WALLY_OK || !written || written > psbt_len ) {
768
+ const char * message [] = { "Failed to decode" , "PSBT base64" };
769
+ await_error_activity (message , 2 );
770
+ goto cleanup ;
771
+ }
772
+ psbt_len = written ;
773
+ } else {
774
+ // Read bytes from binary file
775
+ const size_t bytes_read = read_file_to_buffer (filename , psbt_bytes , psbt_len );
776
+ JADE_ASSERT (bytes_read == psbt_len );
777
+ }
778
+
779
+ // Deserialise bytes
780
+ if (!deserialise_psbt (psbt_bytes , psbt_len , & psbt ) || !psbt ) {
781
+ const char * message [] = { "Failed to load PSBT" };
782
+ await_error_activity (message , 1 );
783
+ goto cleanup ;
784
+ }
785
+
786
+ // Free bytes loaded from file
787
+ free (psbt_bytes );
788
+ psbt_bytes = NULL ;
789
+ psbt_len = 0 ;
790
+
791
+ // Sign PSBT
792
+ const char * errmsg = NULL ;
793
+ const char * network = keychain_get_network_type_restriction () == NETWORK_TYPE_TEST ? TAG_TESTNET : TAG_MAINNET ;
794
+ const int errcode = sign_psbt (network , psbt , & errmsg );
795
+ if (errcode ) {
796
+ if (errcode != CBOR_RPC_USER_CANCELLED ) {
797
+ const char * message [] = { errmsg };
798
+ await_error_activity (message , 1 );
799
+ }
800
+ goto cleanup ;
801
+ }
802
+
803
+ // Write to file
804
+ // FIXME: create a new file ?
805
+ if (b64 ) {
806
+ // Encode to base64
807
+ char * psbt64 = NULL ;
808
+ if (wally_psbt_to_base64 (psbt , 0 , & psbt64 ) != WALLY_OK || !psbt64 ) {
809
+ const char * message [] = { "Failed to" , "serialise PSBT" };
810
+ await_error_activity (message , 2 );
811
+ goto cleanup ;
812
+ }
813
+ write_buffer_to_file (filename , (const uint8_t * )psbt64 , strlen (psbt64 ));
814
+ JADE_WALLY_VERIFY (wally_free_string (psbt64 ));
815
+ } else {
816
+ // Serialise signed PSBT to bytes
817
+ if (!serialise_psbt (psbt , & psbt_bytes , & psbt_len )) {
818
+ const char * message [] = { "Failed to" , "serialise PSBT" };
819
+ await_error_activity (message , 2 );
820
+ goto cleanup ;
821
+ }
822
+ write_buffer_to_file (filename , psbt_bytes , psbt_len );
823
+ }
824
+
825
+ const size_t mount_point_len = strlen (USBSTORAGE_MOUNT_POINT );
826
+ JADE_ASSERT (filename_len > mount_point_len );
827
+ JADE_ASSERT (!memcmp (filename , USBSTORAGE_MOUNT_POINT , mount_point_len ));
828
+ const char * message [] = { "PSBT file saved:" , filename + mount_point_len + 1 };
829
+ await_error_activity (message , 2 );
830
+ retval = true;
831
+
832
+ cleanup :
833
+ JADE_WALLY_VERIFY (wally_psbt_free (psbt ));
834
+ free (psbt_bytes );
835
+
836
+ return retval ;
837
+ }
838
+
839
+ // Sign PSBT file, and write updated file back to the usb-storage directory.
840
+ // Accepts binary PSBT file ('xxx.psbt') or base64-encoded PSBT file ('xxx.psbt.txt').
841
+ // After any signatures are added, the file is written in the same format.
842
+ bool usbstorage_sign_psbt (const char * extra_path )
843
+ {
844
+ // extra_path is optional
845
+ const bool is_async = false;
846
+ return handle_usbstorage_action ("Sign PSBT" , sign_usb_psbt , extra_path , is_async );
847
+ }
0 commit comments