@@ -72,30 +72,49 @@ bool verify_change(change_output_info_t *info, tx_output_t output) {
7272 * - read the bip32 path
7373 *
7474 * The outputs will be on the global context for sign tx (`tx_info`)
75+ *
76+ * returns a 16 bit error code if it fails and 0 on success
7577 **/
76- void read_change_info_v1 (buffer_t * cdata ) {
78+ uint16_t read_change_info_v1 (buffer_t * cdata ) {
7779 if (!buffer_read_u8 (cdata , & G_context .tx_info .change_len )) {
78- THROW ( SW_WRONG_DATA_LENGTH ) ;
80+ return SW_WRONG_DATA_LENGTH ;
7981 }
8082
8183 if (G_context .tx_info .change_len > TX_MAX_TOKENS + 1 ) {
8284 // Some token change is duplicated or we have more tokens than allowed
83- THROW ( SW_WRONG_DATA_LENGTH ) ;
85+ return SW_WRONG_DATA_LENGTH ;
8486 }
8587
88+ // TX_MAX_TOKENS + 1 because we allow 1 change for each token + HTR
89+ uint8_t known_indexes [TX_MAX_TOKENS + 1 ] = {0 };
90+ uint8_t indexes_len = 0 ;
91+
8692 for (uint8_t i = 0 ; i < G_context .tx_info .change_len ; i ++ ) {
8793 change_output_info_t * info = & G_context .tx_info .change_info [i ];
8894
8995 // 1 byte for change output index
9096 if (!buffer_read_u8 (cdata , & info -> index )) {
91- THROW ( SW_WRONG_DATA_LENGTH ) ;
97+ return SW_WRONG_DATA_LENGTH ;
9298 }
9399
100+ // check for duplicates
101+ for (uint8_t j = 0 ; j < indexes_len ; j ++ ) {
102+ if (info -> index == known_indexes [j ]) {
103+ // This output index was already read
104+ // This can happen if the change list is malformed.
105+ PRINTF ("[-] [sign_tx] Duplicate change output index %d\n" , info -> index );
106+ return SW_INVALID_TX ;
107+ }
108+ }
109+ known_indexes [indexes_len ++ ] = info -> index ;
110+
94111 // bip32 path (path length + path data)
95112 if (!buffer_read_bip32_path (cdata , & info -> path )) {
96- THROW ( SW_WRONG_DATA_LENGTH ) ;
113+ return SW_WRONG_DATA_LENGTH ;
97114 }
98115 }
116+
117+ return 0 ;
99118}
100119
101120/**
@@ -108,27 +127,29 @@ void read_change_info_v1(buffer_t *cdata) {
108127 * - read the bip32 path
109128 *
110129 * The outputs will be on the global context for sign tx (`tx_info`)
130+ *
131+ * returns a 16 bit error code if it fails and 0 on success
111132 **/
112- void read_change_info_old_protocol (uint8_t change_byte , buffer_t * cdata ) {
133+ uint16_t read_change_info_old_protocol (uint8_t change_byte , buffer_t * cdata ) {
113134 uint8_t buffer [1 + 4 * MAX_BIP32_PATH ] = {0 };
114135 // Old protocol only allow 1 change output
115136 // and we already checked that there is an output present
116137 G_context .tx_info .change_len = 1 ;
117138 change_output_info_t * info = & G_context .tx_info .change_info [0 ];
118139
119140 if (!buffer_read_u8 (cdata , & info -> index )) {
120- THROW ( SW_WRONG_DATA_LENGTH ) ;
141+ return SW_WRONG_DATA_LENGTH ;
121142 }
122143
123144 buffer [0 ] = change_byte & 0x0F ;
124145
125146 // validate that the path length is valid
126147 if (buffer [0 ] > MAX_BIP32_PATH ) {
127- THROW ( SW_WRONG_DATA_LENGTH ) ;
148+ return SW_WRONG_DATA_LENGTH ;
128149 }
129150 // check we have enough data to read the path
130151 if (cdata -> size - cdata -> offset < 4 * buffer [0 ]) {
131- THROW ( SW_WRONG_DATA_LENGTH ) ;
152+ return SW_WRONG_DATA_LENGTH ;
132153 }
133154
134155 // buffer+1 has 4*MAX_BIP32_PATH capacity
@@ -139,20 +160,24 @@ void read_change_info_old_protocol(uint8_t change_byte, buffer_t *cdata) {
139160
140161 // bip32 path (path length + path data)
141162 if (!buffer_read_bip32_path (& bufdata , & info -> path )) {
142- THROW ( SW_WRONG_DATA_LENGTH ) ;
163+ return SW_WRONG_DATA_LENGTH ;
143164 }
165+
166+ return 0 ;
144167}
145168
146169/**
147170 * Read change information
148171 *
149172 * First byte is the version byte for the protocol (for transactions)
150173 * It will be used to differentiate the old and new protocols
174+ *
175+ * returns a 16 bit error code if it fails and 0 on success
151176 **/
152- void read_change_info (buffer_t * cdata ) {
177+ uint16_t read_change_info (buffer_t * cdata ) {
153178 uint8_t proto_version ;
154179 if (!buffer_read_u8 (cdata , & proto_version )) {
155- THROW ( SW_WRONG_DATA_LENGTH ) ;
180+ return SW_WRONG_DATA_LENGTH ;
156181 }
157182
158183 // check first byte for backwards compatibility
@@ -166,19 +191,22 @@ void read_change_info(buffer_t *cdata) {
166191 if (proto_version == 0 ) {
167192 // old protocol, no change
168193 G_context .tx_info .change_len = 0 ;
169- return ;
194+ return 0 ;
170195 }
171196
197+ // Old protocol with change
172198 if (proto_version & 0x80 ) {
173- read_change_info_old_protocol (proto_version , cdata );
174- } else {
175- if (proto_version == 1 ) {
176- read_change_info_v1 (cdata );
177- } else {
178- // error
179- THROW (SW_INVALID_TX );
180- }
199+ return read_change_info_old_protocol (proto_version , cdata );
200+ }
201+
202+ // Protocol v1
203+ if (proto_version == 1 ) {
204+ return read_change_info_v1 (cdata );
181205 }
206+
207+ // error, unknown change protocol
208+ PRINTF ("[-] [sign_tx] Unknown change protocol version %d\n" , proto_version );
209+ return SW_INVALID_TX ;
182210}
183211
184212/**
@@ -194,8 +222,10 @@ void read_change_info(buffer_t *cdata) {
194222 * Obs: This is only on the first call with chunk=0, so it should never fail for lack of data
195223 *
196224 * The output will be on the global context for sign tx (`tx_info`)
225+ *
226+ * returns a 16 bit error code if it fails and 0 on success
197227 **/
198- void read_tx_data (buffer_t * cdata ) {
228+ uint16_t read_tx_data (buffer_t * cdata ) {
199229 if (!(buffer_read_u16 (cdata ,
200230 & G_context .tx_info .tx_version ,
201231 BE ) && // read version bytes (Big Endian)
@@ -205,8 +235,27 @@ void read_tx_data(buffer_t *cdata) {
205235 buffer_read_u8 (cdata , & G_context .tx_info .remaining_inputs ) &&
206236 buffer_read_u8 (cdata , & G_context .tx_info .outputs_len ))) {
207237 // if an error occurs reading
208- THROW (SW_WRONG_DATA_LENGTH );
238+ return SW_WRONG_DATA_LENGTH ;
239+ }
240+
241+ for (uint8_t i = 0 ; i < G_context .tx_info .change_len ; i ++ ) {
242+ // check that all change indexes are inside the outputs array
243+ if (G_context .tx_info .change_info [i ].index >= G_context .tx_info .outputs_len ) {
244+ PRINTF ("[-] [sign_tx] Change output index %d cannot be an output since length is %d\n" ,
245+ G_context .tx_info .change_info [i ].index ,
246+ G_context .tx_info .outputs_len );
247+ return SW_INVALID_TX ;
248+ }
209249 }
250+ if (G_context .tx_info .change_len > G_context .tx_info .outputs_len ) {
251+ // We have more change indexes than outputs, meaning the change list is malformed
252+ // This is unreachable since we do not allow duplicates and no index is higher than the
253+ // number of outputs even so we should check for this.
254+ PRINTF ("[-] [sign_tx] More change indexes than outputs\n" );
255+ return SW_INVALID_TX ;
256+ }
257+
258+ return 0 ;
210259}
211260
212261/**
@@ -482,13 +531,23 @@ bool receive_data(buffer_t *cdata, uint8_t chunk) {
482531 }
483532
484533 init_sign_tx_ctx ();
534+ uint16_t error_code = 0 ;
485535 // read change output and sighash
486- read_change_info (cdata );
536+ error_code = read_change_info (cdata );
537+ if (error_code != 0 ) {
538+ explicit_bzero (& G_context , sizeof (G_context ));
539+ THROW (error_code );
540+ }
487541 // sighash_all_hash won't move cdata.offset
488542 sighash_all_hash (cdata );
489- read_tx_data (cdata );
543+ error_code = read_tx_data (cdata );
544+ if (error_code != 0 ) {
545+ explicit_bzero (& G_context , sizeof (G_context ));
546+ THROW (error_code );
547+ }
490548
491549 if (!buffer_copy (cdata , G_context .tx_info .buffer , 300 - G_context .tx_info .buffer_len )) {
550+ explicit_bzero (& G_context , sizeof (G_context ));
492551 THROW (SW_WRONG_DATA_LENGTH );
493552 }
494553
@@ -502,6 +561,7 @@ bool receive_data(buffer_t *cdata, uint8_t chunk) {
502561 if (!buffer_copy (cdata ,
503562 G_context .tx_info .buffer + G_context .tx_info .buffer_len ,
504563 300 - G_context .tx_info .buffer_len )) {
564+ explicit_bzero (& G_context , sizeof (G_context ));
505565 THROW (SW_WRONG_DATA_LENGTH );
506566 }
507567 G_context .tx_info .buffer_len += cdata -> size - cdata -> offset ;
0 commit comments