Skip to content

Commit

Permalink
Basic support for parsing IBM proprietary key blocks
Browse files Browse the repository at this point in the history
When identifying the IBM proprietary key usage and mode of use
attributes, it is necessary to determine whether the IBM proprietary
optional block is also present to distinguish it from other proprietary
key blocks using the same attribute values.
  • Loading branch information
leonlynch committed Nov 30, 2023
1 parent 5ec2582 commit 2f7ab32
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 18 deletions.
48 changes: 48 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -869,4 +869,52 @@ if(TARGET tr31-tool AND BUILD_TESTING)
PROPERTIES
PASS_REGULAR_EXPRESSION ${tr31_tool_test52_regex}
)

# test IBM proprietary key block
# NOTE: the input was hand crafted based on https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data and https://www.ibm.com/docs/en/linux-on-systems?topic=data-tr-31-optional-block
add_test(NAME tr31_tool_test53
COMMAND tr31-tool --import D014410A100N0200101CIBMC01140123456789ABCDEFPB04000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --import-no-strict-validation
)
string(CONCAT tr31_tool_test53_regex
"^Key block format version: D[\r\n]"
"Key block length: 144 bytes[\r\n]"
"Key usage: \\[10\\] IBM[\r\n]"
"Key algorithm: \\[A\\] AES[\r\n]"
"Key mode of use: \\[1\\] IBM[\r\n]"
"Key version: Unused[\r\n]"
"Key exportability: \\[N\\] Not exportable[\r\n]"
"Key context: \\[0\\] Determined by wrapping key[\r\n]"
"Optional blocks \\[2\\]:[\r\n]"
"\t\\[10\\] IBM: IBMC01140123456789ABCDEF \\(Common Cryptographic Architecture \\(CCA\\) Control Vector \\(CV\\)\\)[\r\n]"
"\t\\[PB\\] Padding Block: \"\"[\r\n]"
"Key not decrypted[\r\n]"
)
set_tests_properties(tr31_tool_test53
PROPERTIES
PASS_REGULAR_EXPRESSION ${tr31_tool_test53_regex}
)

# test non-IBM proprietary key block
# NOTE: the input was hand crafted based on tr31_tool_test53 but excludes "IBMC"
add_test(NAME tr31_tool_test54
COMMAND tr31-tool --import D014410A100N0200101801140123456789ABCDEFPB08asdf000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 --import-no-strict-validation
)
string(CONCAT tr31_tool_test54_regex
"^Key block format version: D[\r\n]"
"Key block length: 144 bytes[\r\n]"
"Key usage: \\[10\\] Unknown key usage value[\r\n]"
"Key algorithm: \\[A\\] AES[\r\n]"
"Key mode of use: \\[1\\] Unknown key mode of use value[\r\n]"
"Key version: Unused[\r\n]"
"Key exportability: \\[N\\] Not exportable[\r\n]"
"Key context: \\[0\\] Determined by wrapping key[\r\n]"
"Optional blocks \\[2\\]:[\r\n]"
"\t\\[10\\] Unknown: 01140123456789ABCDEF[\r\n]"
"\t\\[PB\\] Padding Block: \"asdf\"[\r\n]"
"Key not decrypted[\r\n]"
)
set_tests_properties(tr31_tool_test54
PROPERTIES
PASS_REGULAR_EXPRESSION ${tr31_tool_test54_regex}
)
endif()
6 changes: 3 additions & 3 deletions src/tr31-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,15 +787,15 @@ static int do_tr31_import(const struct tr31_tool_options_t* options)
printf("Key block length: %zu bytes\n", tr31_ctx.length);
printf("Key usage: [%s] %s\n",
tr31_key_usage_get_ascii(tr31_ctx.key.usage, ascii_buf, sizeof(ascii_buf)),
tr31_key_usage_get_desc(tr31_ctx.key.usage)
tr31_key_usage_get_desc(&tr31_ctx)
);
printf("Key algorithm: [%c] %s\n",
tr31_ctx.key.algorithm,
tr31_key_algorithm_get_desc(&tr31_ctx, tr31_ctx.key.algorithm)
);
printf("Key mode of use: [%c] %s\n",
tr31_ctx.key.mode_of_use,
tr31_key_mode_of_use_get_desc(tr31_ctx.key.mode_of_use)
tr31_key_mode_of_use_get_desc(&tr31_ctx)
);
switch (tr31_ctx.key.key_version) {
case TR31_KEY_VERSION_IS_UNUSED: printf("Key version: Unused\n"); break;
Expand All @@ -821,7 +821,7 @@ static int do_tr31_import(const struct tr31_tool_options_t* options)

printf("\t[%s] %s: ",
tr31_opt_block_id_get_ascii(tr31_ctx.opt_blocks[i].id, ascii_buf, sizeof(ascii_buf)),
tr31_opt_block_id_get_desc(tr31_ctx.opt_blocks[i].id)
tr31_opt_block_id_get_desc(&tr31_ctx.opt_blocks[i])
);

switch (tr31_ctx.opt_blocks[i].id) {
Expand Down
8 changes: 8 additions & 0 deletions src/tr31.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@ enum tr31_key_version_t {
#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.

// Key block attributes and optional block format defined by IBM
#define TR31_KEY_USAGE_IBM (0x3130) ///< Key Usage 10: IBM proprietary key usage
#define TR31_KEY_MODE_OF_USE_IBM ('1') ///< Key Mode of Use 1: IBM proprietary mode for keys
#define TR31_OPT_BLOCK_10_IBM (0x3130) ///< Optional Block 10: IBM proprietary optional block
#define TR31_OPT_BLOCK_10_IBM_MAGIC "IBMC" ///< IBM proprietary optional block magic value
#define TR31_OPT_BLOCK_10_IBM_TLV_CCA_CV (0x01) ///< IBM proprietary optional block: Common Cryptographic Architecture (CCA) Control Vector (CV)
#define TR31_OPT_BLOCK_10_IBM_TLV_X9_SWKB (0x02) ///< IBM proprietary optional block: Internal X9-SWKB controls

/// TR-31 key object
struct tr31_key_t {
unsigned int usage; ///< TR-31 key usage
Expand Down
112 changes: 106 additions & 6 deletions src/tr31_strings.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "tr31_config.h"

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#if defined(HAVE_ARPA_INET_H)
Expand Down Expand Up @@ -53,6 +54,9 @@ static const char* tr31_opt_block_hmac_get_string(const struct tr31_opt_ctx_t* o
static const char* tr31_opt_block_kcv_get_string(const struct tr31_opt_ctx_t* opt_block);
static int tr31_opt_block_iso8601_get_string(const struct tr31_opt_ctx_t* opt_block, char* str, size_t str_len);
static const char* tr31_opt_block_wrapping_pedigree_get_string(const struct tr31_opt_ctx_t* opt_block);
static bool tr31_opt_block_is_ibm(const struct tr31_opt_ctx_t* opt_block);
static bool tr31_opt_block_ibm_found(const struct tr31_ctx_t* ctx);
static const char* tr31_opt_block_ibm_get_string(const struct tr31_opt_ctx_t* opt_block);

static int tr31_validate_format_an(const char* buf, size_t buf_len)
{
Expand Down Expand Up @@ -97,10 +101,14 @@ const char* tr31_key_usage_get_ascii(unsigned int usage, char* ascii, size_t asc
return ascii;
}

const char* tr31_key_usage_get_desc(unsigned int usage)
const char* tr31_key_usage_get_desc(const struct tr31_ctx_t* ctx)
{
if (!ctx) {
return NULL;
}

// See ANSI X9.143:2021, 6.3.1, table 2
switch (usage) {
switch (ctx->key.usage) {
case TR31_KEY_USAGE_BDK: return "Base Derivation Key (BDK)";
case TR31_KEY_USAGE_DUKPT_IK: return "Initial DUKPT Key (IK/IPEK)";
case TR31_KEY_USAGE_BKV: return "Base Key Variant Key";
Expand Down Expand Up @@ -146,6 +154,13 @@ const char* tr31_key_usage_get_desc(unsigned int usage)
case TR31_KEY_USAGE_PVK_X9_132_ALG_3: return "PIN Verification Key (ANSI X9.132 algorithm 3)";
}

// See https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data
if (ctx->key.usage == TR31_KEY_USAGE_IBM &&
tr31_opt_block_ibm_found(ctx)
) {
return "IBM";
}

return "Unknown key usage value";
}

Expand Down Expand Up @@ -175,10 +190,14 @@ const char* tr31_key_algorithm_get_desc(const struct tr31_ctx_t* ctx, unsigned i
return "Unknown key algorithm value";
}

const char* tr31_key_mode_of_use_get_desc(unsigned int mode_of_use)
const char* tr31_key_mode_of_use_get_desc(const struct tr31_ctx_t* ctx)
{
if (!ctx) {
return NULL;
}

// See ANSI X9.143:2021, 6.3.3, table 4
switch (mode_of_use) {
switch (ctx->key.mode_of_use) {
case TR31_KEY_MODE_OF_USE_ENC_DEC: return "Encrypt/Wrap and Decrypt/Unwrap";
case TR31_KEY_MODE_OF_USE_MAC: return "MAC Generate and Verify";
case TR31_KEY_MODE_OF_USE_DEC: return "Decrypt/Unwrap Only";
Expand All @@ -191,6 +210,13 @@ const char* tr31_key_mode_of_use_get_desc(unsigned int mode_of_use)
case TR31_KEY_MODE_OF_USE_VARIANT: return "Create Key Variants";
}

// See https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data
if (ctx->key.mode_of_use == TR31_KEY_MODE_OF_USE_IBM &&
tr31_opt_block_ibm_found(ctx)
) {
return "IBM";
}

return "Unknown key mode of use value";
}

Expand Down Expand Up @@ -242,10 +268,14 @@ const char* tr31_opt_block_id_get_ascii(unsigned int opt_block_id, char* ascii,
return ascii;
}

const char* tr31_opt_block_id_get_desc(unsigned int opt_block_id)
const char* tr31_opt_block_id_get_desc(const struct tr31_opt_ctx_t* opt_block)
{
if (!opt_block) {
return NULL;
}

// See ANSI X9.143:2021, 6.3.6, table 7
switch (opt_block_id) {
switch (opt_block->id) {
case TR31_OPT_BLOCK_AL: return "Asymmetric Key Life (AKL)";
case TR31_OPT_BLOCK_BI: return "Base Derivation Key (BDK) Identifier";
case TR31_OPT_BLOCK_CT: return "Public Key Certificate";
Expand All @@ -263,6 +293,12 @@ const char* tr31_opt_block_id_get_desc(unsigned int opt_block_id)
case TR31_OPT_BLOCK_TC: return "Time of Creation";
case TR31_OPT_BLOCK_TS: return "Time Stamp";
case TR31_OPT_BLOCK_WP: return "Wrapping Pedigree";
default: {
if (tr31_opt_block_is_ibm(opt_block)) {
return "IBM";
}
break;
}
}

return "Unknown";
Expand Down Expand Up @@ -307,6 +343,10 @@ int tr31_opt_block_data_get_desc(const struct tr31_opt_ctx_t* opt_block, char* s
case TR31_OPT_BLOCK_WP:
simple_str = tr31_opt_block_wrapping_pedigree_get_string(opt_block);
break;

default:
simple_str = tr31_opt_block_ibm_get_string(opt_block);
break;
}

if (simple_str) {
Expand Down Expand Up @@ -619,3 +659,63 @@ static const char* tr31_opt_block_wrapping_pedigree_get_string(const struct tr31

return "Unknown";
}

static bool tr31_opt_block_is_ibm(const struct tr31_opt_ctx_t* opt_block)
{
if (opt_block->id != TR31_OPT_BLOCK_10_IBM) {
return false;
}

// See https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data
if (opt_block->data_length < strlen(TR31_OPT_BLOCK_10_IBM_MAGIC)) {
return false;
}
if (memcmp(opt_block->data, TR31_OPT_BLOCK_10_IBM_MAGIC, strlen(TR31_OPT_BLOCK_10_IBM_MAGIC)) != 0) {
return false;
}

if ((opt_block->data_length == 0x1C - 4 || opt_block->data_length != 0x2C - 4) &&
memcmp(opt_block->data + 4, "01", 2) == 0
) {
// IBM Common Cryptographic Architecture (CCA) Control Vector (CV)
return true;
}

if (opt_block->data_length == 0x24 - 4 &&
memcmp(opt_block->data + 4, "02", 2) == 0
) {
// IBM Internal X9-SWKB controls
return true;
}

return false;
}

static bool tr31_opt_block_ibm_found(const struct tr31_ctx_t* ctx)
{
struct tr31_opt_ctx_t* opt_block;

opt_block = tr31_opt_block_find((struct tr31_ctx_t*)ctx, TR31_OPT_BLOCK_10_IBM);
if (!opt_block) {
return false;
}

return tr31_opt_block_is_ibm(opt_block);
}

static const char* tr31_opt_block_ibm_get_string(const struct tr31_opt_ctx_t* opt_block)
{
if (!tr31_opt_block_is_ibm(opt_block)) {
return NULL;
}

// See https://www.ibm.com/docs/en/zos/3.1.0?topic=ktf-x9143-tr-31-key-block-header-optional-block-data
if (memcmp(opt_block->data + 4, "01", 2) == 0) {
return "Common Cryptographic Architecture (CCA) Control Vector (CV)";
}
if (memcmp(opt_block->data + 4, "02", 2) == 0) {
return "Internal X9-SWKB controls";
}

return 0;
}
21 changes: 12 additions & 9 deletions src/tr31_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ struct tr31_opt_ctx_t;
const char* tr31_key_usage_get_ascii(unsigned int usage, char* ascii, size_t ascii_len);

/**
* Retrieve human readable description associated with key usage value
* Retrieve human readable description associated with key usage
*
* @param usage Key usage value
* @param ctx TR-31 context object
* @return Pointer to null-terminated string. Do not free.
*/
const char* tr31_key_usage_get_desc(unsigned int usage);
const char* tr31_key_usage_get_desc(const struct tr31_ctx_t* ctx);

/**
* Retrieve human readable description associated with key algorithm value.
Expand All @@ -62,12 +62,12 @@ const char* tr31_key_usage_get_desc(unsigned int usage);
const char* tr31_key_algorithm_get_desc(const struct tr31_ctx_t* ctx, unsigned int algorithm);

/**
* Retrieve human readable description associated with key mode of use value
* Retrieve human readable description associated with key mode of use
*
* @param mode_of_use Key mode of use value
* @param ctx TR-31 context object
* @return Pointer to null-terminated string. Do not free.
*/
const char* tr31_key_mode_of_use_get_desc(unsigned int mode_of_use);
const char* tr31_key_mode_of_use_get_desc(const struct tr31_ctx_t* ctx);

/**
* Retrieve human readable description associated with key exportability value
Expand Down Expand Up @@ -96,12 +96,15 @@ const char* tr31_key_context_get_desc(unsigned int key_context);
const char* tr31_opt_block_id_get_ascii(unsigned int opt_block_id, char* ascii, size_t ascii_len);

/**
* Retrieve human readable description associated with optional block ID value
* Retrieve human readable description associated with optional block ID value.
*
* @param opt_block_id Optional block ID value
* This function may also consider the optional block length and optional block
* data when determining the description of the optional block ID.
*
* @param opt_block Optional block context object
* @return Pointer to null-terminated string. Do not free.
*/
const char* tr31_opt_block_id_get_desc(unsigned int opt_block_id);
const char* tr31_opt_block_id_get_desc(const struct tr31_opt_ctx_t* opt_block);

/**
* Provide human readable description of optional block data, if available. The
Expand Down

0 comments on commit 2f7ab32

Please sign in to comment.