aboutsummaryrefslogblamecommitdiff
path: root/asn1.c
blob: bbb049f32231d641a1b41f22591b95761c18b784 (plain) (tree)
326
327
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * hal_internal.h
 * --------------
 * Internal API declarations for libhal.
 *
 * Authors: Rob Austein, Paul Selkirk
 * Copyright (c) 2015, NORDUnet A/S All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTER
/*
 * asn1.c
 * ------
 * Minimal ASN.1 implementation in support of Cryptech libhal.
 *
 * The functions in this module are not intended to be part of the
 * public API.  Rather, these are utility functions used by more than
 * one module within the library, which would otherwise have to be
 * duplicated.  The main reason for keeping these private is to avoid
 * having the public API depend on any details of the underlying
 * bignum implementation (currently libtfm, but that might change).
 *
 * As of this writing, the ASN.1 support we need is quite minimal, so,
 * rather than attempting to clean all the unecessary cruft out of a
 * general purpose ASN.1 implementation, we hand code the very small
 * number of data types we need.  At some point this will probably
 * become impractical, at which point we might want to look into using
 * something like the asn1c compiler.
 *
 * Authors: Rob Austein
 * Copyright (c) 2015, NORDUnet A/S
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdint.h>
#include <assert.h>

#include "hal.h"

#include "asn1_internal.h"

/*
 * Encode tag and length fields of an ASN.1 object.
 *
 * Sets *der_len to the size of of the ASN.1 header (tag and length
 * fields); caller supplied length of value field, so presumably
 * already knows it.
 *
 * If der is NULL, just return the size of the header that would be
 * encoded and returns HAL_OK.
 *
 * If der isn't NULL, returns HAL_ERROR_RESULT_TOO_LONG unless full
 * header plus value will fit; this is a bit weird, but is useful when
 * using this to construct encoders for complte ASN.1 objects.
 */

hal_error_t hal_asn1_encode_header(const uint8_t tag,
				   const size_t value_len,
				   uint8_t *der, size_t *der_len, const size_t der_max)
{
  size_t header_len = 2;	/* Shortest encoding is one octet each for tag and length */

  if (value_len >= 128)		/* Add octets for longer length encoding as needed */
    for (size_t n = value_len; n > 0; n >>= 8)
      ++header_len;

  if (der_len != NULL)
    *der_len = header_len;

  if (der == NULL)		/* If caller just wanted the length, we're done */
    return HAL_OK;

  /*
   * Make sure there's enough room for header + value, then encode.
   */

  if (value_len + header_len > der_max)
    return HAL_ERROR_RESULT_TOO_LONG;

  *der++ = tag;

  if (value_len < 128) {
    *der = (uint8_t) value_len;
  }

  else {
    *der = 0x80 | (uint8_t) (header_len -= 2);
    for (size_t n = value_len; n > 0 && header_len > 0; n >>= 8)
      der[header_len--] = (uint8_t) (n & 0xFF);
  }

  return HAL_OK;
}

/*
 * Encode an unsigned ASN.1 INTEGER from a libtfm bignum.  If der is
 * NULL, just return the length of what we would have encoded.
 */

hal_error_t hal_asn1_encode_integer(const fp_int * const bn,
				    uint8_t *der, size_t *der_len, const size_t der_max)
{
  if (bn == NULL)
    return HAL_ERROR_BAD_ARGUMENTS;

  /*
   * We only handle unsigned INTEGERs, so we need to pad data with a
   * leading zero if the most significant bit is set, to avoid
   * flipping the ASN.1 sign bit.  Conveniently, this also handles the
   * difference between libtfm's and ASN.1's encoding of zero.
   */

  if (fp_cmp_d(unconst_fp_int(bn), 0) == FP_LT)
    return HAL_ERROR_BAD_ARGUMENTS;

  const int leading_zero = fp_iszero(bn) || (fp_count_bits(unconst_fp_int(bn)) & 7) == 0;
  const size_t vlen = fp_unsigned_bin_size(unconst_fp_int(bn)) + leading_zero;
  hal_error_t err;
  size_t hlen;

  err = hal_asn1_encode_header(ASN1_INTEGER, vlen, der, &hlen, der_max);

  if (der_len != NULL)
    *der_len = hlen + vlen;

  if (der == NULL || err != HAL_OK)
    return err;

  assert(hlen + vlen <= der_max);

  der += hlen;
  if (leading_zero)
    *der++ = 0x00;
  fp_to_unsigned_bin(unconst_fp_int(bn), der);

  return HAL_OK;
}

/*
 * Encode a public key into an RFC 5280 SubjectPublicKeyInfo.
 */

hal_error_t hal_asn1_encode_spki(const uint8_t * const alg_oid,   const size_t alg_oid_len,
                                 const uint8_t * const curve_oid, const size_t curve_oid_len,
                                 const uint8_t * const pubkey,    const size_t pubkey_len,
                                 uint8_t *der, size_t *der_len, const size_t der_max)
{
  if (alg_oid == NULL || alg_oid_len == 0 || pubkey_len == 0 ||
      (der != NULL && pubkey == NULL) || (curve_oid == NULL && curve_oid_len != 0))
    return HAL_ERROR_BAD_ARGUMENTS;

  const uint8_t curve_oid_tag = curve_oid == NULL ? ASN1_NULL : ASN1_OBJECT_IDENTIFIER;

  hal_error_t err;

  size_t hlen, hlen_spki, hlen_algid, hlen_alg, hlen_curve, hlen_bit;

  if ((err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, alg_oid_len,    NULL, &hlen_alg,   0)) != HAL_OK ||
      (err = hal_asn1_encode_header(curve_oid_tag,          curve_oid_len,  NULL, &hlen_curve, 0)) != HAL_OK ||
      (err = hal_asn1_encode_header(ASN1_BIT_STRING,        1 + pubkey_len, NULL, &hlen_bit,   0)) != HAL_OK)
    return err;

  const size_t algid_len = hlen_alg + alg_oid_len + hlen_curve + curve_oid_len;

  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE,          algid_len,     NULL, &hlen_algid, 0)) != HAL_OK)
    return err;

  const size_t vlen = hlen_algid + hlen_alg + alg_oid_len + hlen_curve + curve_oid_len + hlen_bit + 1 + pubkey_len;

  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE,          vlen,          NULL, &hlen_spki,  0)) != HAL_OK)
    return err;

  /*
   * Handle pubkey early, in case it was staged into our output buffer.
   */
  if (der != NULL && hlen_spki + vlen <= der_max)
    memmove(der + hlen_spki + vlen - pubkey_len, pubkey, pubkey_len);

  err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, der, &hlen, der_max);

  if (der_len != NULL)
    *der_len = hlen + vlen;

  if (der == NULL || err != HAL_OK)
    return err;

  uint8_t *d = der + hlen;
  memset(d, 0, vlen - pubkey_len);

  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE, algid_len, d, &hlen, der + der_max - d)) != HAL_OK)
    return err;
  d += hlen;

  if ((err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, alg_oid_len, d, &hlen, der + der_max - d)) != HAL_OK)
    return err;
  d += hlen;
  memcpy(d, alg_oid, alg_oid_len);
  d += alg_oid_len;

  if ((err = hal_asn1_encode_header(curve_oid_tag, curve_oid_len, d, &hlen, der + der_max - d)) != HAL_OK)
    return err;
  d += hlen;
  if (curve_oid != NULL)
    memcpy(d, curve_oid, curve_oid_len);
  d += curve_oid_len;

  if ((err = hal_asn1_encode_header(ASN1_BIT_STRING, 1 + pubkey_len, d, &hlen, der + der_max - d)) != HAL_OK)
    return err;
  d += hlen;
  *d++ = 0x00;

  d += pubkey_len;              /* pubkey handled early, above. */

  assert(d == der + hlen_spki + vlen);
  assert(d <= der + der_max);

  return HAL_OK;
}

/*
 * Parse tag and length of an ASN.1 object.  Tag must match value
 * specified by the caller.  On success, sets hlen and vlen to lengths
 * of header and value, respectively.
 */

hal_error_t hal_asn1_decode_header(const uint8_t tag,
				   const uint8_t * const der, size_t der_max,
				   size_t *hlen, size_t *vlen)
{
  assert(der != NULL && hlen != NULL && vlen != NULL);

  if (der_max < 2 || der[0] != tag)
    return HAL_ERROR_ASN1_PARSE_FAILED;

  if ((der[1] & 0x80) == 0) {
    *hlen = 2;
    *vlen = der[1];
  }

  else {
    *hlen = 2 + (der[1] & 0x7F);
    *vlen = 0;

    if (*hlen > der_max)
      return HAL_ERROR_ASN1_PARSE_FAILED;

    for (size_t i = 2; i < *hlen; i++)
      *vlen = (*vlen << 8) + der[i];
  }

  if (*hlen + *vlen > der_max)
    return HAL_ERROR_ASN1_PARSE_FAILED;

  return HAL_OK;
}

/*
 * Decode an ASN.1 INTEGER into a libtfm bignum.  Since we only
 * support (or need to support, or expect to see) unsigned integers,
 * we return failure if the sign bit is set in the ASN.1 INTEGER.
 */

hal_error_t hal_asn1_decode_integer(fp_int *bn,
				    const uint8_t * const der, size_t *der_len, const size_t der_max)
{
  if (bn == NULL || der == NULL)
    return HAL_ERROR_BAD_ARGUMENTS;

  hal_error_t err;
  size_t hlen, vlen;

  if ((err = hal_asn1_decode_header(ASN1_INTEGER, der, der_max, &hlen, &vlen)) != HAL_OK)
    return err;

  if (der_len != NULL)
    *der_len = hlen + vlen;

  if (vlen < 1 || (der[hlen] & 0x80) != 0x00)
    return HAL_ERROR_ASN1_PARSE_FAILED;

  fp_init(bn);
  fp_read_unsigned_bin(bn, (uint8_t *) der + hlen, vlen);
  return HAL_OK;
}

/*
 * Decode a public key from an RFC 5280 SubjectPublicKeyInfo.
 */

hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
                                 const uint8_t **curve_oid, size_t *curve_oid_len,
                                 const uint8_t **pubkey,    size_t *pubkey_len,
                                 const uint8_t *const der,  const size_t der_len)
{
  if (alg_oid == NULL || alg_oid_len == NULL || curve_oid == NULL || curve_oid_len == NULL ||
      pubkey == NULL || pubkey_len == NULL || der == NULL)
    return HAL_ERROR_BAD_ARGUMENTS;

  const uint8_t * const der_end = der + der_len;
  const uint8_t *d = der;

  size_t hlen, vlen;
  hal_error_t err;

  if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, d, der_end - d, &hlen, &vlen)) != HAL_OK)
    return err;
  d += hlen;

  if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, d, der_end - d, &hlen, &vlen)) != HAL_OK)
    return err;
  d += hlen;

  const uint8_t * const algid_end = d + vlen;

  if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
    return err;
  d += hlen;
  if (vlen > algid_end - d)
    return HAL_ERROR_ASN1_PARSE_FAILED;
  *alg_oid = d;
  *alg_oid_len = vlen;
  d += vlen;

  *curve_oid = NULL;
  *curve_oid_len = 0;

  if (d < algid_end) {
    switch (*d) {

    case ASN1_OBJECT_IDENTIFIER:
      if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
        return err;
      d += hlen;
      if (vlen > algid_end - d)
        return HAL_ERROR_ASN1_PARSE_FAILED;
      *curve_oid = d;
      *curve_oid_len = vlen;
      d += vlen;
      break;

    case ASN1_NULL:
      if ((err = hal_asn1_decode_header(ASN1_NULL, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
        return err;
      d += hlen;
      if (vlen == 0)
        break;

    default:
      return HAL_ERROR_ASN1_PARSE_FAILED;
    }
  }

  if (d != algid_end)
    return HAL_ERROR_ASN1_PARSE_FAILED;

  if ((err = hal_asn1_decode_header(ASN1_BIT_STRING, d, der_end - d, &hlen, &vlen)) != HAL_OK)
    return err;
  d += hlen;
  if (vlen >= algid_end - d || vlen == 0 || *d != 0x00)
    return HAL_ERROR_ASN1_PARSE_FAILED;
  *pubkey = ++d;
  *pubkey_len = --vlen;
  d += vlen;

  if (d != der_end)
    return HAL_ERROR_ASN1_PARSE_FAILED;

  return HAL_OK;
}

/*
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */
hal_pkey_slot_t *slot, const uint8_t * const der, const size_t der_len) { if (ks == NULL || ks->driver == NULL || ks->driver->store == NULL || slot == NULL || der == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->store(ks, slot, der, der_len); } static inline hal_error_t hal_ks_fetch(hal_ks_t *ks, hal_pkey_slot_t *slot, uint8_t *der, size_t *der_len, const size_t der_max) { if (ks == NULL || ks->driver == NULL || ks->driver->fetch == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->fetch(ks, slot, der, der_len, der_max); } static inline hal_error_t hal_ks_delete(hal_ks_t *ks, hal_pkey_slot_t *slot) { if (ks == NULL || ks->driver == NULL || ks->driver->delete == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->delete(ks, slot); } static inline hal_error_t hal_ks_list(hal_ks_t *ks, const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_info_t *result, unsigned *result_len, const unsigned result_max) { if (ks == NULL || ks->driver == NULL || ks->driver->list == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->list(ks, client, session, result, result_len, result_max); } static inline hal_error_t hal_ks_match(hal_ks_t *ks, const hal_client_handle_t client, const hal_session_handle_t session, const hal_key_type_t type, const hal_curve_name_t curve, const hal_key_flags_t flags, hal_rpc_pkey_attribute_t *attributes, const unsigned attributes_len, hal_uuid_t *result, unsigned *result_len, const unsigned result_max, const hal_uuid_t * const previous_uuid) { if (ks == NULL || ks->driver == NULL || ks->driver->match == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->match(ks, client, session, type, curve, flags, attributes, attributes_len, result, result_len, result_max, previous_uuid); } static inline hal_error_t hal_ks_set_attribute(hal_ks_t *ks, hal_pkey_slot_t *slot, const uint32_t type, const uint8_t * const value, const size_t value_len) { if (ks == NULL || ks->driver == NULL || ks->driver->set_attribute == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->set_attribute(ks, slot, type, value, value_len); } static inline hal_error_t hal_ks_get_attribute(hal_ks_t *ks, hal_pkey_slot_t *slot, const uint32_t type, uint8_t *value, size_t *value_len, const size_t value_max) { if (ks == NULL || ks->driver == NULL || ks->driver->get_attribute == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->get_attribute(ks, slot, type, value, value_len, value_max); } static inline hal_error_t hal_ks_delete_attribute(hal_ks_t *ks, hal_pkey_slot_t *slot, const uint32_t type) { if (ks == NULL || ks->driver == NULL || ks->driver->delete_attribute == NULL || slot == NULL) return HAL_ERROR_BAD_ARGUMENTS; return ks->driver->delete_attribute(ks, slot, type); } /* * Keystore index. This is intended to be usable by both memory-based * (in-memory, mmap(), ...) keystores and keystores based on raw flash. * Some of the features aren't really necessary for memory-based keystores, * but should be harmless. * * General approach is multiple arrays, all but one of which are * indexed by "block" numbers, where a block number might be a slot in * yet another static array, the number of a flash sub-sector, or * whatever is the appropriate unit for holding one keystore record. * * The index array contains nothing but flags and block numbers, and * is deliberately a small data structure so that moving data around * within it is relatively cheap. * * The index array is divided into two portions: the index proper, and * the free queue. The index proper is ordered according to the names * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to * support a simplistic form of wear leveling in flash-based keystores. * * Key names are kept in a separate array, indexed by block number. * Key names here are a composite of the key's UUID and a "chunk" * number; the latter allows storage of keys whose total size exceeds * one block (whatever a block is). For the moment we keep the UUID * and the chunk number in a single array, which may provide (very) * slightly better performance due to reference locality in SDRAM, but * this may change if we need to reclaim the space wasted by structure * size rounding. * * The all-zeros UUID, which (by definition) cannot be a valid key * UUID, is reserved for the (non-key) block used to stash PINs and * other small data which aren't really part of the keystore proper * but are kept with it because the keystore is the flash we have. * * Note that this API deliberately says nothing about how the keys * themselves are stored, that's up to the keystore driver. This * portion of the API is only concerned with allocation and naming. */ typedef struct { hal_uuid_t name; /* Key name */ uint8_t chunk; /* Key chunk number */ } hal_ks_name_t; typedef struct { unsigned size; /* Array length */ unsigned used; /* How many blocks are in use */ uint16_t *index; /* Index/freelist array */ hal_ks_name_t *names; /* Keyname array */ } hal_ks_index_t; /* * Finish setting up key index. Caller must populate index, free * list, and name array. * * This function checks a few things then sorts the index proper. * * If driver cares about wear leveling, driver must supply the free * list in the desired order (FIFO); figuring out what that order is a * problem for the keystore driver. */ extern hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi); /* * Find a key block, return its block number. */ extern hal_error_t hal_ks_index_find(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned chunk, unsigned *blockno, int *hint); /* * Find all the blocks in a key, return the block numbers. */ extern hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned max_blocks, unsigned *n_blocks, unsigned *blocknos, int *hint); /* * Add a key block, return its block number. */ extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned chunk, unsigned *blockno, int *hint); /* * Delete a key block, returns its block number (driver may need it). */ extern hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned chunk, unsigned *blockno, int *hint); /* * Delete all of blocks in a key, returning the block numbers. */ extern hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned max_blocks, unsigned *n_blocks, unsigned *blocknos, int *hint); /* * Replace a key block with a new one, return new block number. * Name of block does not change. This is an optimization of * a delete immediately followed by an add for the same name. */ extern hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi, const hal_uuid_t * const name, const unsigned chunk, unsigned *blockno, int *hint); /* * Check the index for errors. At least for the moment, this just * reports errors, it doesn't attempt to fix them. */ extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi); /* * Keystore attribute utilities, for use by keystore drivers. * * Basic model here is probably to replace the "der" block in a key * object with a byte array. We could use padding to get alignment, * but it's probably easier just to do this DNS style, pulling a * 16-bit length and 32-bit attribute type out of the byte array * directly. Well, maybe. I guess if we cast the uint8_t* to a * structure pointer we could use the structure to pull out the header * fields, but that has portability issues, particulary if the * compiler gets tetchy about type punning. * * Unclear whether we should treat the key DER specially. Might just * give it an attribute code of 0xFFFFFFFF and treat it same as * everything else, just always first for convenience. This assumes * that PKCS #11 will never use 0xFFFFFFFF, which is a bit risky, but * maybe the code just treats it a little bit specially and knows to * skip over the key DER when looking for attributes, etc. * * We probably don't want to let attributes span block boundaries. We * probably do want to attempt to fit a new attribute into the first * available space which can hold it. In theory, taken together, this * means we will only have to update multiple blocks when required to * add a new block (in which case the max_blocks count changes). Most * of this only applies to flash, for volatile we can use as much * memory as we like, although even there we might want smaller chunks * to avoid wasting huge tracts of space that don't end up being used. * But maybe that's just a configuration thing for the volatile * keystore(s). * * If we have to rewrite a block at all we might as well compact it, * so fragmentation in that sense is a non-issue. Might need to * collapse blocks when deletion has freed up enough space, but that * might be something we handle directly in ks_flash rather than in * the ks_attribute code. * * We need some way of figuring out how many attributes there are. * Options are a marker (like the IPv4 END-OF-OPTIONS option) or a * count in the header. Count is simpler and lets us pre-allocate * arrays so probably go with that. */ extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes, const size_t bytes_len, hal_rpc_pkey_attribute_t *attributes, const unsigned attributes_len, size_t *total_len); extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes, const size_t bytes_len, hal_rpc_pkey_attribute_t *attributes, unsigned *attributes_len, size_t *total_len, const uint32_t type); extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len, hal_rpc_pkey_attribute_t *attributes, unsigned *attributes_len, size_t *total_len, const uint32_t type, const uint8_t * const value, const size_t value_len); /* * RPC lowest-level send and receive routines. These are blocking, and * transport-specific (sockets, USB). */ extern hal_error_t hal_rpc_send(const uint8_t * const buf, const size_t len); extern hal_error_t hal_rpc_recv(uint8_t * const buf, size_t * const len); extern hal_error_t hal_rpc_sendto(const uint8_t * const buf, const size_t len, void *opaque); extern hal_error_t hal_rpc_recvfrom(uint8_t * const buf, size_t * const len, void **opaque); extern hal_error_t hal_rpc_client_transport_init(void); extern hal_error_t hal_rpc_client_transport_close(void); extern hal_error_t hal_rpc_server_transport_init(void); extern hal_error_t hal_rpc_server_transport_close(void); /* * RPC function numbers */ typedef enum { RPC_FUNC_GET_VERSION = 0, RPC_FUNC_GET_RANDOM, RPC_FUNC_SET_PIN, RPC_FUNC_LOGIN, RPC_FUNC_LOGOUT, RPC_FUNC_LOGOUT_ALL, RPC_FUNC_IS_LOGGED_IN, RPC_FUNC_HASH_GET_DIGEST_LEN, RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, RPC_FUNC_HASH_GET_ALGORITHM, RPC_FUNC_HASH_INITIALIZE, RPC_FUNC_HASH_UPDATE, RPC_FUNC_HASH_FINALIZE, RPC_FUNC_PKEY_LOAD, RPC_FUNC_PKEY_FIND, RPC_FUNC_PKEY_GENERATE_RSA, RPC_FUNC_PKEY_GENERATE_EC, RPC_FUNC_PKEY_CLOSE, RPC_FUNC_PKEY_DELETE, RPC_FUNC_PKEY_GET_KEY_TYPE, RPC_FUNC_PKEY_GET_KEY_FLAGS, RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN, RPC_FUNC_PKEY_GET_PUBLIC_KEY, RPC_FUNC_PKEY_SIGN, RPC_FUNC_PKEY_VERIFY, RPC_FUNC_PKEY_LIST, RPC_FUNC_PKEY_RENAME, RPC_FUNC_PKEY_MATCH, RPC_FUNC_PKEY_SET_ATTRIBUTE, RPC_FUNC_PKEY_GET_ATTRIBUTE, RPC_FUNC_PKEY_DELETE_ATTRIBUTE, RPC_FUNC_PKEY_GET_KEY_CURVE, } rpc_func_num_t; #define RPC_VERSION 0x01010000 /* 1.1.0.0 */ /* * RPC client locality. These have to be defines rather than an enum, * because they're handled by the preprocessor. */ #define RPC_CLIENT_LOCAL 0 #define RPC_CLIENT_REMOTE 1 #define RPC_CLIENT_MIXED 2 #define RPC_CLIENT_NONE 3 /* * Maximum size of a HAL RPC packet. */ #ifndef HAL_RPC_MAX_PKT_SIZE #define HAL_RPC_MAX_PKT_SIZE 4096 #endif /* * Location of AF_UNIX socket for RPC client mux daemon. */ #ifndef HAL_CLIENT_DAEMON_DEFAULT_SOCKET_NAME #define HAL_CLIENT_DAEMON_DEFAULT_SOCKET_NAME "/tmp/cryptech_rpcd.socket" #endif /* * Default device name and line speed for HAL RPC serial connection to HSM. */ #ifndef HAL_CLIENT_SERIAL_DEFAULT_DEVICE #define HAL_CLIENT_SERIAL_DEFAULT_DEVICE "/dev/ttyUSB0" #endif #ifndef HAL_CLIENT_SERIAL_DEFAULT_SPEED #define HAL_CLIENT_SERIAL_DEFAULT_SPEED 921600 #endif /* * Names of environment variables for setting the above in RPC clients. */ #define HAL_CLIENT_SERIAL_DEVICE_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_DEVICE" #define HAL_CLIENT_SERIAL_SPEED_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_SPEED" #endif /* _HAL_INTERNAL_H_ */ /* * Local variables: * indent-tabs-mode: nil * End: */