diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0afa7c9..7b67483 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -728,4 +728,52 @@ if(TARGET tr31-tool AND BUILD_TESTING) PROPERTIES PASS_REGULAR_EXPRESSION "^D0128M7HG00N0200HM0621PB0A" ) + + # test --import-no-strict-validation with non-standard mode of use + add_test(NAME tr31_tool_test45 + COMMAND tr31-tool --import "B0128B1T900N0300KC0C000169E3KP0C00ECAD62KS18FFFF00A0200001E0000030EFDCB3A740B46D5BA7896587151A41E3D746CF063D0FC2D3FA45B851D06A43" --kbpk AB2E09DB3EF0BA71E0CE6CD755C23A3B --import-no-strict-validation + ) + string(CONCAT tr31_tool_test45_regex + "^Key block format version: B[\r\n]" + "Key block length: 128 bytes[\r\n]" + "Key usage: \\[B1\\] Initial DUKPT Key \\(IK/IPEK\\)[\r\n]" + "Key algorithm: \\[T\\] TDES[\r\n]" + "Key mode of use: \\[9\\] Unknown key mode of use value[\r\n]" + "Key version: Unused[\r\n]" + "Key exportability: \\[N\\] Not exportable[\r\n]" + "Optional blocks \\[3\\]:[\r\n]" + "\t\\[KC\\] Key Check Value \\(KCV\\) of wrapped key: 0169E3 \\(Legacy KCV algorithm\\)[\r\n]" + "\t\\[KP\\] Key Check Value \\(KCV\\) of KBPK: ECAD62 \\(Legacy KCV algorithm\\)[\r\n]" + "\t\\[KS\\] Initial Key Serial Number \\(KSN\\): FFFF00A0200001E00000[\r\n]" + "Key length: 16[\r\n]" + "Key value: BF82DAC6A33DF92CE66E15B70E5DCEB6 \\(KCV: 0169E3\\)[\r\n]" + ) + set_tests_properties(tr31_tool_test45 + PROPERTIES + PASS_REGULAR_EXPRESSION ${tr31_tool_test45_regex} + ) + + # test --import-no-strict-validation with non-standard optional block KS + add_test(NAME tr31_tool_test46 + COMMAND tr31-tool --import "B0120B1TX00N0300KS10DE#GBIC#OPT1KC0C000169E3KP0C00ECAD627E13D36248CFF4CA9D2F880E2401B3727F96C2D92E5FDCA0D932A6DA80CAE8B9" --kbpk AB2E09DB3EF0BA71E0CE6CD755C23A3B --import-no-strict-validation + ) + string(CONCAT tr31_tool_test46_regex + "^Key block format version: B[\r\n]" + "Key block length: 120 bytes[\r\n]" + "Key usage: \\[B1\\] Initial DUKPT Key \\(IK/IPEK\\)[\r\n]" + "Key algorithm: \\[T\\] TDES[\r\n]" + "Key mode of use: \\[X\\] Key Derivation[\r\n]" + "Key version: Unused[\r\n]" + "Key exportability: \\[N\\] Not exportable[\r\n]" + "Optional blocks \\[3\\]:[\r\n]" + "\t\\[KS\\] Initial Key Serial Number \\(KSN\\): DE#GBIC#OPT1[\r\n]" + "\t\\[KC\\] Key Check Value \\(KCV\\) of wrapped key: 0169E3 \\(Legacy KCV algorithm\\)[\r\n]" + "\t\\[KP\\] Key Check Value \\(KCV\\) of KBPK: ECAD62 \\(Legacy KCV algorithm\\)[\r\n]" + "Key length: 16[\r\n]" + "Key value: BF82DAC6A33DF92CE66E15B70E5DCEB6 \\(KCV: 0169E3\\)[\r\n]" + ) + set_tests_properties(tr31_tool_test46 + PROPERTIES + PASS_REGULAR_EXPRESSION ${tr31_tool_test46_regex} + ) endif() diff --git a/src/tr31-tool.c b/src/tr31-tool.c index 1dc608a..10c61b8 100644 --- a/src/tr31-tool.c +++ b/src/tr31-tool.c @@ -102,6 +102,7 @@ static int tr31_init_from_header(const char* header, struct tr31_ctx_t* tr31_ctx // argp option keys enum tr31_tool_option_keys_t { TR31_TOOL_OPTION_IMPORT = 1, + TR31_TOOL_OPTION_IMPORT_NO_STRICT_VALIDATION, TR31_TOOL_OPTION_EXPORT, TR31_TOOL_OPTION_EXPORT_KEY_ALGORITHM, TR31_TOOL_OPTION_EXPORT_FORMAT_VERSION, @@ -132,6 +133,7 @@ enum tr31_tool_option_keys_t { static struct argp_option argp_options[] = { { NULL, 0, NULL, 0, "Options for decoding/decrypting TR-31 key blocks:", 1 }, { "import", TR31_TOOL_OPTION_IMPORT, "KEYBLOCK", 0, "Import TR-31 key block to decode/decrypt. Use - to read raw bytes from stdin. Optionally specify KBPK (--kbpk) to decrypt." }, + { "import-no-strict-validation", TR31_TOOL_OPTION_IMPORT_NO_STRICT_VALIDATION, NULL, 0, "Disable strict validation during key block import" }, { NULL, 0, NULL, 0, "Options for encoding/encrypting TR-31 key blocks:", 2 }, { "export", TR31_TOOL_OPTION_EXPORT, "KEY", 0, "Export TR-31 key block containing KEY. Use - to read raw bytes from stdin. Requires KBPK (--kbpk). Requires either --export-key-algorithm, --export-format-version and --export-template, or only --export-header" }, @@ -264,6 +266,10 @@ static error_t argp_parser_helper(int key, char* arg, struct argp_state* state) options->import = true; return 0; + case TR31_TOOL_OPTION_IMPORT_NO_STRICT_VALIDATION: + options->import_flags |= TR31_IMPORT_NO_STRICT_VALIDATION; + return 0; + case TR31_TOOL_OPTION_EXPORT: options->export_key_buf = buf; options->export_key_buf_len = buf_len; diff --git a/src/tr31.c b/src/tr31.c index 7996d42..7dc7cb7 100644 --- a/src/tr31.c +++ b/src/tr31.c @@ -127,7 +127,7 @@ static struct tr31_opt_ctx_t* tr31_opt_block_alloc(struct tr31_ctx_t* ctx, unsig static inline size_t tr31_opt_block_kcv_data_length(size_t kcv_len); static int tr31_opt_block_encode_kcv(uint8_t kcv_algorithm, const void* kcv, size_t kcv_len, char* encoded_data, size_t encoded_data_len); static int tr31_opt_block_validate_hash_algorithm(uint8_t hash_algorithm); -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_parse(const struct tr31_state_t* state, 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_state_t* state, size_t pb_len, struct tr31_opt_blk_t* opt_blk); @@ -363,9 +363,53 @@ int tr31_key_init( memset(key, 0, sizeof(*key)); + // initialise key attributes before validation + // tr31_import() may choose to ignore some validation errors + key->usage = usage; + key->algorithm = algorithm; + key->mode_of_use = mode_of_use; + if (key_version[0] == '0' && key_version[1] == '0') { + key->key_version = TR31_KEY_VERSION_IS_UNUSED; + memset(key->key_version_str, 0, sizeof(key->key_version_str)); + } else if (key_version[0] == 'c') { + key->key_version = TR31_KEY_VERSION_IS_COMPONENT; + memcpy(key->key_version_str, key_version, 2); + key->key_version_str[2] = 0; + } else { + key->key_version = TR31_KEY_VERSION_IS_VALID; + memcpy(key->key_version_str, key_version, 2); + key->key_version_str[2] = 0; + } + key->exportability = exportability; + + // if key data is available, copy it + if (data && length) { + // validate key length by algorithm + switch (algorithm) { + case TR31_KEY_ALGORITHM_TDES: + if (length > 24) { + // invalid TDES key length + return TR31_ERROR_INVALID_KEY_LENGTH; + } + break; + + case TR31_KEY_ALGORITHM_AES: + if (length > 32) { + // invalid AES key length + return TR31_ERROR_INVALID_KEY_LENGTH; + } + break; + } + + r = tr31_key_set_data(key, data, length); + if (r) { + // return error value as-is + return r; + } + } + // validate key usage field // see ANSI X9.143:2021, 6.3.1, table 2 - key->usage = usage; switch (usage) { case TR31_KEY_USAGE_BDK: case TR31_KEY_USAGE_DUKPT_IK: @@ -419,7 +463,6 @@ int tr31_key_init( // validate algorithm field // see ANSI X9.143:2021, 6.3.2, table 3 - key->algorithm = algorithm; switch (algorithm) { case TR31_KEY_ALGORITHM_AES: case TR31_KEY_ALGORITHM_DES: @@ -437,7 +480,6 @@ int tr31_key_init( // validate mode of use field // see ANSI X9.143:2021, 6.3.3, table 4 - key->mode_of_use = mode_of_use; switch (mode_of_use) { case TR31_KEY_MODE_OF_USE_ENC_DEC: case TR31_KEY_MODE_OF_USE_MAC: @@ -458,15 +500,13 @@ int tr31_key_init( // validate key version number field // see ANSI X9.143:2021, 6.3.4, table 5 - r = tr31_key_set_key_version(key, key_version); + r = tr31_validate_format_an(key_version, 2); if (r) { - // return error value as-is - return r; + return TR31_ERROR_INVALID_KEY_VERSION_FIELD; } // validate exportability field // see ANSI X9.143:2021, 6.3.5, table 6 - key->exportability = exportability; switch (exportability) { case TR31_KEY_EXPORT_TRUSTED: case TR31_KEY_EXPORT_NONE: @@ -478,32 +518,6 @@ int tr31_key_init( return TR31_ERROR_UNSUPPORTED_EXPORTABILITY; } - // if key data is available, copy it - if (data && length) { - // validate key length by algorithm - switch (algorithm) { - case TR31_KEY_ALGORITHM_TDES: - if (length > 24) { - // invalid TDES key length - return TR31_ERROR_INVALID_KEY_LENGTH; - } - break; - - case TR31_KEY_ALGORITHM_AES: - if (length > 32) { - // invalid AES key length - return TR31_ERROR_INVALID_KEY_LENGTH; - } - break; - } - - r = tr31_key_set_data(key, data, length); - if (r) { - // return error value as-is - return r; - } - } - return 0; } @@ -1889,8 +1903,18 @@ int tr31_import( &ctx->key ); if (r) { - // return error value as-is - return r; + if (flags & TR31_IMPORT_NO_STRICT_VALIDATION) { + // when strict validation is disabled, ignore all key attribute errors + if (r < TR31_ERROR_UNSUPPORTED_KEY_USAGE || + r > TR31_ERROR_UNSUPPORTED_EXPORTABILITY + ) { + // return error value as-is + return r; + } + } else { + // return error value as-is + return r; + } } // decode number of optional blocks field @@ -1916,6 +1940,7 @@ int tr31_import( // copy optional block field size_t opt_blk_len; r = tr31_opt_block_parse( + &state, ptr, (void*)key_block + key_block_len - ptr, &opt_blk_len, @@ -2443,6 +2468,7 @@ int tr31_export( } static int tr31_opt_block_parse( + const struct tr31_state_t* state, const void* ptr, size_t remaining_len, size_t* opt_blk_len, @@ -2525,8 +2551,21 @@ static int tr31_opt_block_parse( } opt_blk_data = ptr + opt_blk_hdr_len; - // perform validation of the character or string format required for each - // known optional block ID + // if strict validation is disabled, validate the format only as + // printable ASCII (format PA) + if ((state->flags & TR31_IMPORT_NO_STRICT_VALIDATION) != 0) { + opt_ctx->data_length = (*opt_blk_len - opt_blk_hdr_len); + r = tr31_validate_format_pa(opt_blk_data, opt_ctx->data_length); + if (r) { + return TR31_ERROR_INVALID_OPTIONAL_BLOCK_DATA; + } + opt_ctx->data = malloc(opt_ctx->data_length); + memcpy(opt_ctx->data, opt_blk_data, opt_ctx->data_length); + return 0; + } + + // perform strict validation of the character or string format required for + // each known optional block ID switch (opt_ctx->id) { // optional blocks to be validated as hex (format H) case TR31_OPT_BLOCK_AL: diff --git a/src/tr31.h b/src/tr31.h index a923496..e0c2623 100644 --- a/src/tr31.h +++ b/src/tr31.h @@ -177,7 +177,8 @@ enum tr31_key_version_t { #define TR31_OPT_BLOCK_WP_ASYMMETRIC (2) ///< Asymmetric key at risk of quantum computing #define TR31_OPT_BLOCK_WP_ASYMMETRIC_LT (3) ///< Asymmetric key at risk of quantum computing and symmetric key of lesser effective strength -// TR-31 export flags +// Key block import/export flags +#define TR31_IMPORT_NO_STRICT_VALIDATION (0x01) ///< Disable strict ANSI X9.143 / ISO 20038 validation during import. This is useful for importing non-standard key blocks. #define TR31_EXPORT_NO_KEY_LENGTH_OBFUSCATION (0x01) ///< Disable ANSI X9.143 key length obfuscation during key block export #define TR31_EXPORT_ZERO_OPT_BLOCK_PB (0x02) ///< Fill optional block PB using zeros instead of random characters during key block export.