blob: dc146f276dd74a9d98b6d511138908210e3dbfc7 [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
53/*
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +010054 * Global values
55 */
56FILE *b64_file = NULL; /* file with base64 codes to deserialize */
57char debug = 0; /* flag for debug messages */
58
59/*
60 * Basic printing functions
61 */
62void print_version( )
63{
64 printf( "%s v%d.%d\n", PROG_NAME, VER_MAJOR, VER_MINOR );
65}
66
67void print_usage( )
68{
69 print_version();
70 printf(
71 "Usage:\n"
72 "\t-f path - Path to the file with base64 code\n"
73 "\t-v - Show version\n"
74 "\t-h - Show this usage\n"
75 "\t-d - Print more information\n"
76 "\n"
77 );
78}
79
80void printf_dbg( const char *str, ... )
81{
82 if( debug )
83 {
84 va_list args;
85 va_start( args, str );
86 printf( "debug: " );
87 vprintf( str, args );
88 fflush( stdout );
89 va_end( args );
90 }
91}
92
93void printf_err( const char *str, ... )
94{
95 va_list args;
96 va_start( args, str );
97 fprintf( stderr, "ERROR: " );
98 vfprintf( stderr, str, args );
99 fflush( stderr );
100 va_end( args );
101}
102
103/*
104 * Exit from the program in case of error
105 */
106void error_exit()
107{
108 if( NULL != b64_file )
109 {
110 fclose( b64_file );
111 }
112 exit( -1 );
113}
114
115/*
116 * This function takes the input arguments of this program
117 */
118void parse_arguments( int argc, char *argv[] )
119{
120 int i = 1;
121
122 if( argc < 2 )
123 {
124 print_usage();
125 error_exit();
126 }
127
128 while( i < argc )
129 {
130 if( strcmp( argv[i], "-d" ) == 0 )
131 {
132 debug = 1;
133 }
134 else if( strcmp( argv[i], "-h" ) == 0 )
135 {
136 print_usage();
137 }
138 else if( strcmp( argv[i], "-v" ) == 0 )
139 {
140 print_version();
141 }
142 else if( strcmp( argv[i], "-f" ) == 0 )
143 {
144 if( ++i >= argc )
145 {
146 printf_err( "File path is empty\n" );
147 error_exit();
148 }
149
150 if( ( b64_file = fopen( argv[i], "r" ) ) == NULL )
151 {
152 printf_err( "Cannot find file \"%s\"\n", argv[i] );
153 error_exit();
154 }
155 }
156 else
157 {
158 print_usage();
159 error_exit();
160 }
161
162 i++;
163 }
164}
165
Piotr Nowicki14d31052020-03-16 14:05:22 +0100166/*
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100167 * This function prints base64 code to the stdout
168 */
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100169void print_b64( const unsigned char *b, size_t len )
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100170{
171 size_t i = 0;
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100172 const unsigned char *end = b + len;
173 printf("\t");
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100174 while( b < end )
175 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100176 if( ++i > 75 )
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100177 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100178 printf( "\n\t" );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100179 i = 0;
180 }
181 printf( "%c", *b++ );
182 }
183 printf( "\n" );
184 fflush( stdout );
185}
186
187/*
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100188 * This function prints hex code from the buffer to the stdout.
189 */
190void print_hex( const unsigned char *b, size_t len )
191{
192 size_t i = 0;
193 const unsigned char *end = b + len;
194 printf("\t");
195 while( b < end )
196 {
197 printf( "%02X ", (unsigned char) *b++ );
198 if( ++i > 25 )
199 {
200 printf("\n\t");
201 i = 0;
202 }
203 }
204 printf("\n");
205 fflush(stdout);
206}
207
208/*
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100209 * Print the input string if the bit is set in the value
210 */
211void print_if_bit( const char *str, int bit, int val )
212{
213 if( bit & val )
214 {
215 printf( "\t%s\n", str );
216 }
217}
218
219/*
Piotr Nowicki14d31052020-03-16 14:05:22 +0100220 * Read next base64 code from the 'b64_file'. The 'b64_file' must be opened
221 * previously. After each call to this function, the internal file position
222 * indicator of the global b64_file is advanced.
223 *
224 * /p b64 buffer for input data
225 * /p max_len the maximum number of bytes to write
226 *
227 * \retval number of bytes written in to the b64 buffer or 0 in case no more
228 * data was found
229 */
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100230size_t read_next_b64_code( unsigned char *b64, size_t max_len )
Piotr Nowicki14d31052020-03-16 14:05:22 +0100231{
232 size_t len = 0;
233 uint32_t missed = 0;
234 char pad = 0;
235 char c = 0;
236
237 while( EOF != c )
238 {
239 char c_valid = 0;
240
241 c = (char) fgetc( b64_file );
242
243 if( pad == 1 )
244 {
245 if( c == '=' )
246 {
247 c_valid = 1;
248 pad = 2;
249 }
250 }
251 else if( ( c >= 'A' && c <= 'Z' ) ||
252 ( c >= 'a' && c <= 'z' ) ||
253 ( c >= '0' && c <= '9' ) ||
254 c == '+' || c == '/' )
255 {
256 c_valid = 1;
257 }
258 else if( c == '=' )
259 {
260 c_valid = 1;
261 pad = 1;
262 }
263 else if( c == '-' )
264 {
265 c = '+';
266 c_valid = 1;
267 }
268 else if( c == '_' )
269 {
270 c = '/';
271 c_valid = 1;
272 }
273
274 if( c_valid )
275 {
276 if( len < max_len )
277 {
278 b64[ len++ ] = c;
279 }
280 else
281 {
282 missed++;
283 }
284 }
285 else if( len > 0 )
286 {
287 if( missed > 0 )
288 {
289 printf_err( "Buffer for the base64 code is too small. Missed %u characters\n", missed );
290 }
291 return len;
292 }
293 }
294
295 printf_dbg( "End of file\n" );
296 return 0;
297}
298
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100299/*
300 * This function deserializes and prints to the stdout all obtained information
301 * about the context from provided data. This function was built based on
302 * mbedtls_ssl_context_load(). mbedtls_ssl_context_load() could not be used
303 * due to dependencies on the mbedTLS configuration and the configuration of
304 * the context when serialization was created.
305 *
306 * The data structure in the buffer:
307 * // session sub-structure
308 * opaque session<1..2^32-1>; // see mbedtls_ssl_session_save()
309 * // transform sub-structure
310 * uint8 random[64]; // ServerHello.random+ClientHello.random
311 * uint8 in_cid<0..2^8-1> // Connection ID: expected incoming value
312 * uint8 out_cid<0..2^8-1> // Connection ID: outgoing value to use
313 * // fields from ssl_context
314 * uint32 badmac_seen; // DTLS: number of records with failing MAC
315 * uint64 in_window_top; // DTLS: last validated record seq_num
316 * uint64 in_window; // DTLS: bitmask for replay protection
317 * uint8 disable_datagram_packing; // DTLS: only one record per datagram
318 * uint64 cur_out_ctr; // Record layer: outgoing sequence number
319 * uint16 mtu; // DTLS: path mtu (max outgoing fragment size)
320 * uint8 alpn_chosen<0..2^8-1> // ALPN: negotiated application protocol
321 *
322 * /p ssl pointer to serialized session
323 * /p len number of bytes in the buffer
324 */
325void print_deserialized_ssl( const unsigned char *ssl, size_t len )
326{
327 /* TODO: which versions are compatible */
328 /* TODO: add checking len */
329 const unsigned char *end = ssl + len;
330 int session_cfg_flag;
331 int context_cfg_flag;
332 uint32_t session_len;
333
334 printf( "\nMbed TLS version:\n" );
335
336 printf( "\tmajor:\t%u\n", (unsigned int) *ssl++ );
337 printf( "\tminor:\t%u\n", (unsigned int) *ssl++ );
338 printf( "\tpath:\t%u\n", (unsigned int) *ssl++ );
339
340 session_cfg_flag = ( (int) ssl[0] << 8 ) | ( (int) ssl[1] );
341 ssl += 2;
342
343 context_cfg_flag = ( (int) ssl[0] << 16 ) |
344 ( (int) ssl[1] << 8 ) |
345 ( (int) ssl[2] ) ;
346 ssl += 3;
347
348 printf( "\nEnabled session and context configuration:\n" );
349 printf_dbg( "Session config flags 0x%04X\n", session_cfg_flag );
350 printf_dbg( "Context config flags 0x%06X\n", context_cfg_flag );
351
352 print_if_bit( "MBEDTLS_HAVE_TIME", SESSION_CONFIG_TIME_BIT, session_cfg_flag );
353 print_if_bit( "MBEDTLS_X509_CRT_PARSE_C", SESSION_CONFIG_CRT_BIT, session_cfg_flag );
354 print_if_bit( "MBEDTLS_SSL_MAX_FRAGMENT_LENGTH", SESSION_CONFIG_MFL_BIT, session_cfg_flag );
355 print_if_bit( "MBEDTLS_SSL_TRUNCATED_HMAC", SESSION_CONFIG_TRUNC_HMAC_BIT, session_cfg_flag );
356 print_if_bit( "MBEDTLS_SSL_ENCRYPT_THEN_MAC", SESSION_CONFIG_ETM_BIT, session_cfg_flag );
357 print_if_bit( "MBEDTLS_SSL_SESSION_TICKETS", SESSION_CONFIG_TICKET_BIT, session_cfg_flag );
358 print_if_bit( "MBEDTLS_SSL_SESSION_TICKETS and client", SESSION_CONFIG_CLIENT_TICKET_BIT, session_cfg_flag );
359
360 print_if_bit( "MBEDTLS_SSL_DTLS_CONNECTION_ID", CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT, context_cfg_flag );
361 print_if_bit( "MBEDTLS_SSL_DTLS_BADMAC_LIMIT", CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT, context_cfg_flag );
362 print_if_bit( "MBEDTLS_SSL_DTLS_ANTI_REPLAY", CONTEXT_CONFIG_DTLS_ANTI_REPLAY_BIT, context_cfg_flag );
363 print_if_bit( "MBEDTLS_SSL_ALPN", CONTEXT_CONFIG_ALPN_BIT, context_cfg_flag );
364
365 session_len = ( (uint32_t) ssl[0] << 24 ) |
366 ( (uint32_t) ssl[1] << 16 ) |
367 ( (uint32_t) ssl[2] << 8 ) |
368 ( (uint32_t) ssl[3] );
369 ssl += 4;
370 printf_dbg( "session length %u\n", session_len );
371
372 printf( "\n" );
373}
374
Piotr Nowicki9370f902020-03-13 14:43:22 +0100375int main( int argc, char *argv[] )
376{
Piotr Nowicki14d31052020-03-16 14:05:22 +0100377 enum { B64BUF_LEN = 4 * 1024 };
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100378 enum { SSLBUF_LEN = B64BUF_LEN * 3 / 4 + 1 };
379
380 unsigned char b64[ B64BUF_LEN ];
381 unsigned char ssl[ SSLBUF_LEN ];
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100382 uint32_t b64_counter = 0;
383
Piotr Nowicki88ebbbf2020-03-13 16:26:08 +0100384 parse_arguments( argc, argv );
Piotr Nowicki9370f902020-03-13 14:43:22 +0100385
Piotr Nowicki14d31052020-03-16 14:05:22 +0100386 while( NULL != b64_file )
387 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100388 size_t ssl_len;
389 size_t b64_len = read_next_b64_code( b64, B64BUF_LEN );
390 if( b64_len > 0)
Piotr Nowicki14d31052020-03-16 14:05:22 +0100391 {
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100392 int ret;
393
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100394 printf( "%u. Desierializing:\n", ++b64_counter );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100395
396 if( debug )
397 {
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100398 printf( "\nBase64 code:\n" );
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100399 print_b64( b64, b64_len );
400 }
401
402 ret = mbedtls_base64_decode( ssl, SSLBUF_LEN, &ssl_len, b64, b64_len );
403 if( ret != 0)
404 {
405 mbedtls_strerror( ret, (char*) b64, B64BUF_LEN );
406 printf_err( "base64 code cannot be decoded - %s\n", b64 );
407 continue;
408 }
409
410 if( debug )
411 {
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100412 printf( "\nDecoded data in hex:\n");
Piotr Nowickic7d681c2020-03-17 09:51:31 +0100413 print_hex( ssl, ssl_len );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100414 }
Piotr Nowicki14d31052020-03-16 14:05:22 +0100415
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100416 print_deserialized_ssl( ssl, ssl_len );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100417
Piotr Nowicki14d31052020-03-16 14:05:22 +0100418 }
419 else
420 {
421 fclose( b64_file );
422 b64_file = NULL;
423 }
424 }
425
Piotr Nowicki6b2baf92020-03-17 15:36:52 +0100426 printf_dbg( "Finish. Found %u base64 codes\n", b64_counter );
Piotr Nowicki6842c9b2020-03-16 17:52:56 +0100427
Piotr Nowicki9370f902020-03-13 14:43:22 +0100428 return 0;
429}