From 76b2a31ad6542e4ea28b260e8d6450b28cb831e4 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sun, 8 Oct 2023 11:57:19 +0200 Subject: [PATCH 01/11] Use tr31_get_error_string() wehenver possible --- src/tr31-tool.c | 4 ++-- test/tr31_decode_test.c | 8 ++++---- test/tr31_decrypt_test.c | 34 +++++++++++++++++----------------- test/tr31_export_test.c | 22 +++++++++++----------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 4fb26dd..49ab02f 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -894,7 +894,7 @@ static int populate_tr31_from_template(const struct tr31_tool_options_t* options // populate TR-31 context object r = tr31_init(options->export_format_version, &key, tr31_ctx); if (r) { - fprintf(stderr, "tr31_init() failed; r=%d\n", r); + fprintf(stderr, "tr31_init() error %d: %s\n", r, tr31_get_error_string(r)); return 1; } @@ -954,7 +954,7 @@ static int populate_tr31_from_header(const struct tr31_tool_options_t* options, // populate key data r = tr31_key_set_data(&tr31_ctx->key, options->export_key_buf, options->export_key_buf_len); if (r) { - fprintf(stderr, "tr31_key_set_data() failed; r=%d\n", r); + fprintf(stderr, "tr31_key_set_data() error %d: %s\n", r, tr31_get_error_string(r)); return 1; } diff --git a/test/tr31_decode_test.c b/test/tr31_decode_test.c index b118254..c5c384b 100644 --- a/test/tr31_decode_test.c +++ b/test/tr31_decode_test.c @@ -46,7 +46,7 @@ int main(void) printf("Test 1 (Format version B with optional block KS)...\n"); r = tr31_import(test1_tr31_ascii, NULL, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || @@ -78,7 +78,7 @@ int main(void) printf("Test 2 (Format version D containing TDES key)...\n"); r = tr31_import(test2_tr31_ascii, NULL, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -106,7 +106,7 @@ int main(void) printf("Test 3 (Format version D containing AES key)...\n"); r = tr31_import(test3_tr31_ascii, NULL, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -134,7 +134,7 @@ int main(void) printf("Test 4 (Format version B with optional block KS, KC, KP)...\n"); r = tr31_import(test4_tr31_ascii, NULL, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || diff --git a/test/tr31_decrypt_test.c b/test/tr31_decrypt_test.c index 08f67de..d58a543 100644 --- a/test/tr31_decrypt_test.c +++ b/test/tr31_decrypt_test.c @@ -224,7 +224,7 @@ int main(void) printf("Test 1 (Basic format version A)...\n"); r = tr31_import(test1_tr31_format_a, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_A || @@ -264,7 +264,7 @@ int main(void) printf("Test 1 (Basic format version B)...\n"); r = tr31_import(test1_tr31_format_b, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || @@ -304,7 +304,7 @@ int main(void) printf("Test 1 (Basic format version C)...\n"); r = tr31_import(test1_tr31_format_c, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_C || @@ -350,7 +350,7 @@ int main(void) test_kbpk.data = (void*)test2_kbpk; r = tr31_import(test2_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_A || @@ -396,7 +396,7 @@ int main(void) test_kbpk.data = (void*)test3_kbpk; r = tr31_import(test3_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || @@ -442,7 +442,7 @@ int main(void) test_kbpk.data = (void*)test4_kbpk; r = tr31_import(test4_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_C || @@ -492,7 +492,7 @@ int main(void) test_kbpk.data = (void*)test5_kbpk; r = tr31_import(test5_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || @@ -542,7 +542,7 @@ int main(void) test_kbpk.data = (void*)test6_kbpk; r = tr31_import(test6_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -588,7 +588,7 @@ int main(void) test_kbpk.data = (void*)test7_kbpk; r = tr31_import(test7_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -634,7 +634,7 @@ int main(void) test_kbpk.data = (void*)test8_kbpk; r = tr31_import(test8_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -680,7 +680,7 @@ int main(void) test_kbpk.data = (void*)test9_kbpk; r = tr31_import(test9_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_E || @@ -726,7 +726,7 @@ int main(void) test_kbpk.data = (void*)test10_kbpk; r = tr31_import(test10_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -772,7 +772,7 @@ int main(void) test_kbpk.data = (void*)test11_kbpk; r = tr31_import(test11_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -818,7 +818,7 @@ int main(void) test_kbpk.data = (void*)test15_kbpk; r = tr31_import(test15_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_C || @@ -868,7 +868,7 @@ int main(void) test_kbpk.data = (void*)test16_kbpk; r = tr31_import(test16_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_B || @@ -918,7 +918,7 @@ int main(void) test_kbpk.data = (void*)test17_kbpk; r = tr31_import(test17_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || @@ -964,7 +964,7 @@ int main(void) test_kbpk.data = (void*)test18_kbpk; r = tr31_import(test18_tr31_ascii, &test_kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.version != TR31_VERSION_D || diff --git a/test/tr31_export_test.c b/test/tr31_export_test.c index b5e855a..55bca09 100644 --- a/test/tr31_export_test.c +++ b/test/tr31_export_test.c @@ -803,7 +803,7 @@ int main(void) print_buf("kbpk", test[i].kbpk_data, test[i].kbpk_len); r = tr31_key_set_data(&test[i].kbpk, test[i].kbpk_data, test[i].kbpk_len); if (r) { - fprintf(stderr, "tr31_key_set_data() failed; r=%d\n", r); + fprintf(stderr, "tr31_key_set_data() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } @@ -811,14 +811,14 @@ int main(void) print_buf("key", test[i].key_data, test[i].key_len); r = tr31_key_set_data(&test[i].key, test[i].key_data, test[i].key_len); if (r) { - fprintf(stderr, "tr31_key_set_data() failed; r=%d\n", r); + fprintf(stderr, "tr31_key_set_data() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } // Prepare TR-31 context r = tr31_init(test[i].tr31_version, &test[i].key, &test_tr31); if (r) { - fprintf(stderr, "tr31_init() failed; r=%d\n", r); + fprintf(stderr, "tr31_init() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } test_tr31.export_flags = test[i].export_flags; @@ -831,7 +831,7 @@ int main(void) strlen(test[i].cert_base64[cert_idx]) ); if (r) { - fprintf(stderr, "tr31_opt_block_add_CT() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_CT() error %d: %s\n", r, tr31_get_error_string(r)); return 1; } } @@ -839,21 +839,21 @@ int main(void) if (test[i].opt_blk_HM) { r = tr31_opt_block_add_HM(&test_tr31, test[i].opt_blk_HM); if (r) { - fprintf(stderr, "tr31_opt_block_add_HM() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_HM() error %d: %s\n", r, tr31_get_error_string(r)); return 1; } } if (test[i].opt_blk_KC) { r = tr31_opt_block_add_KC(&test_tr31); if (r) { - fprintf(stderr, "tr31_opt_block_add_KC() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_KC() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } } if (test[i].opt_blk_KP) { r = tr31_opt_block_add_KP(&test_tr31); if (r) { - fprintf(stderr, "tr31_opt_block_add_KP() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_KP() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } } @@ -864,14 +864,14 @@ int main(void) test[i].ksn_len ); if (r) { - fprintf(stderr, "tr31_opt_block_add_KS() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_KS() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } } if (test[i].timestamp) { r = tr31_opt_block_add_TS(&test_tr31, test[i].timestamp); if (r) { - fprintf(stderr, "tr31_opt_block_add_TS() failed; r=%d\n", r); + fprintf(stderr, "tr31_opt_block_add_TS() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } } @@ -879,7 +879,7 @@ int main(void) // Export key block r = tr31_export(&test_tr31, &test[i].kbpk, key_block, sizeof(key_block)); if (r) { - fprintf(stderr, "tr31_export() failed; r=%d\n", r); + fprintf(stderr, "tr31_export() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } printf("TR-31: %s\n", key_block); @@ -909,7 +909,7 @@ int main(void) // Import and decrypt key block r = tr31_import(key_block, &test[i].kbpk, &test_tr31); if (r) { - fprintf(stderr, "tr31_import() failed; r=%d\n", r); + fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; } if (test_tr31.key.length != test[i].key_len || From 373017934aaf6253d23639063adb15d731186d82 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Fri, 13 Oct 2023 21:44:35 +0200 Subject: [PATCH 02/11] Refactor tr31_import() not to require null-termination It is convenient for the caller to be able to pass a key block that was received in some kind of message format (eg ISO 8583) directly to tr31_import() without having to copy it out to add null-termination. However, this requires an additional parameter to tr31_import() for the caller to indicate the exact key block length. Note that this API change is not backward compatible due to the additional parameter for tr31_import() as well as the renumbering of the error enum. The intention is for the next release to update the SONAME version anyway. In addition, tr31_import() will now also validate the key block buffer as printable ASCII as early as possible. This ensures that there aren't any illegal characters or NULL values in the key block buffer. --- src/tr31-tool.c | 19 +++++++++---------- src/tr31.c | 13 +++++++++---- src/tr31.h | 5 ++++- test/tr31_decode_test.c | 8 ++++---- test/tr31_decrypt_test.c | 34 +++++++++++++++++----------------- test/tr31_export_test.c | 2 +- 6 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 49ab02f..9c612e6 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -205,7 +205,7 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state) } else { // Copy argument - buf_len = strlen(arg) + 1; + buf_len = strlen(arg); buf = malloc(buf_len); memcpy(buf, arg, buf_len); } @@ -716,10 +716,10 @@ static int do_tr31_import(const struct tr31_tool_options_t* options) if (options->kbpk) { // if key block protection key was provided // parse and decrypt TR-31 key block - r = tr31_import(options->key_block, &kbpk, &tr31_ctx); + r = tr31_import(options->key_block, options->key_block_len, &kbpk, &tr31_ctx); } else { // else if no key block protection key was provided // parse TR-31 key block - r = tr31_import(options->key_block, NULL, &tr31_ctx); + r = tr31_import(options->key_block, options->key_block_len, NULL, &tr31_ctx); } // check for errors if (r) { @@ -912,16 +912,16 @@ static int populate_tr31_from_header(const struct tr31_tool_options_t* options, switch (options->export_header[0]) { case 'A': case 'C': - tmp_key_block_len += 32 + 8 + 1; + tmp_key_block_len += 32 + 8; break; case 'B': - tmp_key_block_len += 32 + 16 + 1; + tmp_key_block_len += 32 + 16; break; case 'D': case 'E': - tmp_key_block_len += 48 + 16 + 1; + tmp_key_block_len += 48 + 16; break; default: @@ -936,16 +936,15 @@ static int populate_tr31_from_header(const struct tr31_tool_options_t* options, // build fake key block to allow parsing of header char tmp_keyblock[tmp_key_block_len]; memcpy(tmp_keyblock, options->export_header, export_header_len); - memset(tmp_keyblock + export_header_len, '0', sizeof(tmp_keyblock) - export_header_len - 1); - tmp_keyblock[sizeof(tmp_keyblock) - 1] = 0; + memset(tmp_keyblock + export_header_len, '0', sizeof(tmp_keyblock) - export_header_len); // fix length field to allow parsing of header char tmp[5]; - snprintf(tmp, sizeof(tmp), "%04zu", tmp_key_block_len - 1); + snprintf(tmp, sizeof(tmp), "%04zu", sizeof(tmp_keyblock)); memcpy(tmp_keyblock + 1, tmp, 4); // misuse TR-31 import function to parse header into TR-31 context object - r = tr31_import(tmp_keyblock, NULL, tr31_ctx); + r = tr31_import(tmp_keyblock, sizeof(tmp_keyblock), NULL, tr31_ctx); if (r) { fprintf(stderr, "Error while parsing export header; error %d: %s\n", r, tr31_get_error_string(r)); return 1; diff --git a/src/tr31.c b/src/tr31.c index d38f676..edd6906 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -1163,12 +1163,12 @@ int tr31_opt_block_add_WP( int tr31_import( const char* key_block, + size_t key_block_len, const struct tr31_key_t* kbpk, struct tr31_ctx_t* ctx ) { int r; - size_t key_block_len; const struct tr31_header_t* header; size_t opt_blk_len_total = 0; unsigned int enc_block_size; @@ -1178,15 +1178,19 @@ int tr31_import( return -1; } - key_block_len = strlen(key_block); - header = (const struct tr31_header_t*)key_block; - // validate minimum length if (key_block_len < TR31_MIN_KEY_BLOCK_LENGTH) { return TR31_ERROR_INVALID_LENGTH; } + // validate key block as printable ASCII (format PA) + r = tr31_validate_format_pa(key_block, key_block_len); + if (r) { + return TR31_ERROR_INVALID_KEY_BLOCK_STRING; + } + // initialise TR-31 context object + header = (const struct tr31_header_t*)key_block; r = tr31_init(header->version_id, NULL, ctx); if (r) { // return error value as-is @@ -2721,6 +2725,7 @@ const char* tr31_get_error_string(enum tr31_error_t error) switch (error) { case TR31_ERROR_INVALID_LENGTH: return "Invalid key block length"; + case TR31_ERROR_INVALID_KEY_BLOCK_STRING: return "Invalid key block string"; case TR31_ERROR_UNSUPPORTED_VERSION: return "Unsupported key block format version"; case TR31_ERROR_INVALID_LENGTH_FIELD: return "Invalid key block length field"; case TR31_ERROR_UNSUPPORTED_KEY_USAGE: return "Unsupported key usage"; diff --git a/src/tr31.h b/src/tr31.h index e441bdd..62299c3 100644 --- a/src/tr31.h +++ b/src/tr31.h @@ -243,6 +243,7 @@ struct tr31_ctx_t { /// TR-31 library errors enum tr31_error_t { TR31_ERROR_INVALID_LENGTH = 1, ///< Invalid key block length + TR31_ERROR_INVALID_KEY_BLOCK_STRING, ///< Invalid key block string TR31_ERROR_UNSUPPORTED_VERSION, ///< Unsupported key block format version TR31_ERROR_INVALID_LENGTH_FIELD, ///< Invalid key block length field TR31_ERROR_UNSUPPORTED_KEY_USAGE, ///< Unsupported key usage @@ -626,13 +627,15 @@ int tr31_opt_block_add_WP( * @note This function will populate a new TR-31 context object. * Use @ref tr31_release() to release internal resources when done. * - * @param key_block TR-31 key block. Null terminated. At least the header must be ASCII encoded. + * @param key_block TR-31 key block. Must contain printable ASCII characters. Null-termination not required. + * @param key_block_len Length of TR-31 key block in bytes, excluding null-termination. * @param kbpk TR-31 key block protection key. NULL if not available or decryption is not required. * @param ctx TR-31 context object output * @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t */ int tr31_import( const char* key_block, + size_t key_block_len, const struct tr31_key_t* kbpk, struct tr31_ctx_t* ctx ); diff --git a/test/tr31_decode_test.c b/test/tr31_decode_test.c index c5c384b..5319da9 100644 --- a/test/tr31_decode_test.c +++ b/test/tr31_decode_test.c @@ -44,7 +44,7 @@ int main(void) // test key block decoding for format version B with optional block KS printf("Test 1 (Format version B with optional block KS)...\n"); - r = tr31_import(test1_tr31_ascii, NULL, &test_tr31); + r = tr31_import(test1_tr31_ascii, strlen(test1_tr31_ascii), NULL, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -76,7 +76,7 @@ int main(void) // test key block decoding for format version D containing TDES key printf("Test 2 (Format version D containing TDES key)...\n"); - r = tr31_import(test2_tr31_ascii, NULL, &test_tr31); + r = tr31_import(test2_tr31_ascii, strlen(test2_tr31_ascii), NULL, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -104,7 +104,7 @@ int main(void) // test key block decoding for format version D containing AES key printf("Test 3 (Format version D containing AES key)...\n"); - r = tr31_import(test3_tr31_ascii, NULL, &test_tr31); + r = tr31_import(test3_tr31_ascii, strlen(test3_tr31_ascii), NULL, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -132,7 +132,7 @@ int main(void) // test key block decoding for format version B with optional blocks KS, KC, and KP printf("Test 4 (Format version B with optional block KS, KC, KP)...\n"); - r = tr31_import(test4_tr31_ascii, NULL, &test_tr31); + r = tr31_import(test4_tr31_ascii, strlen(test4_tr31_ascii), NULL, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; diff --git a/test/tr31_decrypt_test.c b/test/tr31_decrypt_test.c index d58a543..f1f9a28 100644 --- a/test/tr31_decrypt_test.c +++ b/test/tr31_decrypt_test.c @@ -222,7 +222,7 @@ int main(void) // test key block decryption for format version A printf("Test 1 (Basic format version A)...\n"); - r = tr31_import(test1_tr31_format_a, &test_kbpk, &test_tr31); + r = tr31_import(test1_tr31_format_a, strlen(test1_tr31_format_a), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -262,7 +262,7 @@ int main(void) // test key block decryption for format version B printf("Test 1 (Basic format version B)...\n"); - r = tr31_import(test1_tr31_format_b, &test_kbpk, &test_tr31); + r = tr31_import(test1_tr31_format_b, strlen(test1_tr31_format_b), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -302,7 +302,7 @@ int main(void) // test key block decryption for format version C printf("Test 1 (Basic format version C)...\n"); - r = tr31_import(test1_tr31_format_c, &test_kbpk, &test_tr31); + r = tr31_import(test1_tr31_format_c, strlen(test1_tr31_format_c), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -348,7 +348,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test2_kbpk); test_kbpk.data = (void*)test2_kbpk; - r = tr31_import(test2_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test2_tr31_ascii, strlen(test2_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -394,7 +394,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test3_kbpk); test_kbpk.data = (void*)test3_kbpk; - r = tr31_import(test3_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test3_tr31_ascii, strlen(test3_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -440,7 +440,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test4_kbpk); test_kbpk.data = (void*)test4_kbpk; - r = tr31_import(test4_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test4_tr31_ascii, strlen(test4_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -490,7 +490,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test5_kbpk); test_kbpk.data = (void*)test5_kbpk; - r = tr31_import(test5_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test5_tr31_ascii, strlen(test5_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -540,7 +540,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test6_kbpk); test_kbpk.data = (void*)test6_kbpk; - r = tr31_import(test6_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test6_tr31_ascii, strlen(test6_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -586,7 +586,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test7_kbpk); test_kbpk.data = (void*)test7_kbpk; - r = tr31_import(test7_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test7_tr31_ascii, strlen(test7_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -632,7 +632,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test8_kbpk); test_kbpk.data = (void*)test8_kbpk; - r = tr31_import(test8_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test8_tr31_ascii, strlen(test8_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -678,7 +678,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test9_kbpk); test_kbpk.data = (void*)test9_kbpk; - r = tr31_import(test9_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test9_tr31_ascii, strlen(test9_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -724,7 +724,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test10_kbpk); test_kbpk.data = (void*)test10_kbpk; - r = tr31_import(test10_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test10_tr31_ascii, strlen(test10_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -770,7 +770,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test11_kbpk); test_kbpk.data = (void*)test11_kbpk; - r = tr31_import(test11_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test11_tr31_ascii, strlen(test11_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -816,7 +816,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test15_kbpk); test_kbpk.data = (void*)test15_kbpk; - r = tr31_import(test15_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test15_tr31_ascii, strlen(test15_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -866,7 +866,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test16_kbpk); test_kbpk.data = (void*)test16_kbpk; - r = tr31_import(test16_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test16_tr31_ascii, strlen(test16_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -916,7 +916,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test17_kbpk); test_kbpk.data = (void*)test17_kbpk; - r = tr31_import(test17_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test17_tr31_ascii, strlen(test17_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; @@ -962,7 +962,7 @@ int main(void) test_kbpk.mode_of_use = TR31_KEY_MODE_OF_USE_ENC_DEC; test_kbpk.length = sizeof(test18_kbpk); test_kbpk.data = (void*)test18_kbpk; - r = tr31_import(test18_tr31_ascii, &test_kbpk, &test_tr31); + r = tr31_import(test18_tr31_ascii, strlen(test18_tr31_ascii), &test_kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; diff --git a/test/tr31_export_test.c b/test/tr31_export_test.c index 55bca09..8c83e22 100644 --- a/test/tr31_export_test.c +++ b/test/tr31_export_test.c @@ -907,7 +907,7 @@ int main(void) tr31_release(&test_tr31); // Import and decrypt key block - r = tr31_import(key_block, &test[i].kbpk, &test_tr31); + r = tr31_import(key_block, strlen(key_block), &test[i].kbpk, &test_tr31); if (r) { fprintf(stderr, "tr31_import() error %d: %s\n", r, tr31_get_error_string(r)); goto exit; From 9d86dfd2d4c7b1b5c7a00a81cdedc48e4891e80e Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sat, 14 Oct 2023 14:44:48 +0200 Subject: [PATCH 03/11] Fix character validation for tr31-tool * Trim non-printable characters, including space, from the end of import key blocks * Only allow printable characters for export headers * Only allow alphanumeric characters for optional block DA data --- src/tr31-tool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 9c612e6..620dc4d 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -212,7 +212,7 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state) // Trim KEYBLOCK argument for (char* str = buf; buf_len; --buf_len) { - if (!isalnum(str[buf_len - 1])) { + if (!isgraph(str[buf_len - 1])) { str[buf_len - 1] = 0; } else { break; @@ -289,8 +289,8 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state) argp_error(state, "Export header must be at least 16 characters/bytes"); } for (size_t i = 0; i < strlen(arg); ++i) { - if (!isalnum(arg[i])) { - argp_error(state, "Export header must consist of alphanumeric characters (invalid character '%c' is not allowed)", arg[i]); + if (!isprint(arg[i])) { + argp_error(state, "Export header must consist of printable characters (invalid character '%c' is not allowed)", arg[i]); } } options->export_header = arg; @@ -383,8 +383,8 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state) argp_error(state, "Export optional block DA must be a multiple of 5 bytes"); } for (size_t i = 0; i < strlen(arg); ++i) { - if (!isprint(arg[i]) || isspace(arg[i])) { - argp_error(state, "Export header must consist of printable characters (invalid character '%c' is not allowed)", arg[i]); + if (!isalnum(arg[i])) { + argp_error(state, "Export optional block DA consist of alphanumeric characters (invalid character '%c' is not allowed)", arg[i]); } } options->export_opt_block_DA = arg; From 3e665ecf695ed8cd49ee5ddcd662a4b07e4f65e5 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sat, 14 Oct 2023 19:29:27 +0200 Subject: [PATCH 04/11] Fix cleanup of TR-31 context object * After tr31_import() has allocated the optional block array, all error paths should call tr31_release() * If tr31_import() fails, the caller (in this case tr31-tool), should not call tr31_release() * Ensure that export tests always call tr31_release() upon error --- src/tr31-tool.c | 5 ++++- src/tr31.c | 3 ++- test/tr31_export_test.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 620dc4d..4bad11f 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -838,7 +838,10 @@ static int do_tr31_import(const struct tr31_tool_options_t* options) // cleanup tr31_key_release(&kbpk); - tr31_release(&tr31_ctx); + if (!ret) { + // only cleanup TR-31 context object if tr31_import() was successful + tr31_release(&tr31_ctx); + } return ret; } diff --git a/src/tr31.c b/src/tr31.c index edd6906..67b0475 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -1299,7 +1299,8 @@ int tr31_import( // So we'll use the encryption block size which is determined by the TR-31 // format version. if (opt_blk_len_total & (enc_block_size-1)) { - return TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA; + r = TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA; + goto error; } // ensure that current pointer is valid for minimal payload and authenticator diff --git a/test/tr31_export_test.c b/test/tr31_export_test.c index 8c83e22..8a6b808 100644 --- a/test/tr31_export_test.c +++ b/test/tr31_export_test.c @@ -832,7 +832,7 @@ int main(void) ); if (r) { fprintf(stderr, "tr31_opt_block_add_CT() error %d: %s\n", r, tr31_get_error_string(r)); - return 1; + goto exit; } } } @@ -840,7 +840,7 @@ int main(void) r = tr31_opt_block_add_HM(&test_tr31, test[i].opt_blk_HM); if (r) { fprintf(stderr, "tr31_opt_block_add_HM() error %d: %s\n", r, tr31_get_error_string(r)); - return 1; + goto exit; } } if (test[i].opt_blk_KC) { From 6d86e77b54141fa6f67a7e340cdc188e29d5e6b7 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sat, 14 Oct 2023 20:08:43 +0200 Subject: [PATCH 05/11] Fix output null-termination for tr31_export() Unfortunately tr31_export() had a silly bug whereby the last byte of the provided key block output buffer would not be zero'd. Therefore, if the provided buffer was exactly one byte longer than the created key block, the resulting key block string would not be null-terminated. Note that although tr31_import() has been refactored not to require null-terminated key block input to make it more convenient for the caller, it continues to be convenient for the caller that tr31_export() null-terminates the key block output. This is such that the caller can equally easily print the key block output or obtain its length using strlen(). The key block buffer output length parameter has also been renamed to key_block_buf_len to distinguish it from from the exact key block input length expected in key_block_len. --- src/tr31.c | 26 +++++++++++++------------- src/tr31.h | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tr31.c b/src/tr31.c index 67b0475..ef181f8 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -1520,7 +1520,7 @@ int tr31_export( struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk, char* key_block, - size_t key_block_len + size_t key_block_buf_len ) { int r; @@ -1529,21 +1529,21 @@ int tr31_export( unsigned int enc_block_size; void* ptr; - if (!ctx || !kbpk || !key_block || !key_block_len) { + if (!ctx || !kbpk || !key_block || !key_block_buf_len) { return -1; } if (!ctx->key.data || !ctx->key.length) { return -2; } - // ensure space for null-termination - --key_block_len; - - // validate minimum length - if (key_block_len < TR31_MIN_KEY_BLOCK_LENGTH) { + // validate minimum length (+1 for null-termination) + if (key_block_buf_len < TR31_MIN_KEY_BLOCK_LENGTH + 1) { return TR31_ERROR_INVALID_LENGTH; } - memset(key_block, 0, key_block_len); + + // ensure null-termination + memset(key_block, 0, key_block_buf_len); + --key_block_buf_len; // validate key block format version // set encryption block size for header padding @@ -1641,7 +1641,7 @@ int tr31_export( size_t opt_blk_len; r = tr31_opt_block_export( &ctx->opt_blocks[i], - (void*)key_block + key_block_len - ptr, + (void*)key_block + key_block_buf_len - ptr, &opt_blk_len, ptr ); @@ -1673,7 +1673,7 @@ int tr31_export( pb_len = ((opt_blk_len_total + 4 + enc_block_size) & ~(enc_block_size-1)) - opt_blk_len_total; } - if (ptr + pb_len - (void*)header > key_block_len) { + if (ptr + pb_len - (void*)header > key_block_buf_len) { // optional block length exceeds total key block length return TR31_ERROR_INVALID_LENGTH; } @@ -1709,7 +1709,7 @@ int tr31_export( // return error value as-is return r; } - if (ctx->length > key_block_len) { + if (ctx->length > key_block_buf_len) { return TR31_ERROR_INVALID_LENGTH; } @@ -1808,7 +1808,7 @@ int tr31_export( } // add payload to key block - r = bin_to_hex(ctx->payload, ctx->payload_length, ptr, key_block_len); + r = bin_to_hex(ctx->payload, ctx->payload_length, ptr, key_block_buf_len); if (r) { // internal error return -5; @@ -1816,7 +1816,7 @@ int tr31_export( ptr += (ctx->payload_length * 2); // add authenticator to key block - r = bin_to_hex(ctx->authenticator, ctx->authenticator_length, ptr, key_block_len); + r = bin_to_hex(ctx->authenticator, ctx->authenticator_length, ptr, key_block_buf_len); if (r) { // internal error return -6; diff --git a/src/tr31.h b/src/tr31.h index 62299c3..f731ea7 100644 --- a/src/tr31.h +++ b/src/tr31.h @@ -647,15 +647,15 @@ int tr31_import( * * @param ctx TR-31 context object input * @param kbpk TR-31 key block protection key. - * @param key_block TR-31 key block output. Null terminated. At least the header will be ASCII encoded. - * @param key_block_len TR-31 key block output buffer length. + * @param key_block TR-31 key block output. Will contain printable ASCII characters and will be null-terminated. + * @param key_block_buf_len TR-31 key block output buffer length. * @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t */ int tr31_export( struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk, char* key_block, - size_t key_block_len + size_t key_block_buf_len ); /** From 73659244d780d0f71243b595bc63c58fe4f681fc Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sun, 15 Oct 2023 10:33:26 +0200 Subject: [PATCH 06/11] Add new error value for invalid optional block padding Given various other API changes intended for the next release that are not backward compatible, this is just one more such change. --- src/tr31.c | 7 ++++--- src/tr31.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tr31.c b/src/tr31.c index ef181f8..11c0b0e 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -1296,10 +1296,10 @@ int tr31_import( // ISO 20038:2017, A.2.1 (page 10) indicates that the total length of all // optional blocks must be a multiple of the encryption block size and // does not make an exception for format version E. - // So we'll use the encryption block size which is determined by the TR-31 - // format version. + // So we'll use the encryption block size which is determined by the key + // block format version. if (opt_blk_len_total & (enc_block_size-1)) { - r = TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA; + r = TR31_ERROR_INVALID_OPTIONAL_BLOCK_PADDING; goto error; } @@ -2737,6 +2737,7 @@ const char* tr31_get_error_string(enum tr31_error_t error) case TR31_ERROR_INVALID_NUMBER_OF_OPTIONAL_BLOCKS_FIELD: return "Invalid number of optional blocks field"; case TR31_ERROR_INVALID_OPTIONAL_BLOCK_LENGTH: return "Invalid optional block length"; case TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA: return "Invalid optional block data"; + case TR31_ERROR_INVALID_OPTIONAL_BLOCK_PADDING: return "Invalid optional block padding"; case TR31_ERROR_INVALID_PAYLOAD_FIELD: return "Invalid payload data field"; case TR31_ERROR_INVALID_AUTHENTICATOR_FIELD: return "Invalid authenticator data field"; case TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM: return "Unsupported key block protection key algorithm"; diff --git a/src/tr31.h b/src/tr31.h index f731ea7..20a6fac 100644 --- a/src/tr31.h +++ b/src/tr31.h @@ -254,6 +254,7 @@ enum tr31_error_t { TR31_ERROR_INVALID_NUMBER_OF_OPTIONAL_BLOCKS_FIELD, ///< Invalid number of optional blocks field TR31_ERROR_INVALID_OPTIONAL_BLOCK_LENGTH, ///< Invalid optional block length TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA, ///< Invalid optional block data + TR31_ERROR_INVALID_OPTIONAL_BLOCK_PADDING, ///< Invalid optional block padding TR31_ERROR_INVALID_PAYLOAD_FIELD, ///< Invalid payload data field TR31_ERROR_INVALID_AUTHENTICATOR_FIELD, ///< Invalid authenticator data field TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM, ///< Unsupported key block protection key algorithm From ab7d66930e0e5e2154d94353ba878cf76c8c19d1 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sat, 21 Oct 2023 23:01:19 +0200 Subject: [PATCH 07/11] Refactor tr31_import() to use processing state object * Create new internal processing state object to hold state related to the decryption and verification of key block payloads * Move determination of encryption block size and authenticator length to tr31_state_init() * Move computation of header length and payload length to tr31_state_prepare_import() * Refactor payload decryption functions to avoid large stack allocations that might fail on embedded platforms. This is specifically a problem with key blocks containing asymmetric wrapped keys. * Improve validation of decrypted key length to ensure that it is a multiple of 8 bits At a high level, tr31_state_prepare_import() will populate a buffer containing the key block header, binary payload and binary authenticator. For format version A and C, this buffer can be used as-is for CBC-MAC verification. For format version B and D, the decrypted payload can easily be copied in such that the buffer can then be used for CMAC verification. This change is part of a wider effort to move the fields intended for internal use out of the context object where they are visible to the caller. Instead a new internal processing state object will hold these fields and ensure that they are properly cleansed. --- src/tr31.c | 381 +++++++++++++++++++++++++++------------ test/tr31_decode_test.c | 24 +-- test/tr31_decrypt_test.c | 102 ++--------- 3 files changed, 283 insertions(+), 224 deletions(-) diff --git a/src/tr31.c b/src/tr31.c index 11c0b0e..3ffc51e 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -90,6 +90,26 @@ struct tr31_payload_t { #define TR31_MIN_PAYLOAD_LENGTH (DES_BLOCK_SIZE) #define TR31_MIN_KEY_BLOCK_LENGTH (sizeof(struct tr31_header_t) + TR31_MIN_PAYLOAD_LENGTH + 8) // Minimum TR-31 key block length: header + minimum payload + authenticator +// Internal processing state +struct tr31_state_t { + // encryption block size used for header length validation + unsigned int enc_block_size; + + // buffer containing: + // - verbatim header + // - binary (hex decoded) payload + // - binary (hex decoded) authenticator + size_t decoded_key_block_length; + void* decoded_key_block; + + // lengths and pointers for decoded key block buffer + size_t header_length; + size_t payload_length; + void* payload; + size_t authenticator_length; + void* authenticator; +}; + // helper functions static int dec_to_int(const char* str, size_t str_len); static void int_to_dec(unsigned int value, char* str, size_t str_len); @@ -104,12 +124,15 @@ static int tr31_opt_block_parse(const void* ptr, size_t remaining_len, size_t* o static int tr31_opt_block_validate_iso8601(const char* ts_str, size_t ts_str_len); static int tr31_opt_block_export(const struct tr31_opt_ctx_t* opt_ctx, size_t remaining_len, size_t* opt_blk_len, void* ptr); static int tr31_opt_block_export_PB(const struct tr31_ctx_t* ctx, size_t pb_len, struct tr31_opt_blk_t* opt_blk); +static int tr31_state_init(uint8_t version_id, struct tr31_state_t* state); +static int tr31_state_prepare_import(struct tr31_state_t* state, const void* key_block, size_t key_block_len, size_t header_len); +static void tr31_state_release(struct tr31_state_t* state); static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx); -static int tr31_tdes_decrypt_verify_variant_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_tdes_decrypt_verify_variant_binding(const struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); -static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); -static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); static int dec_to_int(const char* str, size_t str_len) @@ -1170,8 +1193,8 @@ int tr31_import( { int r; const struct tr31_header_t* header; + struct tr31_state_t state; size_t opt_blk_len_total = 0; - unsigned int enc_block_size; const void* ptr; if (!key_block || !ctx) { @@ -1189,8 +1212,18 @@ int tr31_import( return TR31_ERROR_INVALID_KEY_BLOCK_STRING; } - // initialise TR-31 context object + // initialise processing state object + // this will populate: + // - state.enc_block_size + // - state.authenticator_length header = (const struct tr31_header_t*)key_block; + r = tr31_state_init(header->version_id, &state); + if (r) { + // return error value as-is + return r; + } + + // initialise TR-31 context object r = tr31_init(header->version_id, NULL, ctx); if (r) { // return error value as-is @@ -1260,36 +1293,6 @@ int tr31_import( ptr += opt_blk_len; } - // validate key block format version - // set associated authenticator length - // set encryption block size for header length validation - switch (ctx->version) { - case TR31_VERSION_A: - case TR31_VERSION_C: - ctx->authenticator_length = 4; // 4 bytes; 8 ASCII hex digits - enc_block_size = DES_BLOCK_SIZE; - break; - - case TR31_VERSION_B: - ctx->authenticator_length = 8; // 8 bytes; 16 ASCII hex digits - enc_block_size = DES_BLOCK_SIZE; - break; - - case TR31_VERSION_D: - ctx->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits - enc_block_size = AES_BLOCK_SIZE; - break; - - case TR31_VERSION_E: - ctx->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits - enc_block_size = AES_BLOCK_SIZE; - break; - - default: - // invalid format version - return -1; - } - // ANSI X9.143:2021, 6.3.6 (page 19) indicates that the padding block must // result in the total length of all optional blocks being a multiple of // the encryption block length. @@ -1298,45 +1301,25 @@ int tr31_import( // does not make an exception for format version E. // So we'll use the encryption block size which is determined by the key // block format version. - if (opt_blk_len_total & (enc_block_size-1)) { + if (opt_blk_len_total & (state.enc_block_size-1)) { r = TR31_ERROR_INVALID_OPTIONAL_BLOCK_PADDING; goto error; } - // ensure that current pointer is valid for minimal payload and authenticator - if (ptr - (void*)header + TR31_MIN_PAYLOAD_LENGTH + (ctx->authenticator_length * 2) > key_block_len) { - r = TR31_ERROR_INVALID_LENGTH; - goto error; - } - - // update header data in context object - ctx->header_length = ptr - (void*)header; - ctx->header = (void*)header; - - // determine payload length - size_t key_block_payload_length = key_block_len - ctx->header_length - (ctx->authenticator_length * 2); - ctx->payload_length = key_block_payload_length / 2; - - // add payload data to context object - ctx->payload = calloc(1, ctx->payload_length); - r = hex_to_bin(ptr, key_block_payload_length, ctx->payload, ctx->payload_length); - if (r) { - r = TR31_ERROR_INVALID_PAYLOAD_FIELD; - goto error; - } - ptr += key_block_payload_length; - - // ensure that current point is valid for remaining authenticator - if (ptr - (void*)header + (ctx->authenticator_length * 2) != key_block_len) { - r = TR31_ERROR_INVALID_LENGTH; - goto error; - } - - // add authenticator to context object - ctx->authenticator = calloc(1, ctx->authenticator_length); - r = hex_to_bin(ptr, ctx->authenticator_length * 2, ctx->authenticator, ctx->authenticator_length); + // prepare state object for import processing + // this function requires: + // - state.authenticator_length + // and will: + // - validate that the payload and authenticator are hex encoded + // - populate remaining fields required by binding functions + r = tr31_state_prepare_import( + &state, + key_block, + ctx->length, + ptr - (void*)header + ); if (r) { - r = TR31_ERROR_INVALID_AUTHENTICATOR_FIELD; + // return error value as-is goto error; } @@ -1362,7 +1345,7 @@ int tr31_import( // not appear to indicate a minimum or maximum for key length // padding, and therefore this implementation only enforces the // cipher block size - if (ctx->payload_length & (DES_BLOCK_SIZE-1)) { + if (state.payload_length & (DES_BLOCK_SIZE-1)) { // payload length must be a multiple of TDES block size // for format version A, B, C r = TR31_ERROR_INVALID_KEY_LENGTH; @@ -1371,10 +1354,10 @@ int tr31_import( if (ctx->version == TR31_VERSION_A || ctx->version == TR31_VERSION_C) { // verify and decrypt payload - r = tr31_tdes_decrypt_verify_variant_binding(ctx, kbpk); + r = tr31_tdes_decrypt_verify_variant_binding(&state, kbpk, &ctx->key); } else if (ctx->version == TR31_VERSION_B) { // decrypt and verify payload - r = tr31_tdes_decrypt_verify_derivation_binding(ctx, kbpk); + r = tr31_tdes_decrypt_verify_derivation_binding(&state, kbpk, &ctx->key); } else { // invalid format version return -1; @@ -1416,7 +1399,7 @@ int tr31_import( // TR-31:2018 nor ISO 20038:2017 appear to indicate a minimum or // maximum for key length padding, and therefore this // implementation only enforces the cipher block size - if (ctx->payload_length & (AES_BLOCK_SIZE-1)) { + if (state.payload_length & (AES_BLOCK_SIZE-1)) { // payload length must be a multiple of AES block size // for format version D r = TR31_ERROR_INVALID_KEY_LENGTH; @@ -1424,7 +1407,7 @@ int tr31_import( } // decrypt and verify payload - r = tr31_aes_decrypt_verify_derivation_binding(ctx, kbpk); + r = tr31_aes_decrypt_verify_derivation_binding(&state, kbpk, &ctx->key); if (r) { // return error value as-is goto error; @@ -1467,7 +1450,7 @@ int tr31_import( } // decrypt and verify payload - r = tr31_aes_decrypt_verify_derivation_binding(ctx, kbpk); + r = tr31_aes_decrypt_verify_derivation_binding(&state, kbpk, &ctx->key); if (r) { // return error value as-is goto error; @@ -1513,6 +1496,7 @@ int tr31_import( error: tr31_release(ctx); exit: + tr31_state_release(&state); return r; } @@ -2175,6 +2159,98 @@ static int tr31_opt_block_export_PB( return 0; } +static int tr31_state_init(uint8_t version_id, struct tr31_state_t* state) +{ + memset(state, 0, sizeof(*state)); + + // determine authenticator length and encryption block size + switch (version_id) { + case TR31_VERSION_A: + case TR31_VERSION_C: + state->enc_block_size = DES_BLOCK_SIZE; + state->authenticator_length = 4; // 4 bytes; 8 ASCII hex digits + break; + + case TR31_VERSION_B: + state->enc_block_size = DES_BLOCK_SIZE; + state->authenticator_length = 8; // 8 bytes; 16 ASCII hex digits + break; + + case TR31_VERSION_D: + state->enc_block_size = AES_BLOCK_SIZE; + state->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits + break; + + case TR31_VERSION_E: + state->enc_block_size = AES_BLOCK_SIZE; + state->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits + break; + + default: + return TR31_ERROR_UNSUPPORTED_VERSION; + } + + return 0; +} + +static int tr31_state_prepare_import( + struct tr31_state_t* state, + const void* key_block, + size_t key_block_len, + size_t header_len +) +{ + int r; + size_t authenticator_hex_length; + size_t payload_hex_length; + const void* ptr; + + // ensure that key block length is valid for minimal payload and authenticator + authenticator_hex_length = state->authenticator_length * 2; + if (header_len + TR31_MIN_PAYLOAD_LENGTH + authenticator_hex_length > key_block_len) { + return TR31_ERROR_INVALID_LENGTH; + } + + // populate various lengths + state->header_length = header_len; + payload_hex_length = key_block_len - state->header_length - authenticator_hex_length; + state->payload_length = payload_hex_length / 2; + + // prepare decoded key block buffer + state->decoded_key_block_length = state->header_length + state->payload_length + state->authenticator_length; + state->decoded_key_block = malloc(state->decoded_key_block_length); + memcpy(state->decoded_key_block, key_block, state->header_length); + + // decode payload + ptr = key_block + header_len; + state->payload = state->decoded_key_block + state->header_length; + r = hex_to_bin(ptr, payload_hex_length, state->payload, state->payload_length); + if (r) { + return TR31_ERROR_INVALID_PAYLOAD_FIELD; + } + + // decode authenticator + ptr += payload_hex_length; + state->authenticator = state->payload + state->payload_length; + r = hex_to_bin(ptr, authenticator_hex_length, state->authenticator, state->authenticator_length); + if (r) { + return TR31_ERROR_INVALID_AUTHENTICATOR_FIELD; + } + + return 0; +} + +static void tr31_state_release(struct tr31_state_t* state) +{ + if (state->decoded_key_block) { + // cleanse this buffer because the derivation binding functions copy + // cleartext key data into it for CMAC verification + crypto_cleanse(state->decoded_key_block, state->decoded_key_block_length); + free(state->decoded_key_block); + } + memset(state, 0, sizeof(*state)); +} + static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) { size_t padded_key_length; @@ -2251,22 +2327,14 @@ static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) return 0; } -static int tr31_tdes_decrypt_verify_variant_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_tdes_decrypt_verify_variant_binding(const struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key) { int r; uint8_t kbek[TDES3_KEY_SIZE]; uint8_t kbak[TDES3_KEY_SIZE]; + struct tr31_payload_t* decrypted_payload = NULL; size_t key_length; - // buffer for decryption - uint8_t decrypted_payload_buf[ctx->payload_length]; - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)decrypted_payload_buf; - - // buffer for MAC verification - uint8_t mac_input[ctx->header_length + ctx->payload_length]; - memcpy(mac_input, ctx->header, ctx->header_length); - memcpy(mac_input + ctx->header_length, ctx->payload, ctx->payload_length); - // output key block encryption key variant and key block authentication key variant r = tr31_tdes_kbpk_variant(kbpk->data, kbpk->length, kbek, kbak); if (r) { @@ -2275,29 +2343,50 @@ static int tr31_tdes_decrypt_verify_variant_binding(struct tr31_ctx_t* ctx, cons } // verify authenticator - r = tr31_tdes_verify_cbcmac(kbak, kbpk->length, mac_input, sizeof(mac_input), ctx->authenticator, ctx->authenticator_length); + r = tr31_tdes_verify_cbcmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + state->authenticator, + state->authenticator_length + ); if (r) { r = TR31_ERROR_KEY_BLOCK_VERIFICATION_FAILED; goto error; } // decrypt key payload; note that the TR-31 header is used as the IV - r = crypto_tdes_decrypt(kbek, kbpk->length, ctx->header, ctx->payload, ctx->payload_length, decrypted_payload); + decrypted_payload = malloc(state->payload_length); + r = crypto_tdes_decrypt( + kbek, + kbpk->length, + state->decoded_key_block, + state->payload, + state->payload_length, + decrypted_payload + ); if (r) { // return error value as-is goto error; } // validate payload length field - key_length = ntohs(decrypted_payload->length) / 8; // payload length is big endian and in bits, not bytes - if (key_length > ctx->payload_length - 2) { + key_length = ntohs(decrypted_payload->length); // payload length is big endian and in bits, not bytes + if ((key_length & 0x7) != 0) { + // invalid key length is not a multiple of 8 bits + r = TR31_ERROR_INVALID_KEY_LENGTH; + goto error; + } + key_length /= 8; // convert to bytes + if (key_length > state->payload_length - 2) { // invalid key length relative to encrypted payload length r = TR31_ERROR_INVALID_KEY_LENGTH; goto error; } // extract key data - r = tr31_key_set_data(&ctx->key, decrypted_payload->data, key_length); + r = tr31_key_set_data(key, decrypted_payload->data, key_length); if (r) { // return error value as-is goto error; @@ -2312,8 +2401,10 @@ static int tr31_tdes_decrypt_verify_variant_binding(struct tr31_ctx_t* ctx, cons // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_payload_buf, sizeof(decrypted_payload_buf)); - crypto_cleanse(mac_input, sizeof(mac_input)); + if (decrypted_payload) { + crypto_cleanse(decrypted_payload, state->payload_length); + free(decrypted_payload); + } return r; } @@ -2386,18 +2477,14 @@ static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const return r; } -static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key) { int r; uint8_t kbek[TDES3_KEY_SIZE]; uint8_t kbak[TDES3_KEY_SIZE]; + struct tr31_payload_t* decrypted_payload = NULL; size_t key_length; - // buffer for decryption and CMAC verification - uint8_t decrypted_key_block[ctx->header_length + ctx->payload_length]; - memcpy(decrypted_key_block, ctx->header, ctx->header_length); - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)(decrypted_key_block + ctx->header_length); - // derive key block encryption key and key block authentication key from key block protection key r = tr31_tdes_kbpk_derive(kbpk->data, kbpk->length, kbek, kbak); if (r) { @@ -2406,29 +2493,51 @@ static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, c } // decrypt key payload; note that the authenticator is used as the IV - r = crypto_tdes_decrypt(kbek, kbpk->length, ctx->authenticator, ctx->payload, ctx->payload_length, decrypted_payload); + decrypted_payload = malloc(state->payload_length); + r = crypto_tdes_decrypt( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + decrypted_payload + ); if (r) { // return error value as-is goto error; } // extract payload length field - key_length = ntohs(decrypted_payload->length) / 8; // payload length is big endian and in bits, not bytes - if (key_length > ctx->payload_length - 2) { + key_length = ntohs(decrypted_payload->length); // payload length is big endian and in bits, not bytes + if ((key_length & 0x7) != 0) { + // invalid key length is not a multiple of 8 bits + r = TR31_ERROR_INVALID_KEY_LENGTH; + goto error; + } + key_length /= 8; // convert to bytes + if (key_length > state->payload_length - 2) { // invalid key length relative to encrypted payload length r = TR31_ERROR_INVALID_KEY_LENGTH; goto error; } // verify authenticator - r = tr31_tdes_verify_cmac(kbak, kbpk->length, decrypted_key_block, sizeof(decrypted_key_block), ctx->authenticator, ctx->authenticator_length); + memcpy(state->payload, decrypted_payload, state->payload_length); + r = tr31_tdes_verify_cmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + state->authenticator, + state->authenticator_length + ); if (r) { r = TR31_ERROR_KEY_BLOCK_VERIFICATION_FAILED; goto error; } // extract key data - r = tr31_key_set_data(&ctx->key, decrypted_payload->data, key_length); + r = tr31_key_set_data(key, decrypted_payload->data, key_length); if (r) { // return error value as-is goto error; @@ -2443,7 +2552,10 @@ static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, c // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_key_block, sizeof(decrypted_key_block)); + if (decrypted_payload) { + crypto_cleanse(decrypted_payload, state->payload_length); + free(decrypted_payload); + } return r; } @@ -2511,19 +2623,17 @@ static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, con return r; } -static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key) { int r; uint8_t kbek[AES256_KEY_SIZE]; uint8_t kbak[AES256_KEY_SIZE]; + const struct tr31_header_t* header; + struct tr31_payload_t* decrypted_payload = NULL; size_t key_length; - // buffer for decryption and CMAC verification - uint8_t decrypted_key_block[ctx->header_length + ctx->payload_length]; - memcpy(decrypted_key_block, ctx->header, ctx->header_length); - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)(decrypted_key_block + ctx->header_length); - - if (ctx->version == TR31_VERSION_D) { + header = state->decoded_key_block; + if (header->version_id == TR31_VERSION_D) { // derive key block encryption key and key block authentication key from key block protection key // format version D uses CBC block mode r = tr31_aes_kbpk_derive(kbpk->data, kbpk->length, TR31_AES_MODE_CBC, kbek, kbak); @@ -2533,13 +2643,21 @@ static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, co } // decrypt key payload; note that the authenticator is used as the IV - r = crypto_aes_decrypt(kbek, kbpk->length, ctx->authenticator, ctx->payload, ctx->payload_length, decrypted_payload); + decrypted_payload = malloc(state->payload_length); + r = crypto_aes_decrypt( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + decrypted_payload + ); if (r) { // return error value as-is goto error; } - } else if (ctx->version == TR31_VERSION_E) { + } else if (header->version_id == TR31_VERSION_E) { // derive key block encryption key and key block authentication key from key block protection key // format version E uses CTR block mode r = tr31_aes_kbpk_derive(kbpk->data, kbpk->length, TR31_AES_MODE_CTR, kbek, kbak); @@ -2549,7 +2667,15 @@ static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, co } // decrypt key payload; note that the authenticator is used as the IV/nonce - r = crypto_aes_decrypt_ctr(kbek, kbpk->length, ctx->authenticator, ctx->payload, ctx->payload_length, decrypted_payload); + decrypted_payload = malloc(state->payload_length); + r = crypto_aes_decrypt_ctr( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + decrypted_payload + ); if (r) { // return error value as-is goto error; @@ -2561,22 +2687,36 @@ static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, co } // extract payload length field - key_length = ntohs(decrypted_payload->length) / 8; // payload length is big endian and in bits, not bytes - if (key_length > ctx->payload_length - 2) { + key_length = ntohs(decrypted_payload->length); // payload length is big endian and in bits, not bytes + if ((key_length & 0x7) != 0) { + // invalid key length is not a multiple of 8 bits + r = TR31_ERROR_INVALID_KEY_LENGTH; + goto error; + } + key_length /= 8; // convert to bytes + if (key_length > state->payload_length - 2) { // invalid key length relative to encrypted payload length r = TR31_ERROR_INVALID_KEY_LENGTH; goto error; } // verify authenticator - r = tr31_aes_verify_cmac(kbak, kbpk->length, decrypted_key_block, sizeof(decrypted_key_block), ctx->authenticator, ctx->authenticator_length); + memcpy(state->payload, decrypted_payload, state->payload_length); + r = tr31_aes_verify_cmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + state->authenticator, + state->authenticator_length + ); if (r) { r = TR31_ERROR_KEY_BLOCK_VERIFICATION_FAILED; goto error; } // extract key data - r = tr31_key_set_data(&ctx->key, decrypted_payload->data, key_length); + r = tr31_key_set_data(key, decrypted_payload->data, key_length); if (r) { // return error value as-is goto error; @@ -2591,7 +2731,10 @@ static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_ctx_t* ctx, co // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_key_block, sizeof(decrypted_key_block)); + if (decrypted_payload) { + crypto_cleanse(decrypted_payload, state->payload_length); + free(decrypted_payload); + } return r; } diff --git a/test/tr31_decode_test.c b/test/tr31_decode_test.c index 5319da9..8bb04fb 100644 --- a/test/tr31_decode_test.c +++ b/test/tr31_decode_test.c @@ -62,11 +62,7 @@ int main(void) test_tr31.opt_blocks[0].id != TR31_OPT_BLOCK_KS || test_tr31.opt_blocks[0].data_length != sizeof(test1_ksn_verify) || test_tr31.opt_blocks[0].data == NULL || - memcmp(test_tr31.opt_blocks[0].data, test1_ksn_verify, sizeof(test1_ksn_verify)) != 0 || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + memcmp(test_tr31.opt_blocks[0].data, test1_ksn_verify, sizeof(test1_ksn_verify)) != 0 ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -90,11 +86,7 @@ int main(void) test_tr31.key.key_version_str[0] != 0 || test_tr31.key.exportability != TR31_KEY_EXPORT_NONE || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -118,11 +110,7 @@ int main(void) test_tr31.key.key_version_str[0] != 0 || test_tr31.key.exportability != TR31_KEY_EXPORT_NONE || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 48 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -154,11 +142,7 @@ int main(void) test_tr31.opt_blocks[1].id != TR31_OPT_BLOCK_KC || test_tr31.opt_blocks[1].data == NULL || test_tr31.opt_blocks[2].id != TR31_OPT_BLOCK_KP || - test_tr31.opt_blocks[2].data == NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks[2].data == NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; diff --git a/test/tr31_decrypt_test.c b/test/tr31_decrypt_test.c index f1f9a28..c8fc47e 100644 --- a/test/tr31_decrypt_test.c +++ b/test/tr31_decrypt_test.c @@ -238,11 +238,7 @@ int main(void) test_tr31.key.length != sizeof(test1_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 4 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -278,11 +274,7 @@ int main(void) test_tr31.key.length != sizeof(test1_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -318,11 +310,7 @@ int main(void) test_tr31.key.length != sizeof(test1_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 4 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -364,11 +352,7 @@ int main(void) test_tr31.key.length != sizeof(test2_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 4 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -410,11 +394,7 @@ int main(void) test_tr31.key.length != sizeof(test3_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -460,11 +440,7 @@ int main(void) test_tr31.opt_blocks[0].id != TR31_OPT_BLOCK_KS || test_tr31.opt_blocks[0].data_length != sizeof(test4_tr31_ksn_verify) || test_tr31.opt_blocks[0].data == NULL || - memcmp(test_tr31.opt_blocks[0].data, test4_tr31_ksn_verify, sizeof(test4_tr31_ksn_verify)) != 0 || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 4 || - test_tr31.authenticator == NULL + memcmp(test_tr31.opt_blocks[0].data, test4_tr31_ksn_verify, sizeof(test4_tr31_ksn_verify)) != 0 ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -510,11 +486,7 @@ int main(void) test_tr31.opt_blocks[0].id != TR31_OPT_BLOCK_KS || test_tr31.opt_blocks[0].data_length != sizeof(test5_tr31_ksn_verify) || test_tr31.opt_blocks[0].data == NULL || - memcmp(test_tr31.opt_blocks[0].data, test5_tr31_ksn_verify, sizeof(test5_tr31_ksn_verify)) != 0 || - test_tr31.payload_length != 24 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + memcmp(test_tr31.opt_blocks[0].data, test5_tr31_ksn_verify, sizeof(test5_tr31_ksn_verify)) != 0 ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -556,11 +528,7 @@ int main(void) test_tr31.key.length != sizeof(test6_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -602,11 +570,7 @@ int main(void) test_tr31.key.length != sizeof(test7_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -648,11 +612,7 @@ int main(void) test_tr31.key.length != sizeof(test8_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 48 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -694,11 +654,7 @@ int main(void) test_tr31.key.length != sizeof(test9_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 18 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -740,11 +696,7 @@ int main(void) test_tr31.key.length != sizeof(test10_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -786,11 +738,7 @@ int main(void) test_tr31.key.length != sizeof(test11_tr31_key_verify) || test_tr31.key.data == NULL || test_tr31.opt_blocks_count != 0 || - test_tr31.opt_blocks != NULL || - test_tr31.payload_length != 48 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks != NULL ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -836,11 +784,7 @@ int main(void) test_tr31.opt_blocks[0].id != TR31_OPT_BLOCK_KS || test_tr31.opt_blocks[0].data_length != sizeof(test15_tr31_ksn_verify) || test_tr31.opt_blocks[0].data == NULL || - memcmp(test_tr31.opt_blocks[0].data, test15_tr31_ksn_verify, sizeof(test15_tr31_ksn_verify)) != 0 || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 4 || - test_tr31.authenticator == NULL + memcmp(test_tr31.opt_blocks[0].data, test15_tr31_ksn_verify, sizeof(test15_tr31_ksn_verify)) != 0 ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -886,11 +830,7 @@ int main(void) test_tr31.opt_blocks[0].id != TR31_OPT_BLOCK_KS || test_tr31.opt_blocks[0].data_length != sizeof(test16_tr31_ksn_verify) || test_tr31.opt_blocks[0].data == NULL || - memcmp(test_tr31.opt_blocks[0].data, test16_tr31_ksn_verify, sizeof(test16_tr31_ksn_verify)) != 0 || - test_tr31.payload_length != 32 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 8 || - test_tr31.authenticator == NULL + memcmp(test_tr31.opt_blocks[0].data, test16_tr31_ksn_verify, sizeof(test16_tr31_ksn_verify)) != 0 ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -942,11 +882,7 @@ int main(void) test_tr31.opt_blocks[2].data_length != strlen(test17_tr31_ts_verify) || test_tr31.opt_blocks[2].data == NULL || memcmp(test_tr31.opt_blocks[2].data, test17_tr31_ts_verify, strlen(test17_tr31_ts_verify)) != 0 || - test_tr31.opt_blocks[3].id != TR31_OPT_BLOCK_PB || - test_tr31.payload_length != 1200 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks[3].id != TR31_OPT_BLOCK_PB ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; @@ -988,11 +924,7 @@ int main(void) test_tr31.opt_blocks[2].data_length != strlen(test18_tr31_ts_verify) || test_tr31.opt_blocks[2].data == NULL || memcmp(test_tr31.opt_blocks[2].data, test18_tr31_ts_verify, strlen(test18_tr31_ts_verify)) != 0 || - test_tr31.opt_blocks[3].id != TR31_OPT_BLOCK_PB || - test_tr31.payload_length != 128 || - test_tr31.payload == NULL || - test_tr31.authenticator_length != 16 || - test_tr31.authenticator == NULL + test_tr31.opt_blocks[3].id != TR31_OPT_BLOCK_PB ) { fprintf(stderr, "TR-31 context is incorrect\n"); r = 1; From ae4608daab9cfeeb52ee1503140732f89d9867e7 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sun, 22 Oct 2023 18:32:35 +0200 Subject: [PATCH 08/11] Refactor tr31_export() to use processing state object * Copy export flags to processing state object. Eventually this will be removed from the context object. * Remove internal processing fields from context object and let context object parameter for tr31_export() be const * Let tr31_state_prepare_export() update final key block length in key block header At a high level, tr31_state_prepare_export() will populate a buffer containing the key block header and wrapped key, including key length obfuscation and padding. For format version A and C, the encrypted payload can easily be copied in such that the buffer can then be used for CBC-MAC generation. For format version B and D, the buffer can be used as-is for CMAC generation. This change is part of a wider effort to move the fields intended for internal use out of the context object where they are visible to the caller. Instead a new internal processing state object will hold these fields and ensure that they are properly cleansed. --- src/tr31.c | 490 +++++++++++++++++++++++++++-------------------------- src/tr31.h | 13 +- 2 files changed, 255 insertions(+), 248 deletions(-) diff --git a/src/tr31.c b/src/tr31.c index 3ffc51e..b98c26b 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -92,6 +92,9 @@ struct tr31_payload_t { // Internal processing state struct tr31_state_t { + // flags used during processing + uint32_t flags; + // encryption block size used for header length validation unsigned int enc_block_size; @@ -123,17 +126,17 @@ static struct tr31_opt_ctx_t* tr31_opt_block_alloc(struct tr31_ctx_t* ctx, unsig static int tr31_opt_block_parse(const void* ptr, size_t remaining_len, size_t* opt_block_len, struct tr31_opt_ctx_t* opt_ctx); static int tr31_opt_block_validate_iso8601(const char* ts_str, size_t ts_str_len); static int tr31_opt_block_export(const struct tr31_opt_ctx_t* opt_ctx, size_t remaining_len, size_t* opt_blk_len, void* ptr); -static int tr31_opt_block_export_PB(const struct tr31_ctx_t* ctx, size_t pb_len, struct tr31_opt_blk_t* opt_blk); -static int tr31_state_init(uint8_t version_id, struct tr31_state_t* state); +static int tr31_opt_block_export_PB(const struct tr31_state_t* state, size_t pb_len, struct tr31_opt_blk_t* opt_blk); +static int tr31_state_init(uint32_t flags, uint8_t version_id, struct tr31_state_t* state); static int tr31_state_prepare_import(struct tr31_state_t* state, const void* key_block, size_t key_block_len, size_t header_len); +static int tr31_state_prepare_export(struct tr31_state_t* state, struct tr31_header_t* header, size_t header_len, size_t key_block_buf_len, const struct tr31_key_t* key); static void tr31_state_release(struct tr31_state_t* state); -static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx); static int tr31_tdes_decrypt_verify_variant_binding(const struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); -static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk); static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); -static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk); static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key); -static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk); +static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk); static int dec_to_int(const char* str, size_t str_len) { @@ -1217,7 +1220,7 @@ int tr31_import( // - state.enc_block_size // - state.authenticator_length header = (const struct tr31_header_t*)key_block; - r = tr31_state_init(header->version_id, &state); + r = tr31_state_init(0, header->version_id, &state); if (r) { // return error value as-is return r; @@ -1501,16 +1504,16 @@ int tr31_import( } int tr31_export( - struct tr31_ctx_t* ctx, + const struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk, char* key_block, size_t key_block_buf_len ) { int r; + struct tr31_state_t state; struct tr31_header_t* header; size_t opt_blk_len_total = 0; - unsigned int enc_block_size; void* ptr; if (!ctx || !kbpk || !key_block || !key_block_buf_len) { @@ -1529,33 +1532,14 @@ int tr31_export( memset(key_block, 0, key_block_buf_len); --key_block_buf_len; - // validate key block format version - // set encryption block size for header padding - switch (ctx->version) { - case TR31_VERSION_A: - case TR31_VERSION_C: - // supported - enc_block_size = DES_BLOCK_SIZE; - break; - - case TR31_VERSION_B: - // supported - enc_block_size = DES_BLOCK_SIZE; - break; - - case TR31_VERSION_D: - // supported - enc_block_size = AES_BLOCK_SIZE; - break; - - case TR31_VERSION_E: - // supported - enc_block_size = AES_BLOCK_SIZE; - break; - - default: - // unsupported - return TR31_ERROR_UNSUPPORTED_VERSION; + // initialise processing state object + // this will populate: + // - state.enc_block_size + // - state.authenticator_length + r = tr31_state_init(ctx->export_flags, ctx->version, &state); + if (r) { + // return error value as-is + return r; } // populate key block header @@ -1649,12 +1633,12 @@ int tr31_export( // does not make an exception for format version E. // So we'll use the encryption block size which is determined by the TR-31 // format version. - if (opt_blk_len_total & (enc_block_size-1)) { + if (opt_blk_len_total & (state.enc_block_size-1)) { unsigned int pb_len = 4; // Minimum length of optional block PB // compute required padding length - if ((opt_blk_len_total + pb_len) & (enc_block_size-1)) { // if further padding is required - pb_len = ((opt_blk_len_total + 4 + enc_block_size) & ~(enc_block_size-1)) - opt_blk_len_total; + if ((opt_blk_len_total + pb_len) & (state.enc_block_size-1)) { // if further padding is required + pb_len = ((opt_blk_len_total + 4 + state.enc_block_size) & ~(state.enc_block_size-1)) - opt_blk_len_total; } if (ptr + pb_len - (void*)header > key_block_buf_len) { @@ -1663,7 +1647,7 @@ int tr31_export( } // populate optional block PB - r = tr31_opt_block_export_PB(ctx, pb_len, ptr); + r = tr31_opt_block_export_PB(&state, pb_len, ptr); if (r) { // return error value as-is return r; @@ -1679,134 +1663,126 @@ int tr31_export( ptr += pb_len; } - // update header data in context object - ctx->header_length = ptr - (void*)header; - ctx->header = (void*)header; - - // determine final key block lengths, including key obfuscation padding - // this will populate these fields: - // - ctx->length - // - ctx->payload_length - // - ctx->authenticator_length - r = tr31_compute_final_lengths(ctx); + // prepare state object for export processing + // this function requires: + // - state.authenticator_length + // and will: + // - apply key obfuscation padding + // - encode wrapped key + // - update length in header + // - populate remaining state fields required by binding functions + r = tr31_state_prepare_export( + &state, + header, + ptr - (void*)header, + key_block_buf_len, + &ctx->key + ); if (r) { // return error value as-is return r; } - if (ctx->length > key_block_buf_len) { - return TR31_ERROR_INVALID_LENGTH; - } - - // update key block length in header - int_to_dec(ctx->length, header->length, sizeof(header->length)); - - // free internal buffers that my be populated due to reuse of the context object - if (ctx->payload) { - free(ctx->payload); - ctx->payload = NULL; - } - if (ctx->authenticator) { - free(ctx->authenticator); - ctx->authenticator = NULL; - } switch (ctx->version) { case TR31_VERSION_A: case TR31_VERSION_C: // only allow TDES key block protection keys if (kbpk->algorithm != TR31_KEY_ALGORITHM_TDES) { - return TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + r = TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + goto error; } // encrypt and sign payload - // this will populate: - // ctx->payload - // ctx->authenticator - r = tr31_tdes_encrypt_sign_variant_binding(ctx, kbpk); + // this will write data into: + // - state.payload + // - state.authenticator + r = tr31_tdes_encrypt_sign_variant_binding(&state, kbpk); if (r) { // return error value as-is - return r; + goto error; } break; case TR31_VERSION_B: // only allow TDES key block protection keys if (kbpk->algorithm != TR31_KEY_ALGORITHM_TDES) { - return TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + r = TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + goto error; } // sign and encrypt payload - // this will populate: - // ctx->payload - // ctx->authenticator - r = tr31_tdes_encrypt_sign_derivation_binding(ctx, kbpk); + // this will write data into: + // - state.payload + // - state.authenticator + r = tr31_tdes_encrypt_sign_derivation_binding(&state, kbpk); if (r) { // return error value as-is - return r; + goto error; } break; case TR31_VERSION_D: // only allow AES key block protection keys if (kbpk->algorithm != TR31_KEY_ALGORITHM_AES) { - return TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + r = TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + goto error; } // sign and encrypt payload - // this will populate: - // ctx->payload - // ctx->authenticator - r = tr31_aes_encrypt_sign_derivation_binding(ctx, kbpk); + // this will write data into: + // - state.payload + // - state.authenticator + r = tr31_aes_encrypt_sign_derivation_binding(&state, kbpk); if (r) { // return error value as-is - return r; + goto error; } break; case TR31_VERSION_E: // only allow AES key block protection keys if (kbpk->algorithm != TR31_KEY_ALGORITHM_AES) { - return TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + r = TR31_ERROR_UNSUPPORTED_KBPK_ALGORITHM; + goto error; } // sign and encrypt payload - // this will populate: - // ctx->payload - // ctx->authenticator - r = tr31_aes_encrypt_sign_derivation_binding(ctx, kbpk); + // this will write data into: + // - state.payload + // - state.authenticator + r = tr31_aes_encrypt_sign_derivation_binding(&state, kbpk); if (r) { // return error value as-is - return r; + goto error; } break; default: // invalid format version - return -3; - } - - // ensure that encrypted payload and authenticator are available - if (!ctx->payload || !ctx->authenticator) { - // internal error - return -4; + r = -3; + goto error; } - // add payload to key block - r = bin_to_hex(ctx->payload, ctx->payload_length, ptr, key_block_buf_len); + // add payload and authenticator to key block output + r = bin_to_hex( + state.payload, + state.payload_length + state.authenticator_length, + ptr, + key_block_buf_len - state.header_length + ); if (r) { // internal error - return -5; + r = -4; + goto error; } - ptr += (ctx->payload_length * 2); - // add authenticator to key block - r = bin_to_hex(ctx->authenticator, ctx->authenticator_length, ptr, key_block_buf_len); - if (r) { - // internal error - return -6; - } + r = 0; + goto exit; - return 0; +error: +exit: + tr31_state_release(&state); + return r; } static int tr31_opt_block_parse( @@ -2114,7 +2090,7 @@ static int tr31_opt_block_export( } static int tr31_opt_block_export_PB( - const struct tr31_ctx_t* ctx, + const struct tr31_state_t* state, size_t pb_len, struct tr31_opt_blk_t* opt_blk ) @@ -2122,7 +2098,7 @@ static int tr31_opt_block_export_PB( opt_blk->id = htons(TR31_OPT_BLOCK_PB); int_to_hex(pb_len, opt_blk->length, sizeof(opt_blk->length)); - if ((ctx->export_flags & TR31_EXPORT_ZERO_OPT_BLOCK_PB) == 0) { + if ((state->flags & TR31_EXPORT_ZERO_OPT_BLOCK_PB) == 0) { // populate with random data and then transpose to the required range crypto_rand(opt_blk->data, pb_len - 4); } else { @@ -2159,9 +2135,10 @@ static int tr31_opt_block_export_PB( return 0; } -static int tr31_state_init(uint8_t version_id, struct tr31_state_t* state) +static int tr31_state_init(uint32_t flags, uint8_t version_id, struct tr31_state_t* state) { memset(state, 0, sizeof(*state)); + state->flags = flags; // determine authenticator length and encryption block size switch (version_id) { @@ -2240,33 +2217,30 @@ static int tr31_state_prepare_import( return 0; } -static void tr31_state_release(struct tr31_state_t* state) -{ - if (state->decoded_key_block) { - // cleanse this buffer because the derivation binding functions copy - // cleartext key data into it for CMAC verification - crypto_cleanse(state->decoded_key_block, state->decoded_key_block_length); - free(state->decoded_key_block); - } - memset(state, 0, sizeof(*state)); -} - -static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) +static int tr31_state_prepare_export( + struct tr31_state_t* state, + struct tr31_header_t* header, + size_t header_len, + size_t key_block_buf_len, + const struct tr31_key_t* key +) { size_t padded_key_length; + size_t length; + struct tr31_payload_t* payload; // validate key length by algorithm // this ensures that key length cannot exceed padded key length - switch (ctx->key.algorithm) { + switch (key->algorithm) { case TR31_KEY_ALGORITHM_TDES: - if (ctx->key.length > 24) { + if (key->length > 24) { // invalid TDES key length return TR31_ERROR_INVALID_KEY_LENGTH; } break; case TR31_KEY_ALGORITHM_AES: - if (ctx->key.length > 32) { + if (key->length > 32) { // invalid AES key length return TR31_ERROR_INVALID_KEY_LENGTH; } @@ -2274,12 +2248,12 @@ static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) } // use key length as-is by default - padded_key_length = ctx->key.length; + padded_key_length = key->length; - if ((ctx->export_flags & TR31_EXPORT_NO_KEY_LENGTH_OBFUSCATION) == 0) { + if ((state->flags & TR31_EXPORT_NO_KEY_LENGTH_OBFUSCATION) == 0) { // apply key length obfuscation // see ANSI X9.143:2021, 5 and 6.1 - switch (ctx->key.algorithm) { + switch (key->algorithm) { case TR31_KEY_ALGORITHM_TDES: // use maximum TDES length padded_key_length = 24; @@ -2292,26 +2266,22 @@ static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) } } - switch (ctx->version) { + switch (header->version_id) { case TR31_VERSION_A: case TR31_VERSION_C: - ctx->payload_length = DES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); - ctx->authenticator_length = 4; // 4 bytes; 8 ASCII hex digits + state->payload_length = DES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); break; case TR31_VERSION_B: - ctx->payload_length = DES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); - ctx->authenticator_length = 8; // 8 bytes; 16 ASCII hex digits + state->payload_length = DES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); break; case TR31_VERSION_D: - ctx->payload_length = AES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); - ctx->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits + state->payload_length = AES_CIPHERTEXT_LENGTH(sizeof(struct tr31_payload_t) + padded_key_length); break; case TR31_VERSION_E: - ctx->payload_length = sizeof(struct tr31_payload_t) + padded_key_length; // no additional padding required - ctx->authenticator_length = 16; // 16 bytes; 32 ASCII hex digits + state->payload_length = sizeof(struct tr31_payload_t) + padded_key_length; // no additional padding required break; default: @@ -2319,14 +2289,47 @@ static int tr31_compute_final_lengths(struct tr31_ctx_t* ctx) return TR31_ERROR_UNSUPPORTED_VERSION; } - ctx->length = - + ctx->header_length - + (ctx->payload_length * 2) - + (ctx->authenticator_length * 2); + // populate key block length + state->header_length = header_len; + length = + + state->header_length + + (state->payload_length * 2) + + (state->authenticator_length * 2); + if (length > key_block_buf_len) { + return TR31_ERROR_INVALID_LENGTH; + } + int_to_dec(length, header->length, sizeof(header->length)); + + // prepare decoded key block buffer + state->decoded_key_block_length = state->header_length + state->payload_length + state->authenticator_length; + state->decoded_key_block = malloc(state->decoded_key_block_length); + memcpy(state->decoded_key_block, header, state->header_length); + state->payload = state->decoded_key_block + state->header_length; + state->authenticator = state->payload + state->payload_length; + + // encode wrapped key and apply key padding + payload = state->payload; + payload->length = htons(key->length * 8); // payload length is big endian and in bits, not bytes + memcpy(payload->data, key->data, key->length); + crypto_rand( + payload->data + key->length, + state->payload_length - sizeof(struct tr31_payload_t) - key->length + ); return 0; } +static void tr31_state_release(struct tr31_state_t* state) +{ + if (state->decoded_key_block) { + // cleanse this buffer because it contains the cleartext key during + // derivation binding CMAC generation/verification + crypto_cleanse(state->decoded_key_block, state->decoded_key_block_length); + free(state->decoded_key_block); + } + memset(state, 0, sizeof(*state)); +} + static int tr31_tdes_decrypt_verify_variant_binding(const struct tr31_state_t* state, const struct tr31_key_t* kbpk, struct tr31_key_t* key) { int r; @@ -2409,32 +2412,13 @@ static int tr31_tdes_decrypt_verify_variant_binding(const struct tr31_state_t* s return r; } -static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk) { int r; uint8_t kbek[TDES3_KEY_SIZE]; uint8_t kbak[TDES3_KEY_SIZE]; - - // add payload data to context object - ctx->payload = calloc(1, ctx->payload_length); - - // add authenticator to context object - ctx->authenticator = calloc(1, ctx->authenticator_length); - - // buffer for encrypted - uint8_t decrypted_payload_buf[ctx->payload_length]; - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)decrypted_payload_buf; - - // buffer for MAC generation - uint8_t mac_input[ctx->header_length + ctx->payload_length]; - - // populate payload key - decrypted_payload->length = htons(ctx->key.length * 8); // payload length is big endian and in bits, not bytes - memcpy(decrypted_payload->data, ctx->key.data, ctx->key.length); - crypto_rand( - decrypted_payload->data + ctx->key.length, - ctx->payload_length - sizeof(struct tr31_payload_t) - ctx->key.length - ); + uint8_t* encrypted_payload = NULL; + uint8_t mac[DES_CBCMAC_SIZE]; // output key block encryption key variant and key block authentication key variant r = tr31_tdes_kbpk_variant(kbpk->data, kbpk->length, kbek, kbak); @@ -2444,22 +2428,39 @@ static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const } // encrypt key payload; note that the TR-31 header is used as the IV - r = crypto_tdes_encrypt(kbek, kbpk->length, ctx->header, decrypted_payload, ctx->payload_length, ctx->payload); + encrypted_payload = malloc(state->payload_length); + r = crypto_tdes_encrypt( + kbek, + kbpk->length, + state->decoded_key_block, + state->payload, + state->payload_length, + encrypted_payload + ); if (r) { // return error value as-is goto error; } // generate authenticator - uint8_t mac[DES_CBCMAC_SIZE]; - memcpy(mac_input, ctx->header, ctx->header_length); - memcpy(mac_input + ctx->header_length, ctx->payload, ctx->payload_length); - r = crypto_tdes_cbcmac(kbak, kbpk->length, mac_input, sizeof(mac_input), mac); - if (r) { + memcpy(state->payload, encrypted_payload, state->payload_length); + r = crypto_tdes_cbcmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + mac + ); + if (r > 0) { + // internal error + r = -10; + goto error; + } + if (r < 0) { // return error value as-is goto error; } - memcpy(ctx->authenticator, mac, ctx->authenticator_length); + memcpy(state->authenticator, mac, state->authenticator_length); // success r = 0; @@ -2470,8 +2471,10 @@ static int tr31_tdes_encrypt_sign_variant_binding(struct tr31_ctx_t* ctx, const // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_payload_buf, sizeof(decrypted_payload_buf)); - crypto_cleanse(mac_input, sizeof(mac_input)); + if (encrypted_payload) { + crypto_cleanse(encrypted_payload, state->payload_length); + free(encrypted_payload); + } crypto_cleanse(mac, sizeof(mac)); return r; @@ -2560,30 +2563,13 @@ static int tr31_tdes_decrypt_verify_derivation_binding(struct tr31_state_t* stat return r; } -static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk) { int r; uint8_t kbek[TDES3_KEY_SIZE]; uint8_t kbak[TDES3_KEY_SIZE]; - - // add payload data to context object - ctx->payload = calloc(1, ctx->payload_length); - - // add authenticator to context object - ctx->authenticator = calloc(1, ctx->authenticator_length); - - // buffer for CMAC generation and encryption - uint8_t decrypted_key_block[ctx->header_length + ctx->payload_length]; - memcpy(decrypted_key_block, ctx->header, ctx->header_length); - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)(decrypted_key_block + ctx->header_length); - - // populate payload key - decrypted_payload->length = htons(ctx->key.length * 8); // payload length is big endian and in bits, not bytes - memcpy(decrypted_payload->data, ctx->key.data, ctx->key.length); - crypto_rand( - decrypted_payload->data + ctx->key.length, - ctx->payload_length - sizeof(struct tr31_payload_t) - ctx->key.length - ); + uint8_t cmac[DES_CMAC_SIZE]; + uint8_t* encrypted_payload = NULL; // derive key block encryption key and key block authentication key from key block protection key r = tr31_tdes_kbpk_derive(kbpk->data, kbpk->length, kbek, kbak); @@ -2593,20 +2579,39 @@ static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, con } // generate authenticator - uint8_t cmac[DES_CMAC_SIZE]; - r = crypto_tdes_cmac(kbak, kbpk->length, decrypted_key_block, sizeof(decrypted_key_block), cmac); - if (r) { + r = crypto_tdes_cmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + cmac + ); + if (r > 0) { + // internal error + r = -10; + goto error; + } + if (r < 0) { // return error value as-is goto error; } - memcpy(ctx->authenticator, cmac, ctx->authenticator_length); + memcpy(state->authenticator, cmac, state->authenticator_length); // encrypt key payload; note that the authenticator is used as the IV - r = crypto_tdes_encrypt(kbek, kbpk->length, ctx->authenticator, decrypted_payload, ctx->payload_length, ctx->payload); + encrypted_payload = malloc(state->payload_length); + r = crypto_tdes_encrypt( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + encrypted_payload + ); if (r) { // return error value as-is goto error; } + memcpy(state->payload, encrypted_payload, state->payload_length); // success r = 0; @@ -2617,7 +2622,10 @@ static int tr31_tdes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, con // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_key_block, sizeof(decrypted_key_block)); + if (encrypted_payload) { + crypto_cleanse(encrypted_payload, state->payload_length); + free(encrypted_payload); + } crypto_cleanse(cmac, sizeof(cmac)); return r; @@ -2739,33 +2747,17 @@ static int tr31_aes_decrypt_verify_derivation_binding(struct tr31_state_t* state return r; } -static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk) +static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_state_t* state, const struct tr31_key_t* kbpk) { int r; uint8_t kbek[AES256_KEY_SIZE]; uint8_t kbak[AES256_KEY_SIZE]; + const struct tr31_header_t* header; uint8_t cmac[AES_CMAC_SIZE]; + uint8_t* encrypted_payload = NULL; - // add payload data to context object - ctx->payload = calloc(1, ctx->payload_length); - - // add authenticator to context object - ctx->authenticator = calloc(1, ctx->authenticator_length); - - // buffer for CMAC generation and encryption - uint8_t decrypted_key_block[ctx->header_length + ctx->payload_length]; - memcpy(decrypted_key_block, ctx->header, ctx->header_length); - struct tr31_payload_t* decrypted_payload = (struct tr31_payload_t*)(decrypted_key_block + ctx->header_length); - - // populate payload key - decrypted_payload->length = htons(ctx->key.length * 8); // payload length is big endian and in bits, not bytes - memcpy(decrypted_payload->data, ctx->key.data, ctx->key.length); - crypto_rand( - decrypted_payload->data + ctx->key.length, - ctx->payload_length - sizeof(struct tr31_payload_t) - ctx->key.length - ); - - if (ctx->version == TR31_VERSION_D) { + header = state->decoded_key_block; + if (header->version_id == TR31_VERSION_D) { // derive key block encryption key and key block authentication key from key block protection key // format version D uses CBC block mode r = tr31_aes_kbpk_derive(kbpk->data, kbpk->length, TR31_AES_MODE_CBC, kbek, kbak); @@ -2775,21 +2767,36 @@ static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, cons } // generate authenticator - r = crypto_aes_cmac(kbak, kbpk->length, decrypted_key_block, sizeof(decrypted_key_block), cmac); + r = crypto_aes_cmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + cmac + ); if (r) { // return error value as-is goto error; } - memcpy(ctx->authenticator, cmac, ctx->authenticator_length); + memcpy(state->authenticator, cmac, state->authenticator_length); // encrypt key payload; note that the authenticator is used as the IV - r = crypto_aes_encrypt(kbek, kbpk->length, ctx->authenticator, decrypted_payload, ctx->payload_length, ctx->payload); + encrypted_payload = malloc(state->payload_length); + r = crypto_aes_encrypt( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + encrypted_payload + ); if (r) { // return error value as-is goto error; } + memcpy(state->payload, encrypted_payload, state->payload_length); - } else if (ctx->version == TR31_VERSION_E) { + } else if (header->version_id == TR31_VERSION_E) { // derive key block encryption key and key block authentication key from key block protection key // format version E uses CTR block mode r = tr31_aes_kbpk_derive(kbpk->data, kbpk->length, TR31_AES_MODE_CTR, kbek, kbak); @@ -2799,19 +2806,34 @@ static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, cons } // generate authenticator - r = crypto_aes_cmac(kbak, kbpk->length, decrypted_key_block, sizeof(decrypted_key_block), cmac); + r = crypto_aes_cmac( + kbak, + kbpk->length, + state->decoded_key_block, + state->header_length + state->payload_length, + cmac + ); if (r) { // return error value as-is goto error; } - memcpy(ctx->authenticator, cmac, ctx->authenticator_length); + memcpy(state->authenticator, cmac, state->authenticator_length); // encrypt key payload; note that the authenticator is used as the IV/nonce - r = crypto_aes_encrypt_ctr(kbek, kbpk->length, ctx->authenticator, decrypted_payload, ctx->payload_length, ctx->payload); + encrypted_payload = malloc(state->payload_length); + r = crypto_aes_encrypt_ctr( + kbek, + kbpk->length, + state->authenticator, + state->payload, + state->payload_length, + encrypted_payload + ); if (r) { // return error value as-is goto error; } + memcpy(state->payload, encrypted_payload, state->payload_length); } else { // invalid format version @@ -2827,7 +2849,10 @@ static int tr31_aes_encrypt_sign_derivation_binding(struct tr31_ctx_t* ctx, cons // cleanse sensitive buffers crypto_cleanse(kbek, sizeof(kbek)); crypto_cleanse(kbak, sizeof(kbak)); - crypto_cleanse(decrypted_key_block, sizeof(decrypted_key_block)); + if (encrypted_payload) { + crypto_cleanse(encrypted_payload, state->payload_length); + free(encrypted_payload); + } crypto_cleanse(cmac, sizeof(cmac)); return r; @@ -2850,15 +2875,6 @@ void tr31_release(struct tr31_ctx_t* ctx) free(ctx->opt_blocks); ctx->opt_blocks = NULL; } - - if (ctx->payload) { - free(ctx->payload); - ctx->payload = NULL; - } - if (ctx->authenticator) { - free(ctx->authenticator); - ctx->authenticator = NULL; - } } const char* tr31_get_error_string(enum tr31_error_t error) diff --git a/src/tr31.h b/src/tr31.h index 20a6fac..68856b5 100644 --- a/src/tr31.h +++ b/src/tr31.h @@ -221,22 +221,13 @@ struct tr31_opt_ctx_t { */ struct tr31_ctx_t { enum tr31_version_t version; ///< TR-31 key block format version - size_t length; ///< TR-31 key block length in bytes + size_t length; ///< TR-31 key block length in bytes (only populated by @ref tr31_import(), not @ref tr31_export()) struct tr31_key_t key; ///< TR-31 key object size_t opt_blocks_count; ///< TR-31 number of optional blocks struct tr31_opt_ctx_t* opt_blocks; ///< TR-31 optional block context objects - size_t header_length; ///< TR-31 header data length in bytes, including optional blocks - const void* header; ///< Pointer to TR-31 header data for internal use only. @warning For internal use only! - - size_t payload_length; ///< TR-31 payload data length in bytes - void* payload; ///< Decoded TR-31 payload data for internal use only. @warning For internal use only! - - size_t authenticator_length; ///< TR-31 authenticator data length in bytes - void* authenticator; ///< Decoded TR-31 authenticator data for internal use only. @warning For internal use only! - uint32_t export_flags; ///< Flags used during TR-31 export }; @@ -653,7 +644,7 @@ int tr31_import( * @return Zero for success. Less than zero for internal error. Greater than zero for data error. See @ref tr31_error_t */ int tr31_export( - struct tr31_ctx_t* ctx, + const struct tr31_ctx_t* ctx, const struct tr31_key_t* kbpk, char* key_block, size_t key_block_buf_len From acab36f55189330b7bcf23a8ce0e6d999ec48188 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Mon, 23 Oct 2023 20:41:50 +0200 Subject: [PATCH 09/11] Refactor tr31_key_set_data() to cleanup upon error This removes the necessity to use tr31_key_release() after tr31_key_set_data() returns an error. --- src/tr31.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/tr31.c b/src/tr31.c index b98c26b..9f7580b 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -491,6 +491,7 @@ void tr31_key_release(struct tr31_key_t* key) crypto_cleanse(key->data, key->length); free(key->data); key->data = NULL; + key->kcv_len = 0; } } @@ -532,13 +533,9 @@ int tr31_key_set_data(struct tr31_key_t* key, const void* data, size_t length) return -1; } + // release existing key data tr31_key_release(key); - // copy key data - key->length = length; - key->data = calloc(1, key->length); - memcpy(key->data, data, key->length); - // update KCV key->kcv_len = 0; memset(&key->kcv, 0, sizeof(key->kcv)); @@ -546,28 +543,33 @@ int tr31_key_set_data(struct tr31_key_t* key, const void* data, size_t length) // use legacy KCV for TDES key // see ANSI X9.24-1:2017, 7.7.2 key->kcv_algorithm = TR31_OPT_BLOCK_KCV_LEGACY; - r = crypto_tdes_kcv_legacy(key->data, key->length, key->kcv); + r = crypto_tdes_kcv_legacy(data, length, key->kcv); if (r) { // failed to compute KCV return TR31_ERROR_KCV_NOT_AVAILABLE; } key->kcv_len = DES_KCV_SIZE_LEGACY; - return 0; } else if (key->algorithm == TR31_KEY_ALGORITHM_AES) { // use CMAC-based KCV for AES key // see ANSI X9.24-1:2017, 7.7.2 key->kcv_algorithm = TR31_OPT_BLOCK_KCV_CMAC; - r = crypto_aes_kcv(key->data, key->length, key->kcv); + r = crypto_aes_kcv(data, length, key->kcv); if (r) { // failed to compute KCV return TR31_ERROR_KCV_NOT_AVAILABLE; } key->kcv_len = AES_KCV_SIZE; - return 0; + + } else { + // key algorithm not suitable for KCV computation; continue } - // key algorithm not suitable for KCV computation; continue + // copy key data + key->length = length; + key->data = malloc(key->length); + memcpy(key->data, data, key->length); + return 0; } @@ -1251,7 +1253,6 @@ int tr31_import( &ctx->key ); if (r) { - tr31_key_release(&ctx->key); // return error value as-is return r; } From 3527f97c284fda6b42dc636be57fb5a2beae4cb4 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sat, 28 Oct 2023 18:11:28 +0200 Subject: [PATCH 10/11] Remove optional block PB when adding new optional blocks Let tr31_opt_block_alloc() remove all instances of optional block PB when adding a new optional block. ANSI X9.143:2021, 6.3.6, states that the padding block will always be the last optional block in the header. Therefore, if optional block PB was previously added for whatever reason, for example via an export header by tr31-tool, it should be removed before adding further optional blocks. However, if no further optional blocks are added, an existing optional block PB is intentionally preserved specifically to allow the caller, or tr31-tool using an export header, to specify exact content for optional block PB. --- src/CMakeLists.txt | 12 +++++++++++- src/tr31.c | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 844e2f8..b31abf8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -671,11 +671,21 @@ if(TARGET tr31-tool AND BUILD_TESTING) PASS_REGULAR_EXPRESSION "D1840S0ES00N0400CT000405D6020002F0MIICLjCCAdSgAwIBAgIIGDrdWBxuNpAwCgYIKoZIzj0EAwIwMTEXMBUGA1UECgwOQWxwaGEgTWVyY2hhbnQxFjAUBgNVBAMMDVNhbXBsZSBFQ0MgQ0EwHhcNMjAwODE1MDIxMDEwWhcNMjEwODE1MDIxMDEwWjBPMRcwFQYDVQQKDA5BbHBoYSBNZXJjaGFudDEfMB0GA1UECwwWVExTIENsaWVudCBDZXJ0aWZpY2F0ZTETMBEGA1UEAwwKMTIzNDU2Nzg5MDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEI/SLrH6fITA9y6Y3BneuoT/5\\+EHSepZxCYeSstGll2sVvmSDZWWSbN6lh5Fb/zagrDjjQ/gZtWIOTf2wL1vSGjgbcwgbQwCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0OBBYEFHuvP526vFMywEoVoXZ5aXNfhnfeMB8GA1UdIwQYMBaAFI\\+ZFhOWF\\+oMtcfYwg15vH5WmWccMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwuYWxwaGEtbWVyY2hhbnQuZXhhbXBsZS9TYW1wbGVFQ0NDQS5jcmwwCgYIKoZIzj0EAwIDSAAwRQIhAPuWWvCTmOdvQzUjCUmTX7H4sX4Ebpw\\+CI\\+aOQLu1DqwAiA0eR4FdMtvXV4P6\\+WMz5B10oea5xtLTfSgoBDoTkvKYQ==0002C4MIICDjCCAbOgAwIBAgIIfnOsCbsxHjwwCgYIKoZIzj0EAwIwNjEXMBUGA1UECgwOQWxwaGEgTWVyY2hhbnQxGzAZBgNVBAMMElNhbXBsZSBSb290IEVDQyBDQTAeFw0yMDA4MTUwMjEwMDlaFw0zMDA4MTMwMjEwMDlaMDExFzAVBgNVBAoMDkFscGhhIE1lcmNoYW50MRYwFAYDVQQDDA1TYW1wbGUgRUNDIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHCanM9n\\+Rji\\+3EROj\\+HlogmXMU1Fk1td7N3I/8rfFnre1GwWCUqXSePHxwQ9DRHCV3oht3OUU2kDfitfUIujA6OBrzCBrDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUj5kWE5YX6gy1x9jCDXm8flaZZxwwHwYDVR0jBBgwFoAUvElIifFlt6oeUaopV9Y0lJtyPVQwRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2NybC5hbHBoYS1tZXJjaGFudC5leGFtcGxlL1NhbXBsZVJvb3RFQ0NDQS5jcmwwCgYIKoZIzj0EAwIDSQAwRgIhALT8\\+DG\\+\\+\\+KuqqUGyBQ4YG4s34fqbujclxZTHxYWVVSNAiEAn3v5Xmct7fkLpkjGexiHsy6D90r0K2LlUqpN/069y5s=010004asdfKP10012331550BC9TS1320200818004100ZPB07" ) + # test export header containing optional blocks and padding, but not adding additional optional blocks add_test(NAME tr31_tool_test40 COMMAND tr31-tool --kbpk 88E1AB2A2E3DD38C1FA039A536500CC8A87AB9D62DC92C01058FA79F44657DE6 --export 3F419E1CB7079442AA37474C2EFBF8B8 --export-header D1234M7HG00N0200HM0621PB0A5V5E8F ) set_tests_properties(tr31_tool_test40 PROPERTIES - PASS_REGULAR_EXPRESSION "^D0128M7HG00N0200HM0621PB0A5V5E8F" + PASS_REGULAR_EXPRESSION "^D0128M7HG00N0200HM0621PB0A5V5E8F" # note that optional block padding content is intentionally preserved + ) + + # test export header containing optional blocks and padding and also adding additional optional blocks + add_test(NAME tr31_tool_test41 + COMMAND tr31-tool --kbpk 88E1AB2A2E3DD38C1FA039A536500CC8A87AB9D62DC92C01058FA79F44657DE6 --export 3F419E1CB7079442AA37474C2EFBF8B8 --export-header D1234M7HG00N0200HM0621PB0A5V5E8F --export-opt-block-LB "MyKey" + ) + set_tests_properties(tr31_tool_test41 + PROPERTIES + PASS_REGULAR_EXPRESSION "^D0144M7HG00N0300HM0621LB09MyKeyPB11" ) endif() diff --git a/src/tr31.c b/src/tr31.c index 9f7580b..4ebde5d 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -679,18 +679,44 @@ static struct tr31_opt_ctx_t* tr31_opt_block_alloc( ) { struct tr31_opt_ctx_t* opt_ctx; + bool opt_blk_pb_found = false; if (!ctx) { return NULL; } // repeated optional block IDs are not allowed + // and optional block PB must always be last // see ANSI X9.143:2021, 6.3.6 for (size_t i = 0; i < ctx->opt_blocks_count; ++i) { if (ctx->opt_blocks[i].id == id) { // existing optional block found return NULL; } + + if (ctx->opt_blocks[i].id == TR31_OPT_BLOCK_PB) { + // optional block PB found + opt_blk_pb_found = true; + } + } + + // if optional block PB already exists, remove all instances + // NOTE: it will be recreated by tr31_export() + // NOTE: if no new optional blocks are added, PB is intentionally preserved + if (opt_blk_pb_found) { + for (size_t i = 0; i < ctx->opt_blocks_count; ++i) { + if (ctx->opt_blocks[i].id == TR31_OPT_BLOCK_PB) { + free(ctx->opt_blocks[i].data); + ctx->opt_blocks[i].data = NULL; + + ctx->opt_blocks_count -= 1; + if (i < ctx->opt_blocks_count) { + size_t remaining_count = ctx->opt_blocks_count - i; + size_t remaining_bytes = sizeof(*ctx->opt_blocks) * remaining_count; + memmove(&ctx->opt_blocks[i], &ctx->opt_blocks[i + 1], remaining_bytes); + } + } + } } // grow optional block array From 3bd3f7bed05d6776bf012d16b2eba1787da4af73 Mon Sep 17 00:00:00 2001 From: Leon Lynch Date: Sun, 29 Oct 2023 11:19:16 +0100 Subject: [PATCH 11/11] Enhance --export-header option for tr31-tool * If the header (and optional blocks) are not already a multiple of the encryption block size, add fake optional block padding. * Currently this enhancement only supports an optional block count of at most 8 to avoid having to parse the optional block count digits. This does not impact tr31_import() in any way though and is purely a tr31-tool limitation. * The fake optional block padding is removed once tr31_import() succeeds such that tr31_export() can apply new optional block padding as required. * The processing of an export header is now encapsulated in tr31-tool as tr31_init_from_header() such that it can eventually be reused for other purposes too, like parsing verbatim optional block strings, or moved into the TR-31 library. --- README.md | 9 ++- src/CMakeLists.txt | 9 +++ src/tr31-tool.c | 154 +++++++++++++++++++++++++++++++++------------ 3 files changed, 132 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index e9b3600..9489ce3 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,14 @@ either a combination of the `--export-format-version B`, `--export-key-algorithm` and `--export-template` options, or using the `--export-header` option. For example: ``` -tr31-tool --kbpk AB2E09DB3EF0BA71E0CE6CD755C23A3B --export BF82DAC6A33DF92CE66E15B70E5DCEB6 --export-header B0128B1TX00N0300KS18FFFF00A0200001E00000KC0C000169E3KP0C00ECAD62 +tr31-tool --kbpk AB2E09DB3EF0BA71E0CE6CD755C23A3B --export BF82DAC6A33DF92CE66E15B70E5DCEB6 --export-header B0000B1TX00N0200KS18FFFF00A0200001E00000KC0C000169E3 +``` + +Individual optional blocks can also be added when exporting a TR-31 key block +by using the various `--export-opt-block-XX` functions, where `XX` is the +optional block identifier. For example: +``` +tr31-tool --kbpk AB2E09DB3EF0BA71E0CE6CD755C23A3B --export BF82DAC6A33DF92CE66E15B70E5DCEB6 --export-header B0000B1TX00N0000 --export-opt-block-KS FFFF00A0200001E00000 --export-opt-block-KC ``` Roadmap diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b31abf8..52f19ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -688,4 +688,13 @@ if(TARGET tr31-tool AND BUILD_TESTING) PROPERTIES PASS_REGULAR_EXPRESSION "^D0144M7HG00N0300HM0621LB09MyKeyPB11" ) + + # test export header containing optional blocks but insufficient padding + add_test(NAME tr31_tool_test42 + COMMAND tr31-tool --kbpk 88E1AB2A2E3DD38C1FA039A536500CC8A87AB9D62DC92C01058FA79F44657DE6 --export 3F419E1CB7079442AA37474C2EFBF8B8 --export-header D1234M7HG00N0100HM0621 + ) + set_tests_properties(tr31_tool_test42 + PROPERTIES + PASS_REGULAR_EXPRESSION "^D0128M7HG00N0200HM0621PB0A" + ) endif() diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 4bad11f..c288b48 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -96,7 +96,9 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state); static void* read_file(FILE* file, size_t* len); static int parse_hex(const char* hex, void* bin, size_t bin_len); static void print_hex(const void* buf, size_t length); +static void print_str(const void* buf, size_t length); static void print_str_with_quotes(const void* buf, size_t length); +static int tr31_init_from_header(const char* header, struct tr31_ctx_t* tr31_ctx); // argp option keys enum tr31_tool_option_keys_t { @@ -657,6 +659,117 @@ static void print_str_with_quotes(const void* buf, size_t length) printf("\""); } +static int tr31_init_from_header(const char* header, struct tr31_ctx_t* tr31_ctx) +{ + int r; + size_t header_len; + size_t enc_block_size; + size_t payload_and_authenticator_len; + char* padded_header = NULL; + size_t key_block_len; + + header_len = strlen(header); + if (header_len < 16) { + return TR31_ERROR_INVALID_LENGTH_FIELD; + } + + // determine encryption block size and payload+authenticator length + switch (header[0]) { + case 'A': + case 'C': + enc_block_size = 8; // DES block size + payload_and_authenticator_len = 32 + 8; + break; + + case 'B': + enc_block_size = 8; // DES block size + payload_and_authenticator_len = 32 + 16; + break; + + case 'D': + case 'E': + enc_block_size = 16; // AES block size + payload_and_authenticator_len = 48 + 16; + break; + + default: + return TR31_ERROR_UNSUPPORTED_VERSION; + } + + // ensure that header length is a multiple of encryption block size + // and add fake optional block padding if necessary + if (header_len & (enc_block_size-1)) { + unsigned int pb_len = 4; // minimum length of optional block PB + + if (header[12] != '0' || header[13] > '8') { + // only support single digit optional block counts below 9 for now + return TR31_ERROR_INVALID_NUMBER_OF_OPTIONAL_BLOCKS_FIELD; + } + + // compute required padding length + if ((header_len + pb_len) & (enc_block_size-1)) { // if further padding is required + pb_len = ((header_len + 4 + enc_block_size) & ~(enc_block_size-1)) - header_len; + } + + // sanity check + if (pb_len < 4 || pb_len > 15) { + return -1; + } + + // build new header + header_len = header_len + pb_len; + padded_header = malloc(header_len + 1); + snprintf( + padded_header, + header_len + 1, + "%sPB%02X%.*s", + header, + pb_len, + pb_len - 4, + "000000000000000" + ); + padded_header[13]++; // increment optional block count + header = padded_header; + } + + // determine fake key block length to allow parsing of header + key_block_len = header_len + payload_and_authenticator_len; + if (key_block_len > 9999) { + fprintf(stderr, "Export header too large\n"); + return TR31_ERROR_INVALID_LENGTH_FIELD; + } + + // build fake key block to allow parsing of header + char tmp_keyblock[key_block_len]; + memcpy(tmp_keyblock, header, header_len); + memset(tmp_keyblock + header_len, '0', sizeof(tmp_keyblock) - header_len); + + // fix length field to allow parsing of header + char tmp[5]; + snprintf(tmp, sizeof(tmp), "%04zu", sizeof(tmp_keyblock)); + memcpy(tmp_keyblock + 1, tmp, 4); + + // misuse TR-31 import function to parse header into TR-31 context object + r = tr31_import(tmp_keyblock, sizeof(tmp_keyblock), NULL, tr31_ctx); + if (r) { + return r; + } + + // cleanup padded header and remove fake optional block PB from context object + if (padded_header) { + free(padded_header); + if (tr31_ctx->opt_blocks_count) { + tr31_ctx->opt_blocks_count -= 1; + if (tr31_ctx->opt_blocks[tr31_ctx->opt_blocks_count].data) { + free(tr31_ctx->opt_blocks[tr31_ctx->opt_blocks_count].data); + tr31_ctx->opt_blocks[tr31_ctx->opt_blocks_count].data = NULL; + } + } + } + + return 0; +} + // TR-31 KBPK populating helper function static int populate_kbpk(const struct tr31_tool_options_t* options, unsigned int format_version, struct tr31_key_t* kbpk) { @@ -909,45 +1022,8 @@ static int populate_tr31_from_header(const struct tr31_tool_options_t* options, { int r; - // determine fake key block length to allow parsing of header - size_t export_header_len = strlen(options->export_header); - size_t tmp_key_block_len = export_header_len; - switch (options->export_header[0]) { - case 'A': - case 'C': - tmp_key_block_len += 32 + 8; - break; - - case 'B': - tmp_key_block_len += 32 + 16; - break; - - case 'D': - case 'E': - tmp_key_block_len += 48 + 16; - break; - - default: - fprintf(stderr, "Unsupported key block format version\n"); - return 1; - } - if (tmp_key_block_len > 9999) { - fprintf(stderr, "Export header too large\n"); - return 1; - } - - // build fake key block to allow parsing of header - char tmp_keyblock[tmp_key_block_len]; - memcpy(tmp_keyblock, options->export_header, export_header_len); - memset(tmp_keyblock + export_header_len, '0', sizeof(tmp_keyblock) - export_header_len); - - // fix length field to allow parsing of header - char tmp[5]; - snprintf(tmp, sizeof(tmp), "%04zu", sizeof(tmp_keyblock)); - memcpy(tmp_keyblock + 1, tmp, 4); - - // misuse TR-31 import function to parse header into TR-31 context object - r = tr31_import(tmp_keyblock, sizeof(tmp_keyblock), NULL, tr31_ctx); + // parse export header + r = tr31_init_from_header(options->export_header, tr31_ctx); if (r) { fprintf(stderr, "Error while parsing export header; error %d: %s\n", r, tr31_get_error_string(r)); return 1;