blob: fdd4287aacd089e49e748d921b6bbdf57bb1e0ff [file] [log] [blame]
Piotr Nowicki9370f902020-03-13 14:43:22 +01001/*
2 * MbedTLS SSL context deserializer from base64 code
3 *
4 * Copyright (C) 2006-2020, ARM Limited, All Rights Reserved
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * This file is part of mbed TLS (https://tls.mbed.org)
20 */
21
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +010022#include <stdio.h>
23#include <stdlib.h>
Piotr Nowicki14d31052020-03-16 14:05:22 +010024#include <stdint.h>
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +010025#include <stdarg.h>
26#include <string.h>
Piotr Nowickic7d681c2020-03-17 09:51:31 +010027#include "mbedtls/error.h"
28#include "mbedtls/base64.h"
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +010029
30/*
31 * This program version
32 */
33#define PROG_NAME "ssl_base64_dump"
34#define VER_MAJOR 0
35#define VER_MINOR 1
36
37/*
Piotr Nowicki6b2baf92020-03-17 15:36:52 +010038 * Flags copied from the mbedTLS library.
39 */
40#define SESSION_CONFIG_TIME_BIT ( 1 << 0 )
41#define SESSION_CONFIG_CRT_BIT ( 1 << 1 )
42#define SESSION_CONFIG_CLIENT_TICKET_BIT ( 1 << 2 )
43#define SESSION_CONFIG_MFL_BIT ( 1 << 3 )
44#define SESSION_CONFIG_TRUNC_HMAC_BIT ( 1 << 4 )
45#define SESSION_CONFIG_ETM_BIT ( 1 << 5 )
46#define SESSION_CONFIG_TICKET_BIT ( 1 << 6 )
47
48#define CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT ( 1 << 0 )
49#define CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT ( 1 << 1 )
50#define CONTEXT_CONFIG_DTLS_ANTI_REPLAY_BIT ( 1 << 2 )
51#define CONTEXT_CONFIG_ALPN_BIT ( 1 << 3 )
52
Piotr Nowickiab3ecd82020-03-18 15:12:41 +010053#define TRANSFORM_RANDBYTE_LEN 64
54
Piotr Nowicki6b2baf92020-03-17 15:36:52 +010055/*
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +010056 * Global values
57 */
58FILE *b64_file = NULL; /* file with base64 codes to deserialize */
59char debug = 0; /* flag for debug messages */
60
61/*
62 * Basic printing functions
63 */
64void print_version( )
65{
66 printf( "%s v%d.%d\n", PROG_NAME, VER_MAJOR, VER_MINOR );
67}
68
69void print_usage( )
70{
71 print_version();
72 printf(
73 "Usage:\n"
74 "\t-f path - Path to the file with base64 code\n"
75 "\t-v - Show version\n"
76 "\t-h - Show this usage\n"
77 "\t-d - Print more information\n"
78 "\n"
79 );
80}
81
82void printf_dbg( const char *str, ... )
83{
84 if( debug )
85 {
86 va_list args;
87 va_start( args, str );
88 printf( "debug: " );
89 vprintf( str, args );
90 fflush( stdout );
91 va_end( args );
92 }
93}
94
95void printf_err( const char *str, ... )
96{
97 va_list args;
98 va_start( args, str );
99 fprintf( stderr, "ERROR: " );
100 vfprintf( stderr, str, args );
101 fflush( stderr );
102 va_end( args );
103}
104
105/*
106 * Exit from the program in case of error
107 */
108void error_exit()
109{
110 if( NULL != b64_file )
111 {
112 fclose( b64_file );
113 }
114 exit( -1 );
115}
116
117/*
118 * This function takes the input arguments of this program
119 */
120void parse_arguments( int argc, char *argv[] )
121{
122 int i = 1;
123
124 if( argc < 2 )
125 {
126 print_usage();
127 error_exit();
128 }
129
130 while( i < argc )
131 {
132 if( strcmp( argv[i], "-d" ) == 0 )
133 {
134 debug = 1;
135 }
136 else if( strcmp( argv[i], "-h" ) == 0 )
137 {
138 print_usage();
139 }
140 else if( strcmp( argv[i], "-v" ) == 0 )
141 {
142 print_version();
143 }
144 else if( strcmp( argv[i], "-f" ) == 0 )
145 {
146 if( ++i >= argc )
147 {
148 printf_err( "File path is empty\n" );
149 error_exit();
150 }
151
152 if( ( b64_file = fopen( argv[i], "r" ) ) == NULL )
153 {
154 printf_err( "Cannot find file \"%s\"\n", argv[i] );
155 error_exit();
156 }
157 }
158 else
159 {
160 print_usage();
161 error_exit();
162 }
163
164 i++;
165 }
166}
167
Piotr Nowicki14d31052020-03-16 14:05:22 +0100168/*
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100169 * This function prints base64 code to the stdout
170 */
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100171void print_b64( const uint8_t *b, size_t len )
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100172{
173 size_t i = 0;
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100174 const uint8_t *end = b + len;
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100175 printf("\t");
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100176 while( b < end )
177 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100178 if( ++i > 75 )
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100179 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100180 printf( "\n\t" );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100181 i = 0;
182 }
183 printf( "%c", *b++ );
184 }
185 printf( "\n" );
186 fflush( stdout );
187}
188
189/*
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100190 * This function prints hex code from the buffer to the stdout.
191 */
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100192void print_hex( const uint8_t *b, size_t len )
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100193{
194 size_t i = 0;
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100195 const uint8_t *end = b + len;
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100196 printf("\t");
197 while( b < end )
198 {
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100199 printf( "%02X ", (uint8_t) *b++ );
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100200 if( ++i > 25 )
201 {
202 printf("\n\t");
203 i = 0;
204 }
205 }
206 printf("\n");
207 fflush(stdout);
208}
209
210/*
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100211 * Print the input string if the bit is set in the value
212 */
213void print_if_bit( const char *str, int bit, int val )
214{
215 if( bit & val )
216 {
217 printf( "\t%s\n", str );
218 }
219}
220
221/*
Piotr Nowicki14d31052020-03-16 14:05:22 +0100222 * Read next base64 code from the 'b64_file'. The 'b64_file' must be opened
223 * previously. After each call to this function, the internal file position
224 * indicator of the global b64_file is advanced.
225 *
226 * /p b64 buffer for input data
227 * /p max_len the maximum number of bytes to write
228 *
229 * \retval number of bytes written in to the b64 buffer or 0 in case no more
230 * data was found
231 */
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100232size_t read_next_b64_code( uint8_t *b64, size_t max_len )
Piotr Nowicki14d31052020-03-16 14:05:22 +0100233{
234 size_t len = 0;
235 uint32_t missed = 0;
236 char pad = 0;
237 char c = 0;
238
239 while( EOF != c )
240 {
241 char c_valid = 0;
242
243 c = (char) fgetc( b64_file );
244
245 if( pad == 1 )
246 {
247 if( c == '=' )
248 {
249 c_valid = 1;
250 pad = 2;
251 }
252 }
253 else if( ( c >= 'A' && c <= 'Z' ) ||
254 ( c >= 'a' && c <= 'z' ) ||
255 ( c >= '0' && c <= '9' ) ||
256 c == '+' || c == '/' )
257 {
258 c_valid = 1;
259 }
260 else if( c == '=' )
261 {
262 c_valid = 1;
263 pad = 1;
264 }
265 else if( c == '-' )
266 {
267 c = '+';
268 c_valid = 1;
269 }
270 else if( c == '_' )
271 {
272 c = '/';
273 c_valid = 1;
274 }
275
276 if( c_valid )
277 {
278 if( len < max_len )
279 {
280 b64[ len++ ] = c;
281 }
282 else
283 {
284 missed++;
285 }
286 }
287 else if( len > 0 )
288 {
289 if( missed > 0 )
290 {
291 printf_err( "Buffer for the base64 code is too small. Missed %u characters\n", missed );
292 }
293 return len;
294 }
295 }
296
297 printf_dbg( "End of file\n" );
298 return 0;
299}
300
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100301/*
302 * This function deserializes and prints to the stdout all obtained information
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100303 * about the session from provided data. This function was built based on
304 * mbedtls_ssl_session_load(). mbedtls_ssl_session_load() could not be used
305 * due to dependencies on the mbedTLS configuration.
306 *
307 * The data structure in the buffer:
308 * uint64 start_time;
309 * uint8 ciphersuite[2]; // defined by the standard
310 * uint8 compression; // 0 or 1
311 * uint8 session_id_len; // at most 32
312 * opaque session_id[32];
313 * opaque master[48]; // fixed length in the standard
314 * uint32 verify_result;
315 * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert
316 * opaque ticket<0..2^24-1>; // length 0 means no ticket
317 * uint32 ticket_lifetime;
318 * uint8 mfl_code; // up to 255 according to standard
319 * uint8 trunc_hmac; // 0 or 1
320 * uint8 encrypt_then_mac; // 0 or 1
321 *
322 * /p ssl pointer to serialized session
323 * /p len number of bytes in the buffer
324 * /p session_cfg_flag session configuration flags
325 */
326void print_deserialized_ssl_session( const uint8_t *ssl, uint32_t len,
327 int session_cfg_flag )
328{
329 const uint8_t *end = ssl + len;
330 printf( "TODO\n" );
331}
332
333/*
334 * This function deserializes and prints to the stdout all obtained information
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100335 * about the context from provided data. This function was built based on
336 * mbedtls_ssl_context_load(). mbedtls_ssl_context_load() could not be used
337 * due to dependencies on the mbedTLS configuration and the configuration of
338 * the context when serialization was created.
339 *
340 * The data structure in the buffer:
341 * // session sub-structure
342 * opaque session<1..2^32-1>; // see mbedtls_ssl_session_save()
343 * // transform sub-structure
344 * uint8 random[64]; // ServerHello.random+ClientHello.random
345 * uint8 in_cid<0..2^8-1> // Connection ID: expected incoming value
346 * uint8 out_cid<0..2^8-1> // Connection ID: outgoing value to use
347 * // fields from ssl_context
348 * uint32 badmac_seen; // DTLS: number of records with failing MAC
349 * uint64 in_window_top; // DTLS: last validated record seq_num
350 * uint64 in_window; // DTLS: bitmask for replay protection
351 * uint8 disable_datagram_packing; // DTLS: only one record per datagram
352 * uint64 cur_out_ctr; // Record layer: outgoing sequence number
353 * uint16 mtu; // DTLS: path mtu (max outgoing fragment size)
354 * uint8 alpn_chosen<0..2^8-1> // ALPN: negotiated application protocol
355 *
356 * /p ssl pointer to serialized session
357 * /p len number of bytes in the buffer
358 */
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100359void print_deserialized_ssl_context( const uint8_t *ssl, size_t len )
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100360{
361 /* TODO: which versions are compatible */
362 /* TODO: add checking len */
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100363 const uint8_t *end = ssl + len;
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100364 int session_cfg_flag;
365 int context_cfg_flag;
366 uint32_t session_len;
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100367 /* TODO is DTLS compiled? */
368 char dtls_used = 1;
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100369
370 printf( "\nMbed TLS version:\n" );
371
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100372 printf( "\tmajor:\t%u\n", (uint32_t) *ssl++ );
373 printf( "\tminor:\t%u\n", (uint32_t) *ssl++ );
374 printf( "\tpath:\t%u\n", (uint32_t) *ssl++ );
375
376 printf( "\nEnabled session and context configuration:\n" );
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100377
378 session_cfg_flag = ( (int) ssl[0] << 8 ) | ( (int) ssl[1] );
379 ssl += 2;
380
381 context_cfg_flag = ( (int) ssl[0] << 16 ) |
382 ( (int) ssl[1] << 8 ) |
383 ( (int) ssl[2] ) ;
384 ssl += 3;
385
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100386 printf_dbg( "Session config flags 0x%04X\n", session_cfg_flag );
387 printf_dbg( "Context config flags 0x%06X\n", context_cfg_flag );
388
389 print_if_bit( "MBEDTLS_HAVE_TIME", SESSION_CONFIG_TIME_BIT, session_cfg_flag );
390 print_if_bit( "MBEDTLS_X509_CRT_PARSE_C", SESSION_CONFIG_CRT_BIT, session_cfg_flag );
391 print_if_bit( "MBEDTLS_SSL_MAX_FRAGMENT_LENGTH", SESSION_CONFIG_MFL_BIT, session_cfg_flag );
392 print_if_bit( "MBEDTLS_SSL_TRUNCATED_HMAC", SESSION_CONFIG_TRUNC_HMAC_BIT, session_cfg_flag );
393 print_if_bit( "MBEDTLS_SSL_ENCRYPT_THEN_MAC", SESSION_CONFIG_ETM_BIT, session_cfg_flag );
394 print_if_bit( "MBEDTLS_SSL_SESSION_TICKETS", SESSION_CONFIG_TICKET_BIT, session_cfg_flag );
395 print_if_bit( "MBEDTLS_SSL_SESSION_TICKETS and client", SESSION_CONFIG_CLIENT_TICKET_BIT, session_cfg_flag );
396
397 print_if_bit( "MBEDTLS_SSL_DTLS_CONNECTION_ID", CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT, context_cfg_flag );
398 print_if_bit( "MBEDTLS_SSL_DTLS_BADMAC_LIMIT", CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT, context_cfg_flag );
399 print_if_bit( "MBEDTLS_SSL_DTLS_ANTI_REPLAY", CONTEXT_CONFIG_DTLS_ANTI_REPLAY_BIT, context_cfg_flag );
400 print_if_bit( "MBEDTLS_SSL_ALPN", CONTEXT_CONFIG_ALPN_BIT, context_cfg_flag );
401
402 session_len = ( (uint32_t) ssl[0] << 24 ) |
403 ( (uint32_t) ssl[1] << 16 ) |
404 ( (uint32_t) ssl[2] << 8 ) |
405 ( (uint32_t) ssl[3] );
406 ssl += 4;
407 printf_dbg( "session length %u\n", session_len );
408
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100409 print_deserialized_ssl_session( ssl, session_len, session_cfg_flag );
410 ssl += session_len;
411
412 /* TODO ssl_populate_transform */
413 printf( "\nRandom bytes: \n");
414 print_hex( ssl, TRANSFORM_RANDBYTE_LEN );
415 printf( "TODO: ssl_populate_transform\n");
416 ssl += TRANSFORM_RANDBYTE_LEN;
417
418 if( CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT & context_cfg_flag )
419 {
420 uint8_t cid_len;
421 printf( "\nDTLS connection ID:\n" );
422
423 cid_len = *ssl++;
424 printf_dbg( "in_cid_len %u\n", (uint32_t) cid_len );
425
426 printf( "\tin_cid:" );
427 print_hex( ssl, cid_len );
428 ssl += cid_len;
429
430 cid_len = *ssl++;
431 printf_dbg( "out_cid_len %u\n", (uint32_t) cid_len );
432
433 printf( "\tout_cid:" );
434 print_hex( ssl, cid_len );
435 ssl += cid_len;
436 }
437
438 if( CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT & context_cfg_flag )
439 {
440 uint32_t badmac_seen = ( (uint32_t) ssl[0] << 24 ) |
441 ( (uint32_t) ssl[1] << 16 ) |
442 ( (uint32_t) ssl[2] << 8 ) |
443 ( (uint32_t) ssl[3] );
444 ssl += 4;
445 printf( "\tibadmac_seen: %d\n", badmac_seen );
446
447 printf( "\tin_window_top: " );
448 print_hex( ssl, 8 );
449 ssl += 8;
450
451 printf( "\twindow_top: " );
452 print_hex( ssl, 8 );
453 ssl += 8;
454 }
455
456 if( dtls_used )
457 {
458 printf( "\tDTLS datagram packing: %s\n",
459 ( ( *ssl++ ) == 0 ) ?
460 "enabled" : "disabled" );
461 }
462
463 printf( "\tcur_out_ctr: ");
464 print_hex( ssl, 8 );
465 ssl += 8;
466
467 if( dtls_used )
468 {
469 uint16_t mtu = ( ssl[0] << 8 ) | ssl[1];
470 ssl += 2;
471 printf( "\tMTU: %u\n", mtu );
472 }
473
474
475 if( CONTEXT_CONFIG_ALPN_BIT & context_cfg_flag )
476 {
477 uint8_t alpn_len = *ssl++;
478 if( alpn_len > 0 )
479 {
480 if( strlen( (const char*) ssl ) == alpn_len )
481 {
482 printf( "\talpn_chosen: %s\n", ssl );
483 }
484 else
485 {
486 printf_err( "\talpn_len is incorrect\n" );
487 }
488 ssl += alpn_len;
489 }
490 else
491 {
492 printf( "\talpn_chosen: not selected\n" );
493 }
494 }
495
496 /* TODO: check mbedtls_ssl_update_out_pointers( ssl, ssl->transform ); */
497 printf( "TODO: check mbedtls_ssl_update_out_pointers( ssl, ssl->transform );\n" );
498
499 if( 0 < ( end - ssl ) )
500 {
501 printf_dbg( "Left to analyze %u\n", (uint32_t)( end - ssl ) );
502 }
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100503 printf( "\n" );
504}
505
Piotr Nowicki9370f902020-03-13 14:43:22 +0100506int main( int argc, char *argv[] )
507{
Piotr Nowicki14d31052020-03-16 14:05:22 +0100508 enum { B64BUF_LEN = 4 * 1024 };
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100509 enum { SSLBUF_LEN = B64BUF_LEN * 3 / 4 + 1 };
510
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100511 uint8_t b64[ B64BUF_LEN ];
512 uint8_t ssl[ SSLBUF_LEN ];
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100513 uint32_t b64_counter = 0;
514
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +0100515 parse_arguments( argc, argv );
Piotr Nowicki9370f902020-03-13 14:43:22 +0100516
Piotr Nowicki14d31052020-03-16 14:05:22 +0100517 while( NULL != b64_file )
518 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100519 size_t ssl_len;
520 size_t b64_len = read_next_b64_code( b64, B64BUF_LEN );
521 if( b64_len > 0)
Piotr Nowicki14d31052020-03-16 14:05:22 +0100522 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100523 int ret;
524
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100525 printf( "%u. Desierializing:\n", ++b64_counter );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100526
527 if( debug )
528 {
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100529 printf( "\nBase64 code:\n" );
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100530 print_b64( b64, b64_len );
531 }
532
533 ret = mbedtls_base64_decode( ssl, SSLBUF_LEN, &ssl_len, b64, b64_len );
534 if( ret != 0)
535 {
536 mbedtls_strerror( ret, (char*) b64, B64BUF_LEN );
537 printf_err( "base64 code cannot be decoded - %s\n", b64 );
538 continue;
539 }
540
541 if( debug )
542 {
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100543 printf( "\nDecoded data in hex:\n");
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100544 print_hex( ssl, ssl_len );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100545 }
Piotr Nowicki14d31052020-03-16 14:05:22 +0100546
Piotr Nowickiab3ecd82020-03-18 15:12:41 +0100547 print_deserialized_ssl_context( ssl, ssl_len );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100548
Piotr Nowicki14d31052020-03-16 14:05:22 +0100549 }
550 else
551 {
552 fclose( b64_file );
553 b64_file = NULL;
554 }
555 }
556
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100557 printf_dbg( "Finish. Found %u base64 codes\n", b64_counter );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100558
Piotr Nowicki9370f902020-03-13 14:43:22 +0100559 return 0;
560}