[Cryptech-Commits] [sw/libhal] 10/14: First cut at key backup code. Not tested yet.

git at cryptech.is git at cryptech.is
Thu Apr 6 23:38:09 UTC 2017


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch pkcs8
in repository sw/libhal.

commit fb4933fd6e42a661603b3249e3a0f95b003303b6
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Wed Apr 5 23:59:53 2017 -0400

    First cut at key backup code.  Not tested yet.
    
    Still missing Python script to drive backup process, and need to do
    something about setting the EXPORTABLE key flag for this to be useful.
---
 asn1.c          |  83 +++++++++++++---
 asn1_internal.h |   6 ++
 ecdsa.c         |  25 ++---
 hal.h           |  19 ++++
 hal_internal.h  |  28 ++++--
 rpc_api.c       |  24 +++++
 rpc_client.c    |  81 +++++++++++++++-
 rpc_pkey.c      | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
 8 files changed, 505 insertions(+), 53 deletions(-)

diff --git a/asn1.c b/asn1.c
index 1d7e628..3ca8fc8 100644
--- a/asn1.c
+++ b/asn1.c
@@ -52,7 +52,7 @@
 #include <assert.h>
 
 #include "hal.h"
-
+#include "hal_internal.h"
 #include "asn1_internal.h"
 
 #define INIT_FP_INT     {{{0}}}
@@ -67,6 +67,16 @@ const size_t  hal_asn1_oid_rsaEncryption_len = sizeof(hal_asn1_oid_rsaEncryption
 const uint8_t hal_asn1_oid_ecPublicKey[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 };
 const size_t  hal_asn1_oid_ecPublicKey_len = sizeof(hal_asn1_oid_ecPublicKey);
 
+#if KEK_LENGTH == (bitsToBytes(128))
+const uint8_t hal_asn1_oid_aesKeyWrap[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x08 };
+const size_t hal_asn1_oid_aesKeyWrap_len = sizeof(hal_asn1_oid_aesKeyWrap);
+#endif
+
+#if KEK_LENGTH == (bitsToBytes(256))
+const uint8_t hal_asn1_oid_aesKeyWrap[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x30 };
+const size_t hal_asn1_oid_aesKeyWrap_len = sizeof(hal_asn1_oid_aesKeyWrap);
+#endif
+
 /*
  * Encode tag and length fields of an ASN.1 object.
  *
@@ -482,8 +492,7 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_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)
+  if (der == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
 
   const uint8_t * const der_end = der + der_len;
@@ -510,12 +519,16 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
   d += hlen;
   if (vlen > algid_end - d)
     return HAL_ERROR_ASN1_PARSE_FAILED;
-  *alg_oid = d;
-  *alg_oid_len = vlen;
+  if (alg_oid != NULL)
+    *alg_oid = d;
+  if (alg_oid_len != NULL)
+    *alg_oid_len = vlen;
   d += vlen;
 
-  *curve_oid = NULL;
-  *curve_oid_len = 0;
+  if (curve_oid != NULL)
+    *curve_oid = NULL;
+  if (curve_oid_len != NULL)
+    *curve_oid_len = 0;
 
   if (d < algid_end) {
     switch (*d) {
@@ -526,8 +539,10 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
       d += hlen;
       if (vlen > algid_end - d)
         return HAL_ERROR_ASN1_PARSE_FAILED;
-      *curve_oid = d;
-      *curve_oid_len = vlen;
+      if (curve_oid != NULL)
+        *curve_oid = d;
+      if (curve_oid_len != NULL)
+        *curve_oid_len = vlen;
       d += vlen;
       break;
 
@@ -551,8 +566,11 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
   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 (pubkey != NULL)
+    *pubkey = d;
+  if (pubkey_len != NULL)
+    *pubkey_len = vlen;
   d += vlen;
 
   if (d != der_end)
@@ -721,6 +739,49 @@ hal_error_t hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(const uint8_t **alg_oi
 }
 
 /*
+ * Attempt to guess what kind of key we're looking at.
+ */
+
+hal_error_t hal_asn1_guess_key_type(hal_key_type_t *type,
+                                    hal_curve_name_t *curve,
+                                    const uint8_t *const der,  const size_t der_len)
+{
+  if (type == NULL || curve == NULL || der == NULL)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  const uint8_t *alg_oid, *curve_oid;
+  size_t alg_oid_len, curve_oid_len;
+  hal_error_t err;
+  int public = 0;
+
+  err = hal_asn1_decode_pkcs8_privatekeyinfo(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, NULL, 0, der, der_len);
+
+  if (err == HAL_ERROR_ASN1_PARSE_FAILED &&
+      (err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, NULL, 0, der, der_len)) == HAL_OK)
+    public = 1;
+
+  if (err != HAL_OK)
+    return err;
+
+  if (alg_oid_len == hal_asn1_oid_rsaEncryption_len && memcmp(alg_oid, hal_asn1_oid_rsaEncryption, alg_oid_len) == 0) {
+    *type = public ? HAL_KEY_TYPE_RSA_PUBLIC : HAL_KEY_TYPE_RSA_PRIVATE;
+    *curve = HAL_CURVE_NONE;
+    return HAL_OK;
+  }
+
+  if (alg_oid_len == hal_asn1_oid_ecPublicKey_len && memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) == 0) {
+    *type = public ? HAL_KEY_TYPE_EC_PUBLIC : HAL_KEY_TYPE_EC_PRIVATE;
+    if ((err = hal_ecdsa_oid_to_curve(curve, curve_oid, curve_oid_len)) != HAL_OK)
+      *curve = HAL_CURVE_NONE;
+    return err;
+  }
+
+  *type = HAL_KEY_TYPE_NONE;
+  *curve = HAL_CURVE_NONE;
+  return HAL_ERROR_UNSUPPORTED_KEY;
+}
+
+/*
  * Local variables:
  * indent-tabs-mode: nil
  * End:
diff --git a/asn1_internal.h b/asn1_internal.h
index c337d4b..fe2f293 100644
--- a/asn1_internal.h
+++ b/asn1_internal.h
@@ -99,6 +99,9 @@ extern const size_t  hal_asn1_oid_rsaEncryption_len;
 extern const uint8_t hal_asn1_oid_ecPublicKey[];
 extern const size_t  hal_asn1_oid_ecPublicKey_len;
 
+extern const uint8_t hal_asn1_oid_aesKeyWrap[];
+extern const size_t  hal_asn1_oid_aesKeyWrap_len;
+
 /*
  * Transcoding functions.
  */
@@ -145,6 +148,9 @@ extern hal_error_t hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(const uint8_t *
                                                                  const uint8_t **data,     size_t *data_len,
                                                                  const uint8_t *const der, const size_t der_len);
 
+extern hal_error_t hal_asn1_guess_key_type(hal_key_type_t *type, hal_curve_name_t *curve,
+                                           const uint8_t *const der,  const size_t der_len);
+
 #endif /* _HAL_ASN1_INTERNAL_H_ */
 
 /*
diff --git a/ecdsa.c b/ecdsa.c
index 8a31504..3fc1462 100644
--- a/ecdsa.c
+++ b/ecdsa.c
@@ -258,20 +258,21 @@ static const ecdsa_curve_t * const get_curve(const hal_curve_name_t curve)
   }
 }
 
-static inline const ecdsa_curve_t * oid_to_curve(hal_curve_name_t *curve_name,
-                                                 const uint8_t * const oid,
-                                                 const size_t oid_len)
+hal_error_t hal_ecdsa_oid_to_curve(hal_curve_name_t *curve_name,
+                                   const uint8_t * const oid,
+                                   const size_t oid_len)
 {
-  assert(curve_name != NULL && oid != NULL);
+  if (curve_name == NULL || oid == NULL)
+    return HAL_ERROR_BAD_ARGUMENTS;
 
-  const ecdsa_curve_t *curve = NULL;
   *curve_name = HAL_CURVE_NONE;
+  const ecdsa_curve_t *curve;
 
   while ((curve = get_curve(++*curve_name)) != NULL)
     if (oid_len == curve->oid_len && memcmp(oid, curve->oid, oid_len) == 0)
-      return curve;
+      return HAL_OK;
 
-  return NULL;
+  return HAL_ERROR_UNSUPPORTED_KEY;
 }
 
 /*
@@ -1395,7 +1396,7 @@ hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key_,
 
   if (alg_oid_len != hal_asn1_oid_ecPublicKey_len ||
       memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) != 0 ||
-      oid_to_curve(&key->curve, curve_oid, curve_oid_len) == NULL)
+      hal_ecdsa_oid_to_curve(&key->curve, curve_oid, curve_oid_len) != HAL_OK)
     return HAL_ERROR_ASN1_PARSE_FAILED;
 
   if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, privkey, privkey_len, &hlen, &vlen)) != HAL_OK)
@@ -1516,19 +1517,19 @@ hal_error_t hal_ecdsa_public_key_from_der(hal_ecdsa_key_t **key_,
 
   const uint8_t *alg_oid = NULL, *curve_oid = NULL, *pubkey = NULL;
   size_t         alg_oid_len,     curve_oid_len,     pubkey_len;
-  const ecdsa_curve_t *curve;
   hal_error_t err;
 
-  if ((err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, &pubkey, &pubkey_len,
+  if ((err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len,
+                                  &pubkey, &pubkey_len,
                                   der, der_len)) != HAL_OK)
     return err;
 
   if (alg_oid == NULL || curve_oid == NULL || pubkey == NULL ||
       alg_oid_len != hal_asn1_oid_ecPublicKey_len ||
       memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) != 0 ||
-      (curve = oid_to_curve(&key->curve, curve_oid, curve_oid_len)) == NULL ||
+      hal_ecdsa_oid_to_curve(&key->curve, curve_oid, curve_oid_len) != HAL_OK ||
       pubkey_len < 3 || (pubkey_len & 1) == 0 || pubkey[0] != 0x04 ||
-      pubkey_len / 2 != fp_unsigned_bin_size(unconst_fp_int(curve->q)))
+      pubkey_len / 2 != fp_unsigned_bin_size(unconst_fp_int(get_curve(key->curve)->q)))
     return HAL_ERROR_ASN1_PARSE_FAILED;
 
   const uint8_t * const Qx = pubkey + 1;
diff --git a/hal.h b/hal.h
index 29b4dab..38a8330 100644
--- a/hal.h
+++ b/hal.h
@@ -505,6 +505,10 @@ extern const size_t hal_ecdsa_key_t_size;
 
 extern void hal_ecdsa_set_debug(const int onoff);
 
+extern hal_error_t hal_ecdsa_oid_to_curve(hal_curve_name_t *curve,
+                                          const uint8_t * const oid,
+                                          const size_t oid_len);
+
 extern hal_error_t hal_ecdsa_key_load_private(hal_ecdsa_key_t **key,
                                               void *keybuf, const size_t keybuf_len,
                                               const hal_curve_name_t curve,
@@ -725,6 +729,7 @@ typedef uint32_t hal_key_flags_t;
 #define	HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT	(1 << 2)
 #define	HAL_KEY_FLAG_TOKEN                      (1 << 3)
 #define HAL_KEY_FLAG_PUBLIC                     (1 << 4)
+#define HAL_KEY_FLAG_EXPORTABLE                 (1 << 5)
 
 /*
  * hal_pkey_attribute_t.length would be size_t, except that we also
@@ -821,6 +826,20 @@ extern hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey,
                                                uint8_t *attributes_buffer,
                                                const size_t attributes_buffer_len);
 
+extern hal_error_t hal_rpc_pkey_export(const hal_pkey_handle_t pkey,
+                                       const hal_pkey_handle_t kekek,
+                                       uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max,
+                                       uint8_t *kek,   size_t *kek_len,   const size_t kek_max);
+
+extern hal_error_t hal_rpc_pkey_import(const hal_client_handle_t client,
+                                       const hal_session_handle_t session,
+                                       hal_pkey_handle_t *pkey,
+                                       hal_uuid_t *name,
+                                       const hal_pkey_handle_t kekek,
+                                       const uint8_t * const pkcs8, const size_t pkcs8_len,
+                                       const uint8_t * const kek,   const size_t kek_len,
+                                       const hal_key_flags_t flags);
+
 extern hal_error_t hal_rpc_client_init(void);
 
 extern hal_error_t hal_rpc_client_close(void);
diff --git a/hal_internal.h b/hal_internal.h
index 65f6ae7..8130801 100644
--- a/hal_internal.h
+++ b/hal_internal.h
@@ -269,6 +269,20 @@ typedef struct {
                                 uint8_t *attributes_buffer,
                                 const size_t attributes_buffer_len);
 
+  hal_error_t (*export)(const hal_pkey_handle_t pkey_handle,
+                        const hal_pkey_handle_t kekek_handle,
+                        uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max,
+                        uint8_t *kek, size_t *kek_len, const size_t kek_max);
+
+  hal_error_t (*import)(const hal_client_handle_t client,
+                        const hal_session_handle_t session,
+                        hal_pkey_handle_t *pkey,
+                        hal_uuid_t *name,
+                        const hal_pkey_handle_t kekek_handle,
+                        const uint8_t * const pkcs8, const size_t pkcs8_len,
+                        const uint8_t * const kek, const size_t kek_len,
+                        const hal_key_flags_t flags);
+
 } hal_rpc_pkey_dispatch_t;
 
 
@@ -459,11 +473,11 @@ struct hal_ks_driver {
 
   hal_error_t (*store)(hal_ks_t *ks,
                        hal_pkey_slot_t *slot,
-		       const uint8_t * const der,  const size_t der_len);
+                       const uint8_t * const der,  const size_t der_len);
 
   hal_error_t (*fetch)(hal_ks_t *ks,
                        hal_pkey_slot_t *slot,
-		       uint8_t *der, size_t *der_len, const size_t der_max);
+                       uint8_t *der, size_t *der_len, const size_t der_max);
 
   hal_error_t (*delete)(hal_ks_t *ks,
                         hal_pkey_slot_t *slot);
@@ -537,7 +551,7 @@ static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver)
 }
 
 static inline hal_error_t hal_ks_open(const hal_ks_driver_t * const driver,
-			       hal_ks_t **ks)
+                                      hal_ks_t **ks)
 {
   if (driver == NULL || ks == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -863,6 +877,8 @@ typedef enum {
     RPC_FUNC_PKEY_GET_KEY_CURVE,
     RPC_FUNC_PKEY_SET_ATTRIBUTES,
     RPC_FUNC_PKEY_GET_ATTRIBUTES,
+    RPC_FUNC_PKEY_EXPORT,
+    RPC_FUNC_PKEY_IMPORT,
 } rpc_func_num_t;
 
 #define RPC_VERSION 0x01010000          /* 1.1.0.0 */
@@ -898,7 +914,7 @@ typedef enum {
  */
 
 #ifndef HAL_CLIENT_SERIAL_DEFAULT_DEVICE
-#define HAL_CLIENT_SERIAL_DEFAULT_DEVICE 	"/dev/ttyUSB0"
+#define HAL_CLIENT_SERIAL_DEFAULT_DEVICE        "/dev/ttyUSB0"
 #endif
 
 #ifndef HAL_CLIENT_SERIAL_DEFAULT_SPEED
@@ -909,8 +925,8 @@ typedef enum {
  * 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"
+#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_ */
 
diff --git a/rpc_api.c b/rpc_api.c
index 6ffd7a0..099ffcd 100644
--- a/rpc_api.c
+++ b/rpc_api.c
@@ -381,6 +381,30 @@ hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey,
                                                attributes_buffer, attributes_buffer_len);
 }
 
+hal_error_t hal_rpc_pkey_export(const hal_pkey_handle_t pkey,
+                                const hal_pkey_handle_t kekek,
+                                uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max,
+                                uint8_t *kek,   size_t *kek_len,   const size_t kek_max)
+{
+  if (pkcs8 == NULL || pkcs8_len == NULL || kek == NULL || kek_len == NULL || kek_max <= KEK_LENGTH)
+    return HAL_ERROR_BAD_ARGUMENTS;
+  return hal_rpc_pkey_dispatch->export(pkey, kekek, pkcs8, pkcs8_len, pkcs8_max, kek, kek_len, kek_max);
+}
+
+hal_error_t hal_rpc_pkey_import(const hal_client_handle_t client,
+                                const hal_session_handle_t session,
+                                hal_pkey_handle_t *pkey,
+                                hal_uuid_t *name,
+                                const hal_pkey_handle_t kekek,
+                                const uint8_t * const pkcs8, const size_t pkcs8_len,
+                                const uint8_t * const kek,   const size_t kek_len,
+                                const hal_key_flags_t flags)
+{
+  if (pkey == NULL || name == NULL || pkcs8 == NULL || kek == NULL || kek_len <= 2)
+    return HAL_ERROR_BAD_ARGUMENTS;
+  return hal_rpc_pkey_dispatch->import(client, session, pkey, name, kekek, pkcs8, pkcs8_len, kek, kek_len, flags);
+}
+
 /*
  * Local variables:
  * indent-tabs-mode: nil
diff --git a/rpc_client.c b/rpc_client.c
index 4adf247..5729b6f 100644
--- a/rpc_client.c
+++ b/rpc_client.c
@@ -915,6 +915,78 @@ static hal_error_t pkey_remote_get_attributes(const hal_pkey_handle_t pkey,
   return rpc_ret;
 }
 
+static hal_error_t pkey_remote_export(const hal_pkey_handle_t pkey,
+                                      const hal_pkey_handle_t kekek,
+                                      uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max,
+                                      uint8_t *kek,   size_t *kek_len,   const size_t kek_max)
+{
+  uint8_t outbuf[nargs(6)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[nargs(5) + pad(pkcs8_max) + pad(kek_max)];
+  const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  hal_client_handle_t dummy_client = {0};
+  hal_error_t rpc_ret;
+
+  check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_EXPORT));
+  check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle));
+  check(hal_xdr_encode_int(&optr, olimit, pkey.handle));
+  check(hal_xdr_encode_int(&optr, olimit, kekek.handle));
+  check(hal_xdr_encode_int(&optr, olimit, pkcs8_max));
+  check(hal_xdr_encode_int(&optr, olimit, kek_max));
+  check(hal_rpc_send(outbuf, optr - outbuf));
+
+  check(read_matching_packet(RPC_FUNC_PKEY_EXPORT, inbuf, sizeof(inbuf), &iptr, &ilimit));
+
+  check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    uint32_t len;
+    len = pkcs8_max;
+    check(hal_xdr_decode_buffer(&iptr, ilimit, pkcs8, &len));
+    *pkcs8_len = (size_t) len;
+    len = kek_max;
+    check(hal_xdr_decode_buffer(&iptr, ilimit, kek, &len));
+    *kek_len = (size_t) len;
+  }
+  return rpc_ret;
+}
+
+static hal_error_t pkey_remote_import(const hal_client_handle_t client,
+                                      const hal_session_handle_t session,
+                                      hal_pkey_handle_t *pkey,
+                                      hal_uuid_t *name,
+                                      const hal_pkey_handle_t kekek,
+                                      const uint8_t * const pkcs8, const size_t pkcs8_len,
+                                      const uint8_t * const kek,   const size_t kek_len,
+                                      const hal_key_flags_t flags)
+{
+  uint8_t outbuf[nargs(7) + pad(pkcs8_len) + pad(kek_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[nargs(5) + pad(sizeof(name->uuid))];
+  const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  uint32_t name_len = sizeof(name->uuid);
+  hal_error_t rpc_ret;
+
+  check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_IMPORT));
+  check(hal_xdr_encode_int(&optr, olimit, client.handle));
+  check(hal_xdr_encode_int(&optr, olimit, session.handle));
+  check(hal_xdr_encode_int(&optr, olimit, kekek.handle));
+  check(hal_xdr_encode_buffer(&optr, olimit, pkcs8, pkcs8_len));
+  check(hal_xdr_encode_buffer(&optr, olimit, kek, kek_len));
+  check(hal_xdr_encode_int(&optr, olimit, flags));
+  check(hal_rpc_send(outbuf, optr - outbuf));
+
+  check(read_matching_packet(RPC_FUNC_PKEY_IMPORT, inbuf, sizeof(inbuf), &iptr, &ilimit));
+
+  check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
+
+  if (rpc_ret == HAL_OK) {
+    check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle));
+    check(hal_xdr_decode_buffer(&iptr, ilimit, name->uuid, &name_len));
+    if (name_len != sizeof(name->uuid))
+      return HAL_ERROR_KEY_NAME_TOO_LONG;
+  }
+
+  return rpc_ret;
+}
+
 #if RPC_CLIENT == RPC_CLIENT_MIXED
 
 /*
@@ -1043,7 +1115,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_remote_pkey_dispatch = {
   .verify                       = pkey_remote_verify,
   .match                        = pkey_remote_match,
   .set_attributes               = pkey_remote_set_attributes,
-  .get_attributes               = pkey_remote_get_attributes
+  .get_attributes               = pkey_remote_get_attributes,
+  .export                       = pkey_remote_export,
+  .import                       = pkey_remote_import
 };
 
 #if RPC_CLIENT == RPC_CLIENT_MIXED
@@ -1063,7 +1137,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = {
   .verify                       = pkey_mixed_verify,
   .match                        = pkey_remote_match,
   .set_attributes               = pkey_remote_set_attributes,
-  .get_attributes               = pkey_remote_get_attributes
+  .get_attributes               = pkey_remote_get_attributes,
+  .export                       = pkey_remote_export,
+  .import                       = pkey_remote_import
 };
 #endif /* RPC_CLIENT == RPC_CLIENT_MIXED */
 
@@ -1100,7 +1176,6 @@ hal_error_t hal_rpc_client_close(void)
 #endif
 }
 
-
 /*
  * Local variables:
  * indent-tabs-mode: nil
diff --git a/rpc_pkey.c b/rpc_pkey.c
index 48072ce..aff833b 100644
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@ -117,10 +117,10 @@ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle)
  *
  * That's almost the rule that PKCS #11 follows for so-called
  * "private" objects (CKA_PRIVATE = CK_TRUE), but PKCS #11 has a more
- * model which not only allows wider visibility to "public" objects
- * (CKA_PRIVATE = CK_FALSE) but also allows write access to "public
- * session" (CKA_PRIVATE = CK_FALSE, CKA_TOKEN = CK_FALSE) objects
- * regardless of login state.
+ * complex model which not only allows wider visibility to "public"
+ * objects (CKA_PRIVATE = CK_FALSE) but also allows write access to
+ * "public session" (CKA_PRIVATE = CK_FALSE, CKA_TOKEN = CK_FALSE)
+ * objects regardless of login state.
  *
  * PKCS #11 also has a concept of read-only sessions, which we don't
  * bother to implement at all on the HSM, since the PIN is required to
@@ -173,29 +173,55 @@ static inline hal_error_t check_writable(const hal_client_handle_t client,
 }
 
 /*
+ * PKCS #1.5 encryption requires non-zero random bytes, which is a bit
+ * messy if done in place, so make it a separate function for readability.
+ */
+
+static inline hal_error_t get_nonzero_random(uint8_t *buffer, size_t n)
+{
+  assert(buffer != NULL);
+
+  uint32_t word = 0;
+  hal_error_t err;
+
+  while (n > 0) {
+
+    while ((word &  0xFF) == 0)
+      if  ((word & ~0xFF) != 0)
+        word >>= 8;
+      else if ((err = hal_get_random(NULL, &word, sizeof(word))) != HAL_OK)
+        return err;
+
+    *buffer++ = word & 0xFF;
+    word >>= 8;
+    n--;
+  }
+
+  return HAL_OK;
+}
+
+/*
  * Pad an octet string with PKCS #1.5 padding for use with RSA.
  *
- * For the moment, this only handles type 01 encryption blocks, thus
- * is only suitable for use with signature and verification.  If and
- * when we add support for encryption and decryption, this function
- * should be extended to take an argument specifying the block type
- * and include support for generating type 02 encryption blocks.
- * Other than the block type code, the only difference is the padding
- * value: for type 01 it's constant (0xFF), for type 02 it should be
- * non-zero random bytes from the CSPRNG.
+ * This handles type 01 and type 02 encryption blocks.  The formats
+ * are identical, except that the padding string is constant 0xFF
+ * bytes for type 01 and non-zero random bytes for type 02.
  *
  * We use memmove() instead of memcpy() so that the caller can
  * construct the data to be padded in the same buffer.
  */
 
 static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len,
-                               uint8_t *block, const size_t block_len)
+                               uint8_t *block, const size_t block_len,
+                               const uint8_t type)
 {
-  assert(data != NULL && block != NULL);
+  assert(data != NULL && block != NULL && (type == 0x01 || type == 0x02));
+
+  hal_error_t err;
 
   /*
    * Congregation will now please turn to RFC 2313 8.1 as we
-   * construct a PKCS #1.5 type 01 encryption block.
+   * construct a PKCS #1.5 type 01 or type 02 encryption block.
    */
 
   if (data_len > block_len - 11)
@@ -204,10 +230,20 @@ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len
   memmove(block + block_len - data_len, data, data_len);
 
   block[0] = 0x00;
-  block[1] = 0x01;
+  block[1] = type;
+
+  switch (type) {
+
+  case 0x01:                    /* Signature */
+    memset(block + 2, 0xFF, block_len - 3 - data_len);
+    break;
+
+  case 0x02:                    /* Encryption */
+    if ((err = get_nonzero_random(block + 2, block_len - 3 - data_len)) != HAL_OK)
+      return err;
+    break;
 
-  /* This is where we'd use non-zero random bytes if constructing a type 02 block. */
-  memset(block + 2, 0xFF, block_len - 3 - data_len);
+  }
 
   block[block_len - data_len - 1] = 0x00;
 
@@ -231,6 +267,8 @@ static inline hal_error_t ks_open_from_flags(hal_ks_t **ks, const hal_key_flags_
  * return a key handle and the name.
  */
 
+#warning Convert hal_rpc_pkey_load() to use hal-asn1_guess_key_type()?
+
 static hal_error_t pkey_local_load(const hal_client_handle_t client,
                                    const hal_session_handle_t session,
                                    hal_pkey_handle_t *pkey,
@@ -699,7 +737,7 @@ static hal_error_t pkey_local_sign_rsa(uint8_t *keybuf, const size_t keybuf_len,
     input = signature;
   }
 
-  if ((err = pkcs1_5_pad(input, input_len, signature, *signature_len))                         != HAL_OK ||
+  if ((err = pkcs1_5_pad(input, input_len, signature, *signature_len, 0x01))                   != HAL_OK ||
       (err = hal_rsa_decrypt(NULL, key, signature, *signature_len, signature, *signature_len)) != HAL_OK)
     return err;
 
@@ -831,7 +869,7 @@ static hal_error_t pkey_local_verify_rsa(uint8_t *keybuf, const size_t keybuf_le
     input = expected;
   }
 
-  if ((err = pkcs1_5_pad(input, input_len, expected, sizeof(expected)))                        != HAL_OK ||
+  if ((err = pkcs1_5_pad(input, input_len, expected, sizeof(expected), 0x01))                  != HAL_OK ||
       (err = hal_rsa_encrypt(NULL, key, signature, signature_len, received, sizeof(received))) != HAL_OK)
     return err;
 
@@ -1023,6 +1061,216 @@ static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey,
   return err;
 }
 
+/*
+ * This is an RPC function, so the NULL pointer input convention for
+ * querying required buffer length isn't all that useful, but buffer
+ * lengths are predictable anyway:
+ *
+ *   Size of the pkcs8 buffer is a constant, determined by
+ *   oid_aes_aesKeyWrap_len, HAL_KS_WRAPPED_KEYSIZE, and some ASN.1
+ *   overhead;
+ *
+ *   Size of the kek buffer is the same as the length of the
+ *   modulus of the RSA public key indicated by wrap_handle.
+ *
+ * Except that we might want ASN.1 around the KEK, something like:
+ *
+ *   SEQUENCE {
+ *     keyEncryptionAlgorithm AlgorithmIdentifier { rsaEncryption },
+ *     encryptedKey OCTET STRING
+ *   }
+ *
+ * which would still be constant-length, just a bit more verbose.
+ *
+ * Oddly enough, this is exactly the syntax of PKCS #8
+ * EncryptedPrivateKeyInfo, which we already use for other purposes.
+ * Using it to wrap an AES key encrypted with an RSA key seems a bit
+ * odd, but it's a good fit and lets us reuse ASN.1 code.  Cool.
+ */
+
+static hal_error_t pkey_local_export(const hal_pkey_handle_t pkey_handle,
+                                     const hal_pkey_handle_t kekek_handle,
+                                     uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max,
+                                     uint8_t *kek,   size_t *kek_len,   const size_t kek_max)
+{
+  assert(pkcs8 != NULL && pkcs8_len != NULL && kek != NULL && kek_len != NULL && kek_max > KEK_LENGTH);
+
+  uint8_t rsabuf[hal_rsa_key_t_size];
+  hal_rsa_key_t *rsa = NULL;
+  hal_ks_t *ks = NULL;
+  hal_error_t err;
+  size_t len;
+
+  hal_pkey_slot_t * const pkey  = find_handle(pkey_handle);
+  hal_pkey_slot_t * const kekek = find_handle(kekek_handle);
+
+  if (pkey == NULL || kekek == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  if ((pkey->flags & HAL_KEY_FLAG_EXPORTABLE) == 0)
+    return HAL_ERROR_FORBIDDEN;
+
+  if (kekek->type != HAL_KEY_TYPE_RSA_PRIVATE && kekek->type != HAL_KEY_TYPE_RSA_PUBLIC)
+    return HAL_ERROR_UNSUPPORTED_KEY;
+
+  if (pkcs8_max < HAL_KS_WRAPPED_KEYSIZE)
+    return HAL_ERROR_RESULT_TOO_LONG;
+
+  if ((err = ks_open_from_flags(&ks, kekek->flags)) == HAL_OK &&
+      (err = hal_ks_fetch(ks, kekek, pkcs8, &len, pkcs8_max)) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+  if (err != HAL_OK)
+    goto fail;
+
+  switch (kekek->type) {
+  case HAL_KEY_TYPE_RSA_PRIVATE:
+    err = hal_rsa_private_key_from_der(&rsa, rsabuf, sizeof(rsabuf), pkcs8, len);
+    break;
+  case HAL_KEY_TYPE_RSA_PUBLIC:
+    err = hal_rsa_public_key_from_der(&rsa, rsabuf, sizeof(rsabuf), pkcs8, len);
+    break;
+  default:
+    err = HAL_ERROR_IMPOSSIBLE;
+  }
+  if (err != HAL_OK)
+    goto fail;
+
+  if ((err = hal_rsa_get_modulus(rsa, NULL, kek_len, 0)) != HAL_OK)
+    goto fail;
+
+  if (*kek_len > kek_max) {
+    err = HAL_ERROR_RESULT_TOO_LONG;
+    goto fail;
+  }
+
+  if ((err = ks_open_from_flags(&ks, pkey->flags)) == HAL_OK &&
+      (err = hal_ks_fetch(ks, pkey, pkcs8, &len, pkcs8_max)) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+
+  if (err != HAL_OK)
+    goto fail;
+
+  if ((err = hal_get_random(NULL, kek, KEK_LENGTH)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_aes_keywrap(NULL, kek, KEK_LENGTH, pkcs8, len, pkcs8, &len)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_asn1_encode_pkcs8_encryptedprivatekeyinfo(hal_asn1_oid_aesKeyWrap, hal_asn1_oid_aesKeyWrap_len,
+                                                           pkcs8, len, pkcs8, pkcs8_len, pkcs8_max)) != HAL_OK)
+    goto fail;
+
+  if ((err = pkcs1_5_pad(kek, KEK_LENGTH, kek, *kek_len, 0x02)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_rsa_encrypt(NULL, rsa, kek, *kek_len, kek, *kek_len)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_asn1_encode_pkcs8_encryptedprivatekeyinfo(hal_asn1_oid_rsaEncryption, hal_asn1_oid_rsaEncryption_len,
+                                                           kek, *kek_len, kek, *kek_len, kek_max)) != HAL_OK)
+    goto fail;
+
+  memset(rsabuf,  0, sizeof(rsabuf));
+  return HAL_OK;
+
+ fail:
+  memset(pkcs8,   0, pkcs8_max);
+  memset(kek, 0, kek_max);
+  memset(rsabuf,  0, sizeof(rsabuf));
+  *pkcs8_len = *kek_len = 0;
+  return err;
+}
+
+static hal_error_t pkey_local_import(const hal_client_handle_t client,
+                                     const hal_session_handle_t session,
+                                     hal_pkey_handle_t *pkey,
+                                     hal_uuid_t *name,
+                                     const hal_pkey_handle_t kekek_handle,
+                                     const uint8_t * const pkcs8, const size_t pkcs8_len,
+                                     const uint8_t * const kek_,  const size_t kek_len,
+                                     const hal_key_flags_t flags)
+{
+  assert(pkey != NULL && name != NULL && pkcs8 != NULL && kek_ != NULL && kek_len > 2);
+
+  uint8_t kek[KEK_LENGTH], rsabuf[hal_rsa_key_t_size], der[HAL_KS_WRAPPED_KEYSIZE], *d, *oid, *data;
+  size_t der_len, oid_len, data_len;
+  hal_rsa_key_t *rsa = NULL;
+  hal_curve_name_t curve;
+  hal_key_type_t type;
+  hal_ks_t *ks = NULL;
+  hal_error_t err;
+
+  hal_pkey_slot_t * const kekek = find_handle(kekek_handle);
+
+  if (kekek == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  if (kekek->type != HAL_KEY_TYPE_RSA_PRIVATE)
+    return HAL_ERROR_UNSUPPORTED_KEY;
+
+  if ((err = ks_open_from_flags(&ks, kekek->flags)) == HAL_OK &&
+      (err = hal_ks_fetch(ks, kekek, der, &der_len, sizeof(der))) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+  if (err != HAL_OK)
+    goto fail;
+
+  if ((err = hal_rsa_private_key_from_der(&rsa, rsabuf, sizeof(rsabuf), der, der_len)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(&oid, &oid_len, &data, &data_len, kek_, kek_len)) != HAL_OK)
+    goto fail;
+
+  if (oid_len != hal_asn1_oid_rsaEncryption_len ||
+      memcmp(oid, hal_asn1_oid_rsaEncryption, oid_len) != 0 ||
+      data_len > sizeof(der) ||
+      data_len < 2) {
+    err = HAL_ERROR_ASN1_PARSE_FAILED;
+    goto fail;
+  }
+
+  if ((err = hal_rsa_decrypt(NULL, rsa, data, data_len, der, data_len)) != HAL_OK)
+    goto fail;
+
+  d = memchr(der + 2, 0x00, data_len - 2);
+
+  if (der[0] != 0x00 || der[1] != 0x02 || d == NULL || der + data_len != d + 1 + KEK_LENGTH) {
+    err = HAL_ERROR_ASN1_PARSE_FAILED;
+    goto fail;
+  }
+
+  memcpy(kek, d + 1, sizeof(kek));
+
+  if ((err = hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(&oid, &oid_len, &data, &data_len, pkcs8, pkcs8_len)) != HAL_OK)
+    goto fail;
+
+  if (oid_len != hal_asn1_oid_aesKeyWrap_len ||
+      memcmp(oid, hal_asn1_oid_aesKeyWrap, oid_len) != 0 ||
+      data_len > sizeof(der)) {
+    err = HAL_ERROR_ASN1_PARSE_FAILED;
+    goto fail;
+  }
+
+  if ((err = hal_aes_keyunwrap(NULL, kek, sizeof(kek), data, data_len, der, &der_len)) != HAL_OK)
+    goto fail;
+
+  if ((err = hal_asn1_guess_key_type(&type, &curve, der, der_len)) != HAL_OK)
+    goto fail;
+
+  err = pkey_local_load(client, session, pkey, type, curve, name, der, der_len, flags);
+
+ fail:
+  memset(rsabuf, 0, sizeof(rsabuf));
+  memset(kek, 0, sizeof(kek));
+  memset(der, 0, sizeof(der));
+  return err;
+}
+
 const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = {
   .load                 = pkey_local_load,
   .open                 = pkey_local_open,
@@ -1039,7 +1287,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = {
   .verify               = pkey_local_verify,
   .match                = pkey_local_match,
   .set_attributes       = pkey_local_set_attributes,
-  .get_attributes       = pkey_local_get_attributes
+  .get_attributes       = pkey_local_get_attributes,
+  .export               = pkey_local_export,
+  .import               = pkey_local_import
 };
 
 /*



More information about the Commits mailing list