DeriveMech_NISTKDFmGeneric Example

Example Code

// Example of DeriveMech_NISTKDFmGeneric usage
#include <nfastapp.h>
#include <stdlib.h>
#include <stdio.h>

static NFast_AppHandle app;
static NFastApp_Connection conn;

// Hexdump an array.
static void hexdump(const unsigned char *ptr, size_t n)
{
  for (size_t i = 0; i < n; i++) printf("%02x", ptr[i]);
}

// Send a command to the HSM.
// Like everything in this example, in terminates the process
// on error.
static void transact(const M_Command *cmd, M_Reply *reply)
{
  M_Status err;
  char buffer[256];

  err = NFastApp_Transact(conn, NULL, cmd, reply, NULL);
  if (err) {
    NFast_Perror("NFastApp_Transact", err);
    exit(1);
  }
  if (NFastApp_Expected_Reply(app, NULL, buffer, sizeof buffer, reply, cmd->cmd, NULL) <= 0) {
    fprintf(stderr, "NFastApp_Expected_Reply: %s\n", buffer);
    exit(1);
  }
}

// Import a key with a given ACL.
// Returns the key handle.
static M_KeyID import_key(const M_KeyData *keydata, const M_ACL *acl)
{
  M_Command cmd = {
    .cmd = Cmd_Import,
    .args.import.acl = *acl,
    .args.import.data = *keydata,
  };
  M_Reply reply = { 0 };
  transact(&cmd, &reply);
  M_KeyID key = reply.reply.import.key;
  NFastApp_Free_Reply(app, NULL, NULL, &reply);
  return key;
}

// Export a key.
static void export_key(M_KeyID key, M_KeyData *keydata)
{
  M_Command cmd = {
    .cmd = Cmd_Export,
    .args.export.key = key,
  };
  M_Reply reply = { 0 };
  transact(&cmd, &reply);
  *keydata = reply.reply.export.data;
  memset(&reply.reply.export.data, 0, sizeof reply.reply.export.data);
  NFastApp_Free_Reply(app, NULL, NULL, &reply);
}

// Destroy a key.
static void destroy_key(M_KeyID key)
{
  M_Command cmd = {
    .cmd = Cmd_Destroy,
    .args.destroy.key = key,
  };
  M_Reply reply = { 0 };
  transact(&cmd, &reply);
  NFastApp_Free_Reply(app, NULL, NULL, &reply);
}

// Get the nCore key hash of a key.
static M_KeyHash get_key_hash(M_KeyID key)
{
  M_Command cmd = {
    .cmd = Cmd_GetKeyInfo,
    .args.getkeyinfo.key = key,
  };
  M_Reply reply = { 0 };
  transact(&cmd, &reply);
  M_KeyHash keyhash = reply.reply.getkeyinfo.hash;
  NFastApp_Free_Reply(app, NULL, NULL, &reply);
  return keyhash;
}

// Import a template key that contains a given derive key ACL.
// Returns the key handle.
static M_KeyID import_template_key(void)
{
  M_Status err;

  // The derived key ACL.
  // In this example the only thing we want to do is export it,
  // so that's all we allow. In the key was going to be used for
  // encryption and decryption then instead we would have the
  // _Encrypt and _Decrypt bits.
  M_Action derived_key_action = {
    .type = Act_OpPermissions,
    .details.oppermissions.perms = Act_OpPermissions_Details_perms_ExportAsPlain,
  };
  M_PermissionGroup derived_key_group = {
    .n_actions = 1,
    .actions = &derived_key_action,
  };
  M_ACL derived_key_acl = {
    .n_groups = 1,
    .groups = &derived_key_group,
  };

  // The template key ACL. This just needs to permit the key to
  // be ued as a template key.
  M_Action template_action = {
    .type = Act_DeriveKey,
    .details.derivekey.mech = DeriveMech_NISTKDFmGeneric,
    .details.derivekey.role = DeriveRole_TemplateKey,
  };
  M_PermissionGroup template_group = {
    .n_actions = 1,
    .actions = &template_action,
  };
  M_ACL template_key_acl = {
    .n_groups = 1,
    .groups = &template_group,
  };

  // DKTemplate 'key' material is a marshaled ACL
  M_KeyData template_keydata = {
    .type = KeyType_DKTemplate,
  };
  if ((err = NFastApp_MarshalACL(
         app, NULL, NULL, &derived_key_acl, &template_keydata.data.dktemplate.nested_acl))) {
    NFast_Perror("NFastApp_MarshalACL", err);
    exit(1);
  }

  M_KeyID template_key = import_key(&template_keydata, &template_key_acl);
  NFastApp_Free(app, template_keydata.data.dktemplate.nested_acl.ptr, NULL, NULL);
  return template_key;
}

// Import the base key.
// Returns the key handle.
static M_KeyID import_base_key(size_t base_len, unsigned char *base, M_KeyHash template_key_hash)
{
  // The base key ACL
  M_KeyRoleID otherkeys = {
    .hash = template_key_hash,
    .role = DeriveRole_TemplateKey,
  };
  M_Action base_action = {
    .type = Act_DeriveKey,
    .details.derivekey.mech = DeriveMech_NISTKDFmGeneric,
    .details.derivekey.role = DeriveRole_BaseKey,
    // Restrict the ACL of the derive key to that given in
    // template_key_hash. In this example, where we just
    // export the derived key, this is futile, but in
    // some use cases the restriction is useful.
    .details.derivekey.n_otherkeys = 1,
    .details.derivekey.otherkeys = &otherkeys,
  };
  M_PermissionGroup base_group = {
    .n_actions = 1,
    .actions = &base_action,
  };
  M_ACL base_key_acl = {
    .n_groups = 1,
    .groups = &base_group,
  };

  // The base key material
  M_KeyData base_keydata = {
    .type = KeyType_Random,
    .data.random.k.ptr = base,
    .data.random.k.len = (M_Word)base_len,
  };

  return import_key(&base_keydata, &base_key_acl);
}

// Derive a key from a given base key.
// Returns the derived key handle.
static M_KeyID derive_key(size_t context_len, unsigned char *context, M_KeyID template_key,
                          M_KeyID base_key)
{
  // The set of fields used in the input to the PRF.
  static M_KDFField fields[] = { KDFField_Counter1r4BE, KDFField_Context, KDFField_Lengthr4BE };

  // Key handles passed to the command.
  // The order is always { template, base, wrap, ... }
  M_KeyID keys[] = { template_key, base_key };

  M_Command cmd = {
    .cmd = Cmd_DeriveKey,
    .args.derivekey.n_keys = 2,
    .args.derivekey.keys = keys,
    .args.derivekey.mech = DeriveMech_NISTKDFmGeneric,
    .args.derivekey.params.nistkdfmgeneric.keylen = 32 * 8, // bits
    .args.derivekey.params.nistkdfmgeneric.keytype = KeyType_Random,
    .args.derivekey.params.nistkdfmgeneric.prf = Mech_HMACSHA256,
    .args.derivekey.params.nistkdfmgeneric.context.len = (M_Word)context_len,
    .args.derivekey.params.nistkdfmgeneric.context.ptr = context,
    .args.derivekey.params.nistkdfmgeneric.n_fields = 3,
    .args.derivekey.params.nistkdfmgeneric.fields = fields,
  };
  M_Reply reply = { 0 };

  transact(&cmd, &reply);
  M_KeyID derived_key = reply.reply.derivekey.key;
  NFastApp_Free_Reply(app, NULL, NULL, &reply);
  return derived_key;
}

// Derive one frmo a given context and base key material
static void derive_key_example(size_t context_len, unsigned char *context, size_t base_len,
                               unsigned char *base)
{

  printf("Context: ");
  hexdump(context, context_len);
  printf("\n");
  printf("Base: ");
  hexdump(base, base_len);
  printf("\n");

  // Set up the inputs
  M_KeyID template_key = import_template_key();
  M_KeyID base_key = import_base_key(base_len, base, get_key_hash(template_key));

  // Do the derivation
  M_KeyID derived_key = derive_key(context_len, context, template_key, base_key);

  // Retrieve the derived key material
  M_KeyData derived_keydata;
  export_key(derived_key, &derived_keydata);

  // In this example we assume KeyType_Random output
  assert(derived_keydata.type == KeyType_Random);

  // Display the derived key material
  printf("Output: ");
  hexdump(derived_keydata.data.random.k.ptr, derived_keydata.data.random.k.len);
  printf("\n");

  // Clean up key handles
  destroy_key(base_key);
  destroy_key(template_key);
  destroy_key(derived_key);

  printf("\n");
}

// Run the KDF with some example inputs
static void example(void)
{
  static struct
  {
    size_t context_len;
    unsigned char context[64];
    size_t base_len;
    unsigned char base[64];
  } example_data[] = {
    {
      .context_len = 2,
      .context = { 0x00, 0x00 },
      .base_len = 32,
      .base = {
        0x66, 0x73, 0x6b, 0x70, 0x5f, 0x6b, 0x64, 0x6b, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
        0x20, 0x68, 0x6f, 0x77, 0x20, 0x66, 0x73, 0x6b, 0x70, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65,
      },
    },
    {
      .context_len = 34,
      .context = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x00, 0x00,
      },
      .base_len = 32,
      .base = {
        0x66, 0x73, 0x6b, 0x70, 0x5f, 0x6b, 0x64, 0x6b, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
        0x20, 0x68, 0x6f, 0x77, 0x20, 0x66, 0x73, 0x6b, 0x70, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65,
      },
    },
    {
      .context_len = 3,
      .context = { 0x01, 0x00, 0x01 },
      .base_len = 32,
      .base = {
        0x66, 0x73, 0x6b, 0x70, 0x5f, 0x6b, 0x64, 0x6b, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
        0x20, 0x68, 0x6f, 0x77, 0x20, 0x66, 0x73, 0x6b, 0x70, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65,
      },
    },
    {
      .context_len = 9,
      .context = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x04,  },
      .base_len = 32,
      .base = {
        0x66, 0x73, 0x6b, 0x70, 0x5f, 0x6b, 0x64, 0x6b, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
        0x20, 0x68, 0x6f, 0x77, 0x20, 0x66, 0x73, 0x6b, 0x70, 0x20, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65,
      },
    },
  };

  for (size_t i = 0; i < sizeof example_data / sizeof *example_data; i++) {
    derive_key_example(example_data[i].context_len,
                       example_data[i].context,
                       example_data[i].base_len,
                       example_data[i].base);
  }
}

int main(void)
{
  M_Status err;

  // Set up connectivity to the HSM
  if ((err = NFastApp_InitEx(&app, NULL, NULL))) {
    NFast_Perror("NFastApp_InitEx", err);
    exit(1);
  }
  if ((err = NFastApp_Connect(app, &conn, 0, NULL))) {
    NFast_Perror("NFastApp_Connect", err);
    exit(1);
  }

  example();

  return 0;
}

Running the example

To compile it under Linux:

gcc -I/opt/nfast/c/ctd/gcc/include/ \
    -o nistkdfmgeneric \
    nistkdfmgeneric.c \
    -L /opt/nfast/c/ctd/gcc/lib \
    -lnfstub -lnflog -lcutils

To run it:

./nistkdfmgeneric

The output should look like this:

Context: 0000
Base: 66736b705f6b646b206578616d706c6520686f772066736b7020646572697665
Output: f89b8dd626faa9a41365f20606969e1113d90f6340258c1308aa3927611b9b1c

Context: 00000000000000000000000000000000000000000000000000000000000000010000
Base: 66736b705f6b646b206578616d706c6520686f772066736b7020646572697665
Output: 0aa0dd4dbb3776e80474b638212041b76fa580d66a3e0fab6a741e482c85be69

Context: 010001
Base: 66736b705f6b646b206578616d706c6520686f772066736b7020646572697665
Output: 9259bb22085e479633e45554034230e44f94f688829993082b9ebeeebefc335f

Context: 000100020000030004
Base: 66736b705f6b646b206578616d706c6520686f772066736b7020646572697665
Output: 467b3dbf1a07d0aa69c3943f11448638f6cd0b16737d6887ed2de1285c6fc2a5

Limitations

  • The example only covers using the final (expansion) step.

  • The input key is imported rather than derived from some other source.

  • No effort is made to secure the derived key