Skip to content

Commit

Permalink
Implement import flag to disable strict validation
Browse files Browse the repository at this point in the history
* Also add --import-no-strict-validation option to tr31-tool
* This is useful for importing non-standard key blocks
  • Loading branch information
leonlynch committed Nov 4, 2023
1 parent 47dd094 commit 21ca00a
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 39 deletions.
48 changes: 48 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
6 changes: 6 additions & 0 deletions src/tr31-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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;
Expand Down
115 changes: 77 additions & 38 deletions src/tr31.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion src/tr31.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit 21ca00a

Please sign in to comment.