[Cryptech-Commits] [sw/libhal] 05/06: Add support for hashsig key export/import.

git at cryptech.is git at cryptech.is
Mon Apr 1 00:59:36 UTC 2019


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

paul at psgd.org pushed a commit to branch master
in repository sw/libhal.

commit 418b7689e1ef575d036047354cad6b22eea0736d
Author: Paul Selkirk <paul at psgd.org>
AuthorDate: Wed Mar 13 11:09:28 2019 -0400

    Add support for hashsig key export/import.
---
 hashsig.c           | 551 +++++++++++++++++++++++++++++++++-------------------
 hashsig.h           |  15 +-
 rpc_pkey.c          |  70 ++++---
 utils/Makefile      |   9 +-
 utils/pkey-export.c | 187 ++++++++++++++++++
 utils/pkey-import.c | 168 ++++++++++++++++
 6 files changed, 762 insertions(+), 238 deletions(-)

diff --git a/hashsig.c b/hashsig.c
index d311dd0..4c42caf 100644
--- a/hashsig.c
+++ b/hashsig.c
@@ -1,7 +1,7 @@
 /*
  * hashsig.c
  * ---------
- * Implementation of draft-mcgrew-hash-sigs-10.txt
+ * Implementation of draft-mcgrew-hash-sigs-15.txt
  *
  * Copyright (c) 2018, NORDUnet A/S All rights reserved.
  *
@@ -188,13 +188,13 @@ static inline hal_error_t hal_asn1_decode_bytestring32(bytestring32 *data, const
 
 typedef const struct lmots_parameter_set {
     hal_lmots_algorithm_t type;
-    size_t                  n, w,   p, ls;
+    size_t                     n,  w, p,   ls;
 } lmots_parameter_t;
 static lmots_parameter_t lmots_parameters[] = {
     { hal_lmots_sha256_n32_w1, 32, 1, 265, 7 },
     { hal_lmots_sha256_n32_w2, 32, 2, 133, 6 },
-    { hal_lmots_sha256_n32_w4, 32, 4,  67, 4 },
-    { hal_lmots_sha256_n32_w8, 32, 8,  34, 0 },
+    { hal_lmots_sha256_n32_w4, 32, 4, 67,  4 },
+    { hal_lmots_sha256_n32_w8, 32, 8, 34,  0 },
 };
 
 typedef struct lmots_key {
@@ -683,10 +683,10 @@ static hal_error_t lmots_private_key_from_der(lmots_key_t *key,
 
 typedef const struct lms_parameter_set {
     hal_lms_algorithm_t type;
-    size_t                 m,  h;
+    size_t                    m,  h;
 } lms_parameter_t;
 static lms_parameter_t lms_parameters[] = {
-    { hal_lms_sha256_n32_h5,  32,  5 },
+    { hal_lms_sha256_n32_h5,  32, 5  },
     { hal_lms_sha256_n32_h10, 32, 10 },
     { hal_lms_sha256_n32_h15, 32, 15 },
     { hal_lms_sha256_n32_h20, 32, 20 },
@@ -700,6 +700,7 @@ typedef struct lms_key {
     lmots_parameter_t *lmots;
     bytestring16 I;
     size_t q;			/* index of next lmots signing key */
+    size_t q_end;
     hal_uuid_t *lmots_keys;	/* private key components */
     bytestring32 *T;		/* public key components */
     bytestring32 T1;		/* copy of T[1] */
@@ -765,40 +766,33 @@ static hal_error_t lms_compute_T_intr(lms_key_t *key)
     return HAL_OK;
 }
 
-/* Given a key with most fields filled in, generate the lms private and
- * public key components.
- * Let the caller worry about storage.
- */
-static hal_error_t lms_generate(lms_key_t *key, bytestring32 *seed)
+static hal_error_t lms_generate_lmots(lms_key_t *key, size_t q, bytestring32 *seed)
 {
-    if (key == NULL || key->type != HAL_KEY_TYPE_HASHSIG_LMS || key->lms == NULL || key->lmots == NULL || key->lmots_keys == NULL || key->T == NULL)
-        return HAL_ERROR_BAD_ARGUMENTS;
-
-    check(hal_uuid_gen((hal_uuid_t *)&key->I));
-    key->q = 0;
-
     bytestring32 x[key->lmots->p];
     lmots_key_t lmots_key = {
         .type = HAL_KEY_TYPE_HASHSIG_LMOTS,
         .lmots = key->lmots,
+        .q = q,
         .x = x
     };
     memcpy(&lmots_key.I, &key->I, sizeof(key->I));
 
-    hal_pkey_slot_t slot = {
-        .type  = HAL_KEY_TYPE_HASHSIG_LMOTS,
-        .curve = HAL_CURVE_NONE,
-        .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | ((key->level == 0) ? HAL_KEY_FLAG_TOKEN: 0)
-    };
-    hal_ks_t *ks = (key->level == 0) ? hal_ks_token : hal_ks_volatile;
+    /* generate the lmots private and public key components */
+    check(lmots_generate(&lmots_key, seed));
 
-    /* private key - array of lmots key names */
-    for (size_t q = 0; q < (1U << key->lms->h); ++q) {
-        /* generate the lmots private and public key components */
-        lmots_key.q = q;
-        check(lmots_generate(&lmots_key, seed));
+    /* Note: we have to generate all the lmots keys, even if q > 0 or
+     * q_end < 2^h, because we need them to calculate T[].
+     * We just don't need to store the ones that are out of range.
+     */
 
+    if (q >= key->q && q < key->q_end) {
         /* store the lmots key */
+        hal_ks_t *ks = (key->level == 0) ? hal_ks_token : hal_ks_volatile;
+        hal_pkey_slot_t slot = {
+            .type  = HAL_KEY_TYPE_HASHSIG_LMOTS,
+            .curve = HAL_CURVE_NONE,
+            .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | ((key->level == 0) ? HAL_KEY_FLAG_TOKEN: 0)
+        };                    
         uint8_t der[lmots_private_key_to_der_len(&lmots_key)];
         size_t der_len;
         check(lmots_private_key_to_der(&lmots_key, der, &der_len, sizeof(der)));
@@ -810,10 +804,34 @@ static hal_error_t lms_generate(lms_key_t *key, bytestring32 *seed)
 
         /* record the lmots keystore name */
         memcpy(&key->lmots_keys[q], &slot.name, sizeof(slot.name));
+    }
+    else
+        memset(&x, 0, sizeof(x));
+
+    /* compute T[r] = H(I || u32str(r) || u16str(D_LEAF) || K) */
+    check(lms_compute_T_leaf(key, &lmots_key));
 
-        /* compute T[r] = H(I || u32str(r) || u16str(D_LEAF) || OTS_PUB_HASH[r-2^h]) */
-        check(lms_compute_T_leaf(key, &lmots_key));
+    return HAL_OK;
+}
 
+/* Given a key with most fields filled in, generate the lms private and
+ * public key components.
+ * Let the caller worry about storage.
+ */
+static hal_error_t lms_generate(lms_key_t *key, bytestring32 *seed)
+{
+    if (key == NULL || key->type != HAL_KEY_TYPE_HASHSIG_LMS ||
+        key->lms == NULL || key->lmots == NULL ||
+        key->lmots_keys == NULL || key->T == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    hal_uuid_t I_0 = {{0}};
+    if (hal_uuid_cmp((hal_uuid_t *)&key->I, &I_0) == 0)
+        check(hal_uuid_gen((hal_uuid_t *)&key->I));
+
+    /* private key - array of lmots key names */
+    for (size_t q = 0; q < (1U << key->lms->h); ++q) {
+        check(lms_generate_lmots(key, q, seed));
         hal_task_yield_maybe();
     }
 
@@ -838,14 +856,17 @@ static hal_error_t lms_generate(lms_key_t *key, bytestring32 *seed)
 
 static hal_error_t lms_delete(const lms_key_t * const key)
 {
-    hal_pkey_slot_t slot = {{0}};
     hal_ks_t *ks = (key->level == 0) ? hal_ks_token : hal_ks_volatile;
+    hal_pkey_slot_t slot = {{0}};
+    hal_uuid_t uuid_0 = {{0}};
 
     /* delete the lmots keys */
     for (size_t i = 0; i < (1U << key->lms->h); ++i) {
-        memcpy(&slot.name, &key->lmots_keys[i], sizeof(slot.name));
-        check(hal_ks_delete(ks, &slot));
-        hal_task_yield_maybe();
+        if (hal_uuid_cmp(&key->lmots_keys[i], &uuid_0) != 0) {
+            memcpy(&slot.name, &key->lmots_keys[i], sizeof(slot.name));
+            (void)hal_ks_delete(ks, &slot);
+            hal_task_yield_maybe();
+        }
     }
 
     /* delete the lms key */
@@ -863,7 +884,7 @@ static hal_error_t lms_sign(lms_key_t * const key,
     if (key == NULL || key->type != HAL_KEY_TYPE_HASHSIG_LMS || msg == NULL || sig == NULL)
         return HAL_ERROR_BAD_ARGUMENTS;
 
-    if (key->q >= (1U << key->lms->h))
+    if (key->q >= key->q_end)
         return HAL_ERROR_HASHSIG_KEY_EXHAUSTED;
 
     if (sig_max < lms_signature_len(key->lms, key->lmots))
@@ -876,8 +897,7 @@ static hal_error_t lms_sign(lms_key_t * const key,
     check(hal_xdr_encode_int(&sigptr, siglim, key->q));
 
     /* fetch and decode the lmots signing key from the keystore */
-    hal_pkey_slot_t slot;
-    memset(&slot, 0, sizeof(slot));
+    hal_pkey_slot_t slot = {0};
     memcpy(&slot.name, &key->lmots_keys[key->q], sizeof(slot.name));
 
     lmots_key_t lmots_key;
@@ -1121,7 +1141,7 @@ static hal_error_t lms_private_key_to_der(const lms_key_t * const key,
      * Calculate data length.
      */
 
-    // u32str(lms_type) || u32str(lmots_type) || I || q
+    // u32str(lms_type) || u32str(lmots_type) || I || q || q_end
 
     size_t len, vlen = 0, hlen;
 
@@ -1129,6 +1149,7 @@ static hal_error_t lms_private_key_to_der(const lms_key_t * const key,
     check(hal_asn1_encode_lmots_algorithm(key->lmots->type, NULL, &len, 0)); vlen += len;
     check(hal_asn1_encode_bytestring16(&key->I, NULL, &len, 0));             vlen += len;
     check(hal_asn1_encode_size_t(key->q, NULL, &len, 0));                    vlen += len;
+    check(hal_asn1_encode_size_t(key->q_end, NULL, &len, 0));                vlen += len;
 
     check(hal_asn1_encode_header(ASN1_SEQUENCE, vlen, NULL, &hlen, 0));
 
@@ -1151,6 +1172,7 @@ static hal_error_t lms_private_key_to_der(const lms_key_t * const key,
     check(hal_asn1_encode_lmots_algorithm(key->lmots->type, d, &len, vlen)); d += len; vlen -= len;
     check(hal_asn1_encode_bytestring16(&key->I, d, &len, vlen));             d += len; vlen -= len;
     check(hal_asn1_encode_size_t(key->q, d, &len, vlen));                    d += len; vlen -= len;
+    check(hal_asn1_encode_size_t(key->q_end, d, &len, vlen));                d += len; vlen -= len;
 
     return hal_asn1_encode_pkcs8_privatekeyinfo(hal_asn1_oid_mts_hashsig, hal_asn1_oid_mts_hashsig_len,
                                                 NULL, 0, der, d - der, der, der_len, der_max);
@@ -1168,6 +1190,8 @@ static hal_error_t lms_private_key_from_der(lms_key_t *key,
     if (key == NULL || der == NULL)
         return HAL_ERROR_BAD_ARGUMENTS;
 
+    memset(key, 0, sizeof(*key));
+
     key->type = HAL_KEY_TYPE_HASHSIG_LMS;
 
     size_t hlen, vlen, alg_oid_len, curve_oid_len, privkey_len;
@@ -1188,7 +1212,7 @@ static hal_error_t lms_private_key_from_der(lms_key_t *key,
     const uint8_t *d = privkey + hlen;
     size_t n;
 
-    // u32str(lms_type) || u32str(lmots_type) || I || q
+    // u32str(lms_type) || u32str(lmots_type) || I || q || q_end
 
     hal_lms_algorithm_t lms_type;
     check(hal_asn1_decode_lms_algorithm(&lms_type, d, &n, vlen));     d += n; vlen -= n;
@@ -1198,6 +1222,7 @@ static hal_error_t lms_private_key_from_der(lms_key_t *key,
     key->lmots = lmots_select_parameter_set(lmots_type);
     check(hal_asn1_decode_bytestring16(&key->I, d, &n, vlen));        d += n; vlen -= n;
     check(hal_asn1_decode_size_t(&key->q, d, &n, vlen));              d += n; vlen -= n;
+    check(hal_asn1_decode_size_t(&key->q_end, d, &n, vlen));          d += n; vlen -= n;
 
     if (d != privkey + privkey_len)
         return HAL_ERROR_ASN1_PARSE_FAILED;
@@ -1226,6 +1251,7 @@ struct hal_hashsig_key {
     lms_parameter_t *lms;
     lmots_parameter_t *lmots;
     bytestring16 I;
+    size_t q_start, q_end;
     bytestring32 T1;
     bytestring32 seed;
     lms_key_t *lms_keys;
@@ -1235,6 +1261,16 @@ const size_t hal_hashsig_key_t_size = sizeof(hss_key_t);
 
 static hss_key_t *hss_keys = NULL;
 
+static hss_key_t *hss_find(bytestring16 *I)
+{
+    for (hss_key_t *key = hss_keys; key != NULL; key = key->next) {
+        if (memcmp(&key->I, I, sizeof(*I)) == 0)
+            return key;
+    }
+
+    return NULL;
+}
+
 #if 0 /* currently unused */
 static inline size_t hss_public_key_len(lms_parameter_t * const lms)
 {
@@ -1286,25 +1322,18 @@ static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size)
     return ret;
 }
 
-static hal_error_t hss_alloc(hal_hashsig_key_t **key_,
-                             const size_t L,
-                             const hal_lms_algorithm_t lms_type,
-                             const hal_lmots_algorithm_t lmots_type)
+static hal_error_t hss_alloc(hal_hashsig_key_t **key_)
 {
-    if (key_ == NULL)
-        return HAL_ERROR_BAD_ARGUMENTS;
-
-    if (L == 0 || L > 8)
-        return HAL_ERROR_BAD_ARGUMENTS;
-
-    lms_parameter_t *lms = lms_select_parameter_set(lms_type);
-    if (lms == NULL)
+    if (key_ == NULL || *key_ == NULL ||
+        (*key_)->type != HAL_KEY_TYPE_HASHSIG_PRIVATE ||
+        (*key_)->L == 0 || (*key_)->L > 8 ||
+        (*key_)->lms == NULL || (*key_)->lmots == NULL)
         return HAL_ERROR_BAD_ARGUMENTS;
-    size_t h2 = (1 << lms->h);
 
-    lmots_parameter_t *lmots = lmots_select_parameter_set(lmots_type);
-    if (lmots == NULL)
-        return HAL_ERROR_BAD_ARGUMENTS;
+    size_t L = (*key_)->L;
+    lms_parameter_t *lms = (*key_)->lms;
+    lmots_parameter_t *lmots = (*key_)->lmots;
+    size_t h2 = (1U << lms->h);
 
     /* w=1 fails on the Alpha, because the key exceeds the keystore block
      * size. The XDR encoding of the key is going to differ from the DER
@@ -1339,21 +1368,19 @@ static hal_error_t hss_alloc(hal_hashsig_key_t **key_,
     memset(mem, 0, len);
 
     /* allocate the key that will stay in working memory */
-    hss_key_t *key = gnaw(&mem, &len, sizeof(hss_key_t));
+    hss_key_t *key = gnaw(&mem, &len, sizeof(*key));
+
+    /* initialize it from the transitory key */
+    memcpy(key, *key_, sizeof(*key));
     *key_ = key;
-    key->type = HAL_KEY_TYPE_HASHSIG_PRIVATE;
-    key->L = L;
-    key->lms = lms;
-    key->lmots = lmots;
 
-    /* add to the list of active keys */
+    /* add the in-memory key to the list of active keys */
     key->next = hss_keys;
     hss_keys = key;
 
     /* allocate the list of lms trees */
     key->lms_keys = gnaw(&mem, &len, L * sizeof(lms_key_t));
     for (size_t i = 0; i < L; ++i) {
-        /* XXX some of this is redundant to lms_private_key_from_der */
         lms_key_t * lms_key = &key->lms_keys[i];
         lms_key->type = HAL_KEY_TYPE_HASHSIG_LMS;
         lms_key->lms = lms;
@@ -1365,71 +1392,94 @@ static hal_error_t hss_alloc(hal_hashsig_key_t **key_,
         lms_key->signature_len = lms_sig_len;
         lms_key->pubkey = gnaw(&mem, &len, lms_pub_len);
         lms_key->pubkey_len = lms_pub_len;
+        lms_key->q_end = h2;
     }
 
     return HAL_OK;
 }
 
-/* called from pkey_local_generate_hashsig */
-hal_error_t hal_hashsig_key_gen(hal_core_t *core,
-                                hal_hashsig_key_t **key_,
-                                const size_t L,
-                                const hal_lms_algorithm_t lms_type,
-                                const hal_lmots_algorithm_t lmots_type,
-                                const hal_key_flags_t flags)
+static hal_error_t hss_generate(hss_key_t **key_, const hal_key_flags_t flags)
 {
-    /* hss_alloc does most of the checks */
-
-    if (restart_in_progress)
-        return HAL_ERROR_NOT_READY;
+    /* Hashsig keys can only be used for signing, so it makes sense to check
+     * that now, rather than waiting until the user tries to sign.
+     *
+     * Also, the top-level tree must be stored in the token (flash) keystore.
+     * I experimented with allowing keys to be stored in the volatile
+     * keystore, but that had some ugly consequences around the fact that
+     * volatile keys are automatically deleted when the user logs out. I'm
+     * also not sure there's a good use case for volatile hashsig keys.
+     */
+    if (!(flags & HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE) ||
+        !(flags & HAL_KEY_FLAG_TOKEN))
+        return HAL_ERROR_FORBIDDEN;
 
-    /* check flash keystore for space to store the root tree */
-    lms_parameter_t *lms = lms_select_parameter_set(lms_type);
-    if (lms == NULL)
+    if (key_ == NULL || *key_ == NULL || (*key_)->lms == NULL)
         return HAL_ERROR_BAD_ARGUMENTS;
+
+    /* hss_alloc does most of the sanity checks */
+
+    /* check flash keystore for space to store the root tree:
+     * 2^h lmots keys + 1 lms key + 1 hss key
+     */
     size_t available;
     check(hal_ks_available(hal_ks_token, &available));
-    if (available < (1U << lms->h) + 2)
+    if (available < (*key_)->q_end - (*key_)->q_start + 2)
         return HAL_ERROR_NO_KEY_INDEX_SLOTS;
 
-    check(hss_alloc(key_, L, lms_type, lmots_type));
+    check(hss_alloc(key_));
     hss_key_t *key = *key_;
+    hal_error_t err;
 
     /* generate the lms trees */
-    for (size_t i = 0; i < L; ++i) {
+    for (size_t i = 0; i < key->L; ++i) {
         lms_key_t * lms_key = &key->lms_keys[i];
+        bytestring32 *seed = NULL;
 
-        if (flags & HAL_KEY_FLAG_EXPORTABLE && i == 0) {
-            bytestring32 *seed = &key->seed;
-            check(hal_rpc_get_random(seed, sizeof(*seed)));
-            check(lms_generate(lms_key, seed));
-        }
-        else {
-            /* hss_alloc zeroes out the allocation, so we know seed == {0}
-             * for later use (store/restore)
+        if (i == 0) {
+            memcpy(&lms_key->I, &key->I, sizeof(key->I));
+            lms_key->q = key->q_start;
+            lms_key->q_end = key->q_end;
+
+            /* If we're called from import, seed will be filled in.
+             * If called from key_gen, seed will be 0, and we may need to
+             * generate it.
              */
-            check(lms_generate(lms_key, NULL));
+            bytestring32 seed_0 = {{0}};
+            if (memcmp(&key->seed, &seed_0, sizeof(seed_0)) != 0) {
+                seed = &key->seed;
+            }
+            else if (flags & HAL_KEY_FLAG_EXPORTABLE) {
+                seed = &key->seed;
+                if ((err = hal_rpc_get_random(seed, sizeof(*seed))) != HAL_OK)
+                    goto err_out;
+            }
         }
 
+        if ((err = lms_generate(lms_key, seed)) != HAL_OK)
+            goto err_out;
+
         if (i > 0)
             /* sign this tree with the previous */
-            check(lms_sign(&key->lms_keys[i-1],
-                           (const uint8_t * const)lms_key->pubkey, lms_public_key_len(key->lms),
-                           lms_key->signature, NULL, lms_signature_len(key->lms, key->lmots)));
+            if ((err = lms_sign(&key->lms_keys[i-1],
+                                (const uint8_t * const)lms_key->pubkey,
+                                lms_public_key_len(key->lms),
+                                lms_key->signature, NULL,
+                                lms_signature_len(key->lms, key->lmots))) != HAL_OK)
+                goto err_out;
 
         /* store the lms key */
+        hal_ks_t *ks = (i == 0) ? hal_ks_token : hal_ks_volatile;
         hal_pkey_slot_t slot = {
             .type  = HAL_KEY_TYPE_HASHSIG_LMS,
-            .curve = HAL_CURVE_NONE,
             .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | ((i == 0) ? HAL_KEY_FLAG_TOKEN: 0)
         };
-        hal_ks_t *ks = (i == 0) ? hal_ks_token : hal_ks_volatile;
         uint8_t der[lms_private_key_to_der_len(lms_key)];
         size_t der_len;
 
         memcpy(&slot.name, &lms_key->I, sizeof(slot.name));
-        check(lms_private_key_to_der(lms_key, der, &der_len, sizeof(der)));
-        check(hal_ks_store(ks, &slot, der, der_len));
+        if ((err = lms_private_key_to_der(lms_key, der, &der_len, sizeof(der))) != HAL_OK ||
+            (err = hal_ks_store(ks, &slot, der, der_len)) != HAL_OK)
+            goto err_out;
     }
 
     memcpy(&key->I, &key->lms_keys[0].I, sizeof(key->I));
@@ -1438,26 +1488,43 @@ hal_error_t hal_hashsig_key_gen(hal_core_t *core,
     /* pkey_local_generate_hashsig stores the key */
 
     return HAL_OK;
+
+err_out:
+    (void)hal_free_static_memory(key);
+    return err;
 }
 
-/* caller will delete the hss key from the keystore */
-hal_error_t hal_hashsig_key_delete(const hal_hashsig_key_t * const key)
-{
+/* called from pkey_local_generate_hashsig
+ * caller will store the key
+ */
+hal_error_t hal_hashsig_key_gen(hal_core_t *core,
+                                hal_hashsig_key_t **key_,
+                                void *keybuf, const size_t keybuf_len,
+                                const size_t L,
+                                const hal_lms_algorithm_t lms_type,
+                                const hal_lmots_algorithm_t lmots_type,
+                                const hal_key_flags_t flags)
+{ 
+   if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(hss_key_t))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
     if (restart_in_progress)
         return HAL_ERROR_NOT_READY;
 
-    if (key == NULL || key->type != HAL_KEY_TYPE_HASHSIG_PRIVATE)
-        return HAL_ERROR_BAD_ARGUMENTS;
-
-    /* delete the lms trees and their lmots keys */
-    for (size_t level = 0; level < key->L; ++level)
-        check(lms_delete(&key->lms_keys[level]));
+    hss_key_t *key = *key_ = keybuf;
+    memset(key, 0, sizeof(*key));
+    key->type = HAL_KEY_TYPE_HASHSIG_PRIVATE;
+    key->L = L;
+    key->lms = lms_select_parameter_set(lms_type);
+    key->lmots = lmots_select_parameter_set(lmots_type);
+    key->q_end = (1U << key->lms->h);
 
-    /* XXX free memory, if supported */
-    (void)hal_free_static_memory(key);
+    return hss_generate(key_, flags);
+}
 
-    /* remove from global hss_keys linked list */
-    /* XXX or mark it unused, for possible re-use */
+static void hss_delete(hss_key_t *key)
+{
+    /* remove key from global hss_keys linked list */
     if (hss_keys == key) {
         hss_keys = key->next;
     }
@@ -1470,6 +1537,41 @@ hal_error_t hal_hashsig_key_delete(const hal_hashsig_key_t * const key)
         }
     }
 
+    /* delete the lms trees and their lmots keys */
+    for (size_t level = 0; level < key->L; ++level)
+        (void)lms_delete(&key->lms_keys[level]);
+
+    /* free memory, if possible */
+    (void)hal_free_static_memory(key);
+}
+
+/* caller will delete the hss key from the keystore */
+hal_error_t hal_hashsig_delete(const hal_uuid_t * const name)
+{
+    if (restart_in_progress)
+        return HAL_ERROR_NOT_READY;
+
+    hal_pkey_slot_t slot = { .name = *name };
+    uint8_t der[HAL_KS_WRAPPED_KEYSIZE];
+    size_t der_len;
+    check(hal_ks_fetch(hal_ks_token, &slot, der, &der_len, sizeof(der)));
+
+    hal_hashsig_key_t keybuf, *key;
+    check(hal_hashsig_private_key_from_der(&key, &keybuf, sizeof(keybuf), der, der_len));
+
+    /* hal_hashsig_private_key_from_der returns the key in the list of
+     * active hashsig keys, so we don't need this temporary key.
+     */
+    memset(der, 0, sizeof(der));
+    memset(&keybuf, 0, sizeof(keybuf));
+
+    /* OTOH, if we found the key in the keystore, but not in the list of
+     * active hashsig keys, that's Bad.
+     */
+    if (key == &keybuf)
+        return HAL_ERROR_KEY_NOT_FOUND;
+
+    hss_delete(key);
     return HAL_OK;
 }
 
@@ -1501,10 +1603,9 @@ hal_error_t hal_hashsig_sign(hal_core_t *core,
 //      values, then the public key pub[i] is signed with prv[i-1], and
 //      sig[i-1] is set to the resulting value.
 
-    size_t h2 = (1 << key->lms->h);
-    if (key->lms_keys[key->L-1].q >= h2) {
+    if (key->lms_keys[key->L-1].q >= key->lms_keys[key->L-1].q_end) {
         size_t d;
-        for (d = key->L-1; d > 0 && key->lms_keys[d-1].q >= h2; --d) {
+        for (d = key->L-1; d > 0 && key->lms_keys[d-1].q >= key->lms_keys[d-1].q_end; --d) {
         }
         if (d == 0)
             return HAL_ERROR_HASHSIG_KEY_EXHAUSTED;
@@ -1517,6 +1618,7 @@ hal_error_t hal_hashsig_sign(hal_core_t *core,
              * any additional memory.
              */
             check(lms_delete(lms_key));
+            lms_key->q = 0;
             check(lms_generate(lms_key, NULL));
             check(lms_sign(&key->lms_keys[d-1],
                            (const uint8_t * const)lms_key->pubkey, lms_key->pubkey_len,
@@ -1525,9 +1627,9 @@ hal_error_t hal_hashsig_sign(hal_core_t *core,
             hal_pkey_slot_t slot = {
                 .type  = HAL_KEY_TYPE_HASHSIG_LMS,
                 .curve = HAL_CURVE_NONE,
-                .flags = (lms_key->level == 0) ? HAL_KEY_FLAG_TOKEN: 0
+                .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | (lms_key->level == 0) ? HAL_KEY_FLAG_TOKEN: 0
             };
-            hal_ks_t *ks = (lms_key->level == 0) ? hal_ks_token : hal_ks_volatile;
+            hal_ks_t *ks = hal_ks_volatile;
             uint8_t der[lms_private_key_to_der_len(lms_key)];
             size_t der_len;
 
@@ -1573,7 +1675,7 @@ hal_error_t hal_hashsig_verify(hal_core_t *core,
                                const uint8_t * const msg, const size_t msg_len,
                                const uint8_t * const sig, const size_t sig_len)
 {
-    if (key == NULL || key->type != HAL_KEY_TYPE_HASHSIG_PUBLIC || msg == NULL || sig == NULL)
+    if (key == NULL || (key->type != HAL_KEY_TYPE_HASHSIG_PRIVATE && key->type != HAL_KEY_TYPE_HASHSIG_PUBLIC) || msg == NULL || sig == NULL)
         return HAL_ERROR_BAD_ARGUMENTS;
 
 //   To verify a signature sig and message using the public key pub, the
@@ -1674,6 +1776,8 @@ hal_error_t hal_hashsig_private_key_to_der(const hal_hashsig_key_t * const key,
     check(hal_asn1_encode_bytestring16(&key->I, NULL, &len, 0));             vlen += len;
     check(hal_asn1_encode_bytestring32(&key->T1, NULL, &len, 0));            vlen += len;
     check(hal_asn1_encode_bytestring32(&key->seed, NULL, &len, 0));          vlen += len;
+    check(hal_asn1_encode_size_t(key->q_start, NULL, &len, 0));              vlen += len;
+    check(hal_asn1_encode_size_t(key->q_end, NULL, &len, 0));                vlen += len;
 
     check(hal_asn1_encode_header(ASN1_SEQUENCE, vlen, NULL, &hlen, 0));
 
@@ -1698,6 +1802,8 @@ hal_error_t hal_hashsig_private_key_to_der(const hal_hashsig_key_t * const key,
     check(hal_asn1_encode_bytestring16(&key->I, d, &len, vlen));             d += len; vlen -= len;
     check(hal_asn1_encode_bytestring32(&key->T1, d, &len, vlen));            d += len; vlen -= len;
     check(hal_asn1_encode_bytestring32(&key->seed, d, &len, vlen));          d += len; vlen -= len;
+    check(hal_asn1_encode_size_t(key->q_start, d, &len, vlen));              d += len; vlen -= len;
+    check(hal_asn1_encode_size_t(key->q_end, d, &len, vlen));                d += len; vlen -= len;
 
     return hal_asn1_encode_pkcs8_privatekeyinfo(hal_asn1_oid_mts_hashsig, hal_asn1_oid_mts_hashsig_len,
                                                 NULL, 0, der, d - der, der, der_len, der_max);
@@ -1753,6 +1859,8 @@ hal_error_t hal_hashsig_private_key_from_der(hal_hashsig_key_t **key_,
     check(hal_asn1_decode_bytestring16(&key->I, d, &n, vlen));        d += n; vlen -= n;
     check(hal_asn1_decode_bytestring32(&key->T1, d, &n, vlen));       d += n; vlen -= n;
     check(hal_asn1_decode_bytestring32(&key->seed, d, &n, vlen));     d += n; vlen -= n;
+    check(hal_asn1_decode_size_t(&key->q_start, d, &n, vlen));        d += n; vlen -= n;
+    check(hal_asn1_decode_size_t(&key->q_end, d, &n, vlen));          d += n; vlen -= n;
 
     if (d != privkey + privkey_len)
         return HAL_ERROR_ASN1_PARSE_FAILED;
@@ -1762,11 +1870,9 @@ hal_error_t hal_hashsig_private_key_from_der(hal_hashsig_key_t **key_,
      * structure. (The caller will wipe his own key structure when done,
      * and not molest ours.)
      */
-    for (hss_key_t *hss_key = hss_keys; hss_key != NULL; hss_key = hss_key->next) {
-        if (memcmp(&key->I, &hss_key->lms_keys[0].I, sizeof(key->I)) == 0) {
-            *key_ = hss_key;
-        }
-    }
+    hss_key_t *hss_key = hss_find(&key->I);
+    if (hss_key != NULL)
+        *key_ = hss_key;
 
     return HAL_OK;
 }
@@ -1998,6 +2104,9 @@ hal_error_t hal_hashsig_ks_init(void)
         if (hal_ks_fetch(hal_ks_token, &slot, der, &der_len, sizeof(der)) != HAL_OK ||
             hal_hashsig_private_key_from_der(&key, (void *)&keybuf, sizeof(keybuf), der, der_len) != HAL_OK) {
             (void)hal_ks_delete(hal_ks_token, &slot);
+            memset(der, 0, sizeof(der));
+            memset(&keybuf, 0, sizeof(keybuf));
+            key = NULL;
             continue;
         }
 
@@ -2011,24 +2120,29 @@ hal_error_t hal_hashsig_ks_init(void)
             lms_key.lms != key->lms ||
             lms_key.lmots != key->lmots ||
             memcmp(&lms_key.I, &key->I, sizeof(lms_key.I)) != 0 ||
+            /* check that key isn't exhausted */
+            lms_key.q >= lms_key.q_end ||
             /* optimistically allocate the full hss key structure */
-            hss_alloc(&key, key->L, key->lms->type, key->lmots->type) != HAL_OK) {
+            hss_alloc(&key) != HAL_OK) {
             (void)hal_ks_delete(hal_ks_token, &slot);
             (void)hal_ks_delete(hal_ks_token, &lms_slot);
+            memset(der, 0, sizeof(der));
+            memset(&lms_key, 0, sizeof(lms_key));
+            memset(&keybuf, 0, sizeof(keybuf));
+            key = NULL;
             continue;
         }
 
-        /* hss_alloc redefines key, so copy fields from the old version of the key */
-        memcpy(&key->I, &keybuf.I, sizeof(key->I));
-        memcpy(&key->T1, &keybuf.T1, sizeof(key->T1));
-        memcpy(&key->seed, &keybuf.seed, sizeof(key->seed));
-        key->name = slot.name;
-
         /* initialize top-level lms key (beyond what hss_alloc did) */
         memcpy(&key->lms_keys[0].I, &lms_key.I, sizeof(lms_key.I));
         key->lms_keys[0].q = lms_key.q;
+        key->lms_keys[0].q_end = key->q_end;
 
-        prev_name = slot.name;
+        prev_name = key->name = slot.name;
+        memset(der, 0, sizeof(der));
+        memset(&lms_key, 0, sizeof(lms_key));
+        memset(&keybuf, 0, sizeof(keybuf));
+        key = NULL;
         hal_task_yield_maybe();
     }
 
@@ -2037,12 +2151,7 @@ hal_error_t hal_hashsig_ks_init(void)
     while ((hal_ks_match(hal_ks_token, client, session,
                          HAL_KEY_TYPE_HASHSIG_LMS, HAL_CURVE_NONE, 0, 0, NULL, 0,
                          &slot.name, &len, 1, &prev_name) == HAL_OK) && (len > 0)) {
-        hss_key_t *hss_key;
-        for (hss_key = hss_keys; hss_key != NULL; hss_key = hss_key->next) {
-            if (memcmp(&slot.name, &hss_key->I, sizeof(slot.name)) == 0)
-                break;
-        }
-        if (hss_key == NULL) {
+        if (hss_find((bytestring16 *)&slot.name) == NULL) {
             (void)hal_ks_delete(hal_ks_token, &slot);
             continue;
         }
@@ -2066,17 +2175,15 @@ hal_error_t hal_hashsig_ks_init(void)
         if (hal_ks_fetch(hal_ks_token, &slot, der, &der_len, sizeof(der)) != HAL_OK ||
             lmots_private_key_from_der(&lmots_key, der, der_len) != HAL_OK) {
             (void)hal_ks_delete(hal_ks_token, &slot);
+            memset(&lmots_key, 0, sizeof(lmots_key));
             continue;
         }
 
-        hss_key_t *hss_key;
-        for (hss_key = hss_keys; hss_key != NULL; hss_key = hss_key->next) {
-            if (memcmp(&hss_key->I, &lmots_key.I, sizeof(lmots_key.I)) == 0)
-                break;
-        }
+        hss_key_t *hss_key = hss_find(&lmots_key.I);
         if (hss_key == NULL) {
             /* delete orphaned key */
             (void)hal_ks_delete(hal_ks_token, &slot);
+            memset(&lmots_key, 0, sizeof(lmots_key));
             continue;
         }
 
@@ -2084,9 +2191,14 @@ hal_error_t hal_hashsig_ks_init(void)
         memcpy(&hss_key->lms_keys[0].lmots_keys[lmots_key.q], &slot.name, sizeof(slot.name));
 
         /* compute T[r] = H(I || u32str(r) || u16str(D_LEAF) || K) */
-        check(lms_compute_T_leaf(&hss_key->lms_keys[0], &lmots_key));
+        if (lms_compute_T_leaf(&hss_key->lms_keys[0], &lmots_key) != HAL_OK) {
+            (void)hal_ks_delete(hal_ks_token, &slot);
+            memset(&lmots_key, 0, sizeof(lmots_key));
+            continue;
+        }
 
         prev_name = slot.name;
+        memset(&lmots_key, 0, sizeof(lmots_key));
         hal_task_yield_maybe();
     }
 
@@ -2096,93 +2208,42 @@ hal_error_t hal_hashsig_ks_init(void)
     for (hss_key = hss_keys; hss_key != NULL; hss_key = hss_next) {
         hss_next = hss_key->next;
         int fail = 0;
+        lms_key_t *lms_key = hss_key->lms_keys;
         for (size_t q = 0; q < (1U << hss_key->lms->h); ++q) {
-            if (hal_uuid_cmp(&hss_key->lms_keys[0].lmots_keys[q], &uuid_0) == 0) {
+            if (hal_uuid_cmp(&lms_key->lmots_keys[q], &uuid_0) == 0) {
                 bytestring32 seed_0 = {{0}};
                 if (memcmp(&hss_key->seed, &seed_0, sizeof(seed_0)) == 0) {
+                    /* lms key is incomplete, give up on it */
                     fail = 1;
                     break;
                 }
                 else {
-                    /* This key was generated with the pseudo-random
-                     * method, and can be regenerated.
+                    /* This key was generated with the pseudo-random method,
+                     * and can be regenerated.
                      */
-                    bytestring32 x[hss_key->lmots->p];
-                    lmots_key_t lmots_key = {
-                        .type = HAL_KEY_TYPE_HASHSIG_LMOTS,
-                        .lmots = hss_key->lmots,
-                        .q = q,
-                        .x = x
-                    };
-                    memcpy(&lmots_key.I, &hss_key->I, sizeof(hss_key->I));
-
-                    /* regenerate the lmots private and public key components */
-                    check(lmots_generate(&lmots_key, &hss_key->seed));
-
-                    /* store the lmots key */
-                    hal_pkey_slot_t slot = {
-                        .type  = HAL_KEY_TYPE_HASHSIG_LMOTS,
-                        .curve = HAL_CURVE_NONE,
-                        .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_TOKEN
-                    };                    
-                    uint8_t der[lmots_private_key_to_der_len(&lmots_key)];
-                    size_t der_len;
-                    check(lmots_private_key_to_der(&lmots_key, der, &der_len, sizeof(der)));
-                    check(hal_uuid_gen(&slot.name));
-                    hal_error_t err = hal_ks_store(hal_ks_token, &slot, der, der_len);
-                    memset(&x, 0, sizeof(x));
-                    memset(der, 0, sizeof(der));
-                    if (err != HAL_OK) return err;
-
-                    /* record the lmots keystore name */
-                    memcpy(&hss_key->lms_keys[0].lmots_keys[q], &slot.name, sizeof(slot.name));
-
-                    /* compute T[r] = H(I || u32str(r) || u16str(D_LEAF) || K) */
-                    check(lms_compute_T_leaf(&hss_key->lms_keys[0], &lmots_key));
+                    check(lms_generate_lmots(lms_key, q, &hss_key->seed));
+                    hal_task_yield_maybe();
                 }
             }
         }
         if (fail) {
         fail:
-            /* lms key is incomplete, give up on it */
-            /* delete lmots keys */
-            for (size_t i = 0; i < (1U << hss_key->lms->h); ++i) {
-                if (hal_uuid_cmp(&hss_key->lms_keys[0].lmots_keys[i], &uuid_0) != 0) {
-                    memcpy(&slot.name, &hss_key->lms_keys[0].lmots_keys[i], sizeof(slot.name));
-                    (void)hal_ks_delete(hal_ks_token, &slot);
-                }
-            }
-            /* delete lms key */
-            memcpy(&slot.name, &hss_key->I, sizeof(slot.name));
-            (void)hal_ks_delete(hal_ks_token, &slot);
             /* delete hss key */
+            hss_delete(hss_key);
             slot.name = hss_key->name;
             (void)hal_ks_delete(hal_ks_token, &slot);
-            /* remove the hss key from the key list */
-            if (hss_keys == hss_key) {
-                hss_keys = hss_key->next;
-            }
-            else {
-                for (hss_key_t *prev = hss_keys; prev != NULL; prev = prev->next) {
-                    if (prev->next == hss_key) {
-                        prev->next = hss_key->next;
-                        break;
-                    }
-                }
-            }
-            (void)hal_free_static_memory(hss_key);
             hal_task_yield_maybe();
             continue;
         }
 
         /* generate the rest of T[] */
-        lms_compute_T_intr(&hss_key->lms_keys[0]);
-        if (memcmp(&hss_key->lms_keys[0].T[1], &hss_key->T1, sizeof(hss_key->lms_keys[0].T[1])) != 0)
+        lms_compute_T_intr(lms_key);
+        if (memcmp(&lms_key->T[1], &hss_key->T1, sizeof(lms_key->T[1])) != 0)
             goto fail;
 
         /* generate the lower-level lms keys */
         for (size_t i = 1; i < hss_key->L; ++i) {
-            lms_key_t * lms_key = &hss_key->lms_keys[i];
+            lms_key = &hss_key->lms_keys[i];
             if (lms_generate(lms_key, NULL) != HAL_OK)
                 goto fail;
 
@@ -2204,4 +2265,92 @@ hal_error_t hal_hashsig_ks_init(void)
     restart_in_progress = 0;
     return HAL_OK;
 }
+
+hal_error_t hal_hashsig_export(const hal_uuid_t * const name, uint8_t *der, size_t *der_len, const size_t der_max)
+{
+    hal_error_t err;
+    hal_hashsig_key_t keybuf, *tmp_key = &keybuf, *hss_key;
+
+    if ((err = hal_hashsig_private_key_from_der(&hss_key, &keybuf, sizeof(keybuf), der, *der_len)) != HAL_OK)
+        goto err_out;
+    if (hss_key == tmp_key) {
+        err = HAL_ERROR_KEY_NOT_FOUND;         /* or IMPOSSIBLE? */
+        goto err_out;
+    }
+
+    /* adjust hss_key->end and tmp_key->start */
+    size_t new_end = (hss_key->lms_keys[0].q + hss_key->lms_keys[0].q_end) / 2;
+    if (new_end == hss_key->lms_keys[0].q) {
+        err = HAL_ERROR_HASHSIG_KEY_EXHAUSTED;
+        goto err_out;
+    }
+    hss_key->q_end = hss_key->lms_keys[0].q_end = tmp_key->q_start = new_end;
+
+    /* store updated hss_key */
+    hal_pkey_slot_t slot = {
+        .type = HAL_KEY_TYPE_HASHSIG_PRIVATE,
+        .name = *name,
+        .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_TOKEN | HAL_KEY_FLAG_EXPORTABLE
+    };
+    if ((err = hal_hashsig_private_key_to_der(hss_key, der, der_len, der_max)) != HAL_OK ||
+        (err = hal_ks_rewrite_der(hal_ks_token, &slot, der, *der_len)) != HAL_OK)
+        goto err_out;
+
+    /* store updated lms_key */
+    lms_key_t *lms_key = &hss_key->lms_keys[0];
+    uint8_t lms_der[HAL_KS_WRAPPED_KEYSIZE];
+    size_t lms_der_len;
+    if ((err = lms_private_key_to_der(lms_key, lms_der, &lms_der_len, sizeof(lms_der))) != HAL_OK)
+        goto err_out;
+
+    hal_pkey_slot_t lms_slot = {
+        .type = HAL_KEY_TYPE_HASHSIG_LMS,
+        .flags = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_TOKEN
+    };
+    memcpy(&lms_slot.name, &lms_key->I, sizeof(lms_slot.name));
+    if ((err = hal_ks_rewrite_der(hal_ks_token, &lms_slot, lms_der, lms_der_len)) != HAL_OK)
+        goto err_out;
+
+    /* re-encode tmp_key to der */
+    if ((err = hal_hashsig_private_key_to_der(tmp_key, der, der_len, der_max)) != HAL_OK)
+        goto err_out;
+
+    /* delete unused lmots keys? */
+
+err_out:
+    memset(&keybuf, 0, sizeof(keybuf));
+    hss_key = NULL;
+    return err;
+}
+
+hal_error_t hal_hashsig_import(const uint8_t *der, const size_t der_len,
+                               const hal_key_flags_t flags)
+{
+    if (restart_in_progress)
+        return HAL_ERROR_NOT_READY;
+
+    hss_key_t keybuf, *key;
+    hal_error_t err;
+
+    if ((err = hal_hashsig_private_key_from_der(&key, &keybuf, sizeof(keybuf), der, der_len)) != HAL_OK)
+        goto err_out;
+
+    /* If the key already exists, it could be that the user is attempting to
+     * return an exported key to its origin, and we could consolidate them,
+     * but then we have to deal with the possibility of disjoint partitions of
+     * the keyspace (or worse, overlapping or duplicate partitions, which is
+     * always an error). In any case, it's easier just to disallow it.
+     */
+    if (hss_find(&key->I) != NULL) {
+        err = HAL_ERROR_KEY_NAME_IN_USE;
+        goto err_out;
+    }
+
+    err = hss_generate(&key, flags);
+
+err_out:
+    memset(&keybuf, 0, sizeof(keybuf));
+    key = NULL;
+    return err;
+}
 #endif
diff --git a/hashsig.h b/hashsig.h
index 505d0f3..d335d7c 100644
--- a/hashsig.h
+++ b/hashsig.h
@@ -1,7 +1,7 @@
 /*
  * hashsig.h
  * ---------
- * Implementation of draft-mcgrew-hash-sigs-08.txt
+ * Implementation of draft-mcgrew-hash-sigs-15.txt
  *
  * Copyright (c) 2018, NORDUnet A/S All rights reserved.
  *
@@ -41,12 +41,13 @@ extern const size_t hal_hashsig_key_t_size;
 
 extern hal_error_t hal_hashsig_key_gen(hal_core_t *core,
                                        hal_hashsig_key_t **key_,
+                                       void *keybuf, const size_t keybuf_len,
                                        const size_t hss_levels,
                                        const hal_lms_algorithm_t lms_type,
                                        const hal_lmots_algorithm_t lmots_type,
                                        const hal_key_flags_t flags);
 
-extern hal_error_t hal_hashsig_key_delete(const hal_hashsig_key_t * const key);
+extern hal_error_t hal_hashsig_delete(const hal_uuid_t * const name);
 
 extern hal_error_t hal_hashsig_private_key_to_der(const hal_hashsig_key_t * const key,
                                                   uint8_t *der, size_t *der_len, const size_t der_max);
@@ -69,12 +70,12 @@ extern hal_error_t hal_hashsig_public_key_from_der(hal_hashsig_key_t **key,
 extern hal_error_t hal_hashsig_sign(hal_core_t *core,
                                     const hal_hashsig_key_t * const key,
                                     const uint8_t * const hash, const size_t hash_len,
-                                    uint8_t *signature, size_t *signature_len, const size_t signature_max);
+                                    uint8_t *sig, size_t *sig_len, const size_t sig_max);
 
 extern hal_error_t hal_hashsig_verify(hal_core_t *core,
                                       const hal_hashsig_key_t * const key,
                                       const uint8_t * const hash, const size_t hash_len,
-                                      const uint8_t * const signature, const size_t signature_len);
+                                      const uint8_t * const sig, const size_t sig_len);
 
 extern hal_error_t hal_hashsig_key_load_public(hal_hashsig_key_t **key_,
                                                void *keybuf, const size_t keybuf_len,
@@ -99,4 +100,10 @@ extern hal_error_t hal_hashsig_public_key_der_to_xdr(const uint8_t * const der,
 
 extern hal_error_t hal_hashsig_ks_init(void);
 
+extern hal_error_t hal_hashsig_export(const hal_uuid_t * const name,
+                                      uint8_t *der, size_t *der_len, const size_t der_max);
+
+extern hal_error_t hal_hashsig_import(const uint8_t *der, const size_t der_len,
+                                      const hal_key_flags_t flags);
+
 #endif /* _HAL_HASHSIG_H_ */
diff --git a/rpc_pkey.c b/rpc_pkey.c
index 138dd73..13c1d74 100644
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@ -536,6 +536,7 @@ static hal_error_t pkey_local_generate_hashsig(const hal_client_handle_t client,
 {
   hal_assert(pkey != NULL && name != NULL);
 
+  uint8_t keybuf[hal_hashsig_key_t_size];
   hal_hashsig_key_t *key = NULL;
   hal_pkey_slot_t *slot;
   hal_error_t err;
@@ -555,7 +556,7 @@ static hal_error_t pkey_local_generate_hashsig(const hal_client_handle_t client,
   slot->curve   = HAL_CURVE_NONE;
   slot->flags   = flags;
 
-  if ((err = hal_hashsig_key_gen(NULL, &key, hss_levels, lms_type, lmots_type, flags)) != HAL_OK) {
+  if ((err = hal_hashsig_key_gen(NULL, &key, keybuf, sizeof(keybuf), hss_levels, lms_type, lmots_type, flags)) != HAL_OK) {
     slot->type = HAL_KEY_TYPE_NONE;
     return err;
   }
@@ -566,11 +567,8 @@ static hal_error_t pkey_local_generate_hashsig(const hal_client_handle_t client,
   if ((err = hal_hashsig_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK)
     err = hal_ks_store(ks_from_flags(flags), slot, der, der_len);
 
-  /* There's nothing sensitive in the top-level private key, but we wipe
-   * the der anyway, for symmetry with other key types. The actual key buf
-   * is allocated internally and stays in memory, because everything else
-   * is linked off of it.
-   */
+  key = NULL;
+  memset(keybuf, 0, sizeof(keybuf));
   memset(der, 0, sizeof(der));
 
   if (err != HAL_OK) {
@@ -602,8 +600,6 @@ static hal_error_t pkey_local_close(const hal_pkey_handle_t pkey)
 /*
  * Delete a key from the store, given its key handle.
  */
-static hal_error_t pkey_local_get_key_type(const hal_pkey_handle_t pkey, hal_key_type_t *type);
-
 static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey)
 {
   hal_pkey_slot_t *slot = find_handle(pkey);
@@ -616,20 +612,9 @@ static hal_error_t pkey_local_delete(const hal_pkey_handle_t pkey)
   if ((err = check_writable(slot->client, slot->flags)) != HAL_OK)
     return err;
 
-  hal_key_type_t key_type;
-  if ((err = pkey_local_get_key_type(pkey, &key_type)) != HAL_OK)
-      return err;
-
-  if (key_type == HAL_KEY_TYPE_HASHSIG_PRIVATE) {
-    hal_hashsig_key_t *key;
-    uint8_t keybuf[hal_hashsig_key_t_size];
-    uint8_t der[HAL_KS_WRAPPED_KEYSIZE];
-    size_t der_len;
-    if ((err = ks_fetch_from_flags(slot, der, &der_len, sizeof(der))) != HAL_OK ||
-        (err = hal_hashsig_private_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)) != HAL_OK ||
-        (err = hal_hashsig_key_delete(key)) != HAL_OK)
-      return err;
-  }
+  if (slot->type == HAL_KEY_TYPE_HASHSIG_PRIVATE &&
+      (err = hal_hashsig_delete(&slot->name)) != HAL_OK)
+    return err;
 
   err = hal_ks_delete(ks_from_flags(slot->flags), slot);
 
@@ -745,8 +730,10 @@ static size_t pkey_local_get_public_key_len(const hal_pkey_handle_t pkey)
       break;
 
     case HAL_KEY_TYPE_HASHSIG_PRIVATE:
-      if (hal_hashsig_private_key_from_der(&hashsig_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK)
+      if (hal_hashsig_private_key_from_der(&hashsig_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK) {
         result = hal_hashsig_public_key_to_der_len(hashsig_key);
+        hashsig_key = NULL;
+      }
       break;
 
     default:
@@ -807,8 +794,10 @@ static hal_error_t pkey_local_get_public_key(const hal_pkey_handle_t pkey,
       break;
 
     case HAL_KEY_TYPE_HASHSIG_PRIVATE:
-      if ((err = hal_hashsig_private_key_from_der(&hashsig_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK)
+      if ((err = hal_hashsig_private_key_from_der(&hashsig_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK) {
         err = hal_hashsig_public_key_to_der(hashsig_key, der, der_len, der_max);
+        hashsig_key = NULL;
+      }
       break;
 
     default:
@@ -943,10 +932,10 @@ static hal_error_t pkey_local_sign_hashsig(hal_pkey_slot_t *slot,
     input = signature;
   }
 
-  if ((err = hal_hashsig_sign(NULL, key, input, input_len, signature, signature_len, signature_max)) != HAL_OK)
-    return err;
+  err = hal_hashsig_sign(NULL, key, input, input_len, signature, signature_len, signature_max);
+  key = NULL;
 
-  return HAL_OK;
+  return err;
 }
 
 static hal_error_t pkey_local_sign(const hal_pkey_handle_t pkey,
@@ -1113,13 +1102,23 @@ static hal_error_t pkey_local_verify_hashsig(uint8_t *keybuf, const size_t keybu
   hal_assert(signature != NULL && signature_len > 0);
   hal_assert((hash.handle == HAL_HANDLE_NONE) != (input == NULL || input_len == 0));
 
-  if ((err = hal_hashsig_public_key_from_der(&key, keybuf, keybuf_len, der, der_len)) != HAL_OK)
+  switch (type) {
+  case HAL_KEY_TYPE_HASHSIG_PRIVATE:
+    err = hal_hashsig_private_key_from_der(&key, keybuf, keybuf_len, der, der_len);
+    break;
+  case HAL_KEY_TYPE_HASHSIG_PUBLIC:
+    err = hal_hashsig_public_key_from_der(&key, keybuf, keybuf_len, der, der_len);
+    break;
+  default:
+    err = HAL_ERROR_IMPOSSIBLE;
+  }
+
+  if (err != HAL_OK)
     return err;
 
   if (input == NULL || input_len == 0) {
     hal_digest_algorithm_t alg;
 
-    // ???
     if ((err = hal_rpc_hash_get_algorithm(hash, &alg))              != HAL_OK ||
         (err = hal_rpc_hash_get_digest_length(alg, &input_len))     != HAL_OK ||
         (err = hal_rpc_hash_finalize(hash, digest, sizeof(digest))) != HAL_OK)
@@ -1162,6 +1161,7 @@ static hal_error_t pkey_local_verify(const hal_pkey_handle_t pkey,
     verifier = pkey_local_verify_ecdsa;
     keybuf_size = hal_ecdsa_key_t_size;
     break;
+  case HAL_KEY_TYPE_HASHSIG_PRIVATE:
   case HAL_KEY_TYPE_HASHSIG_PUBLIC:
     verifier = pkey_local_verify_hashsig;
     keybuf_size = hal_hashsig_key_t_size;
@@ -1374,6 +1374,11 @@ static hal_error_t pkey_local_export(const hal_pkey_handle_t pkey_handle,
   if ((err = ks_fetch_from_flags(pkey, pkcs8, &len, pkcs8_max)) != HAL_OK)
     goto fail;
 
+  /* hashsig export partitions the keyspace, so needs to update the stored key */
+  if (pkey->type == HAL_KEY_TYPE_HASHSIG_PRIVATE &&
+      (err = hal_hashsig_export(&pkey->name, pkcs8, &len, pkcs8_max)) != HAL_OK)
+    goto fail;
+
   if ((err = hal_get_random(NULL, kek, KEK_LENGTH)) != HAL_OK)
     goto fail;
 
@@ -1483,6 +1488,13 @@ static hal_error_t pkey_local_import(const hal_client_handle_t client,
   if ((err = hal_aes_keyunwrap(NULL, kek, sizeof(kek), data, data_len, der, &der_len)) != HAL_OK)
     goto fail;
 
+  hal_key_type_t type;
+  hal_curve_name_t curve;
+  if ((err = hal_asn1_guess_key_type(&type, &curve, der, der_len)) == HAL_OK &&
+      type == HAL_KEY_TYPE_HASHSIG_PRIVATE &&
+      (err = hal_hashsig_import(der, der_len, flags)) != HAL_OK)
+    goto fail;
+
   err = hal_rpc_pkey_load(client, session, pkey, name, der, der_len, flags);
 
  fail:
diff --git a/utils/Makefile b/utils/Makefile
index c9899f6..e88cb87 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -29,14 +29,15 @@
 
 LIBHAL_SRC	?= ..
 LIBHAL_BLD	?= ${LIBHAL_SRC}
+LIBTFM_BLD	?= ../../thirdparty/libtfm
 
-LIBS		= ${LIBHAL_BLD}/libhal.a
+LIBS		= ${LIBHAL_BLD}/libhal.a ${LIBTFM_BLD}/libtfm.a
 
-CFLAGS		?= -g3 -Wall -fPIC -std=c99 -I${LIBHAL_SRC}
+CFLAGS		?= -g3 -Wall -fPIC -std=c99 -I${LIBHAL_SRC} -I${LIBTFM_BLD}
 
-BIN		= eim_peek_poke cores
+BIN		= $(if $(wildcard ${LIBHAL_BLD}/hal_io_eim.o),eim_peek_poke) $(if $(wildcard ${LIBHAL_BLD}/core.o),cores) pkey-export pkey-import
 
-all: $(if $(wildcard ${LIBHAL_BLD}/hal_io_eim.o),eim_peek_poke) $(if $(wildcard ${LIBHAL_BLD}/core.o),cores)
+all: ${BIN}
 
 clean:
 	rm -f *.o ${BIN}
diff --git a/utils/pkey-export.c b/utils/pkey-export.c
new file mode 100644
index 0000000..660052e
--- /dev/null
+++ b/utils/pkey-export.c
@@ -0,0 +1,187 @@
+/*
+ * pkey-export.c
+ * -------------
+ * Export a key.
+ *
+ * Copyright (c) 2018, 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 <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <hal.h>
+
+#define HAL_KS_WRAPPED_KEYSIZE  ((2373 + 6 * 4096 / 8 + 6 * 4 + 15) & ~7)
+
+#define lose(...) do { printf(__VA_ARGS__); goto fail; } while (0)
+
+static int read_file(const char * const fn, uint8_t * const buf, size_t *buf_len, const size_t buf_max)
+{
+    int fd;
+    if ((fd = open(fn, O_RDONLY)) == -1)
+        lose("Error opening %s: %s\n", fn, strerror(errno));
+
+    size_t nread;
+    if ((nread = read(fd, buf, buf_max)) == -1)
+        lose("Error reading %s: %s\n", fn, strerror(errno));
+
+    *buf_len = nread;
+
+    if (close(fd) != 0)
+        lose("Error closing %s: %s\n", fn, strerror(errno));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static int write_buf(const char * const name, const char * const ext, const uint8_t * const buf, const size_t buf_len)
+{
+    char fn[strlen(name) + strlen(ext) + 1];
+    strcpy(fn, name);
+    strcat(fn, ext);
+
+    int fd = creat(fn, S_IRUSR);
+    if (fd == -1)
+        lose("Error opening %s: %s\n", fn, strerror(errno));
+
+    ssize_t nwrite;
+    if ((nwrite = write(fd, buf, buf_len)) != buf_len)
+        lose("Error writing %s: wrote %lu, expected %lu\n", fn, nwrite, buf_len);
+
+    if (close(fd) != 0)
+        lose("Error closing %s: %s\n", fn, strerror(errno));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+#define lose_usage(...) do { printf(__VA_ARGS__); printf(usage, argv[0]); goto fail; } while (0)
+
+int main(int argc, char *argv[])
+{
+    hal_error_t err;
+    const hal_client_handle_t client = {HAL_HANDLE_NONE};
+    const hal_session_handle_t session = {HAL_HANDLE_NONE};
+    char *pin = "fnord";
+    char *kekek_fn = NULL;
+    char *save_fn = NULL;
+    char *keyname = NULL;
+
+char usage[] = "\
+Usage: %s [-p pin] <-k kekek> [-o outfile] keyname\n\
+";
+
+    int opt;
+    while ((opt = getopt(argc, argv, "p:k:o:")) != -1) {
+        switch (opt) {
+        case 'p':
+            pin = optarg;
+            break;
+        case 'k':
+            kekek_fn = optarg;
+            break;
+        case 'o':
+            save_fn = optarg;
+            break;
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        }
+    }
+    keyname = argv[optind];
+
+    if (kekek_fn == NULL)
+        lose_usage("Error: missing option -k\n");
+    if (save_fn == NULL)
+        save_fn = keyname;
+    if (keyname == NULL)
+        lose_usage("Error: missing keyname\n");
+
+    uint8_t kekek_der[HAL_KS_WRAPPED_KEYSIZE]; size_t kekek_der_len;
+    if (read_file(kekek_fn, kekek_der, &kekek_der_len, sizeof(kekek_der)) != 0)
+        goto fail;
+
+    hal_uuid_t pkey_name;
+
+    if ((err = hal_uuid_parse(&pkey_name, keyname)) != HAL_OK)
+        lose("Error parsing private key name: %s\n", hal_error_string(err));
+
+
+    if ((err = hal_rpc_client_init()) != HAL_OK)
+        lose("Error initializing RPC client: %s\n", hal_error_string(err));
+
+    if ((err = hal_rpc_login(client, HAL_USER_NORMAL, pin, strlen(pin))) != HAL_OK)
+        lose("Error logging into HSM: %s\n", hal_error_string(err));
+
+    hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
+
+    if ((err = hal_rpc_pkey_open(client, session, &pkey, &pkey_name)) != HAL_OK)
+        lose("Error opening private key: %s\n", hal_error_string(err));
+
+    hal_pkey_handle_t kekek = {HAL_HANDLE_NONE};
+    hal_uuid_t kekek_name;
+
+    if ((err = hal_rpc_pkey_load(client, session, &kekek, &kekek_name,
+                                 kekek_der, kekek_der_len,
+                                 HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT)) != HAL_OK)
+        lose("Error loading export key: %s\n", hal_error_string(err));
+
+    uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len;
+    uint8_t kek[HAL_KS_WRAPPED_KEYSIZE]; size_t kek_len;
+
+    if ((err = hal_rpc_pkey_export(pkey, kekek,
+                                   der, &der_len, sizeof(der),
+                                   kek, &kek_len, sizeof(kek))) != HAL_OK)
+        lose("Error exporting private key: %s\n", hal_error_string(err));
+
+    if (write_buf(save_fn, ".der", der, der_len) != 0 ||
+        write_buf(save_fn, ".kek", kek, kek_len) != 0)
+        goto fail;
+
+    if ((err = hal_rpc_logout(client)) != HAL_OK)
+        lose("Error logging out of HSM: %s\n", hal_error_string(err));
+
+    if ((err = hal_rpc_client_close()) != HAL_OK)
+        lose("Error shutting down RPC client: %s\n", hal_error_string(err));
+
+    return 0;
+
+fail:
+    return -1;
+}
diff --git a/utils/pkey-import.c b/utils/pkey-import.c
new file mode 100644
index 0000000..611448f
--- /dev/null
+++ b/utils/pkey-import.c
@@ -0,0 +1,168 @@
+/*
+ * pkey-import.c
+ * -------------
+ * Import a key.
+ *
+ * Copyright (c) 2018, 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 <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <hal.h>
+
+#define HAL_KS_WRAPPED_KEYSIZE  ((2373 + 6 * 4096 / 8 + 6 * 4 + 15) & ~7)
+
+#define lose(...) do { printf(__VA_ARGS__); goto fail; } while (0)
+
+static int read_file(const char * const fn, uint8_t * const buf, size_t *buf_len, const size_t buf_max)
+{
+    int fd;
+    if ((fd = open(fn, O_RDONLY)) == -1)
+        lose("Error opening %s: %s\n", fn, strerror(errno));
+
+    size_t nread;
+    if ((nread = read(fd, buf, buf_max)) == -1)
+        lose("Error reading %s: %s\n", fn, strerror(errno));
+
+    *buf_len = nread;
+
+    if (close(fd) != 0)
+        lose("Error closing %s: %s\n", fn, strerror(errno));
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+static int read_buf(const char * const name, const char * const ext, uint8_t * const buf, size_t *buf_len, const size_t buf_max)
+{
+    char fn[strlen(name) + strlen(ext) + 1];
+    strcpy(fn, name);
+    strcat(fn, ext);
+
+    return read_file(fn, buf, buf_len, buf_max);
+}
+
+#define lose_usage(...) do { printf(__VA_ARGS__); printf(usage, argv[0]); goto fail; } while (0)
+
+int main(int argc, char *argv[])
+{
+    hal_error_t err;
+    const hal_client_handle_t client = {HAL_HANDLE_NONE};
+    const hal_session_handle_t session = {HAL_HANDLE_NONE};
+    char *pin = "fnord";
+    char *kekek_fn = NULL;
+    char *key_fn = NULL;
+
+char usage[] = "\
+Usage: %s [-p pin] <-k kekek> keyfile\n\
+";
+
+    int opt;
+    while ((opt = getopt(argc, argv, "p:k:")) != -1) {
+        switch (opt) {
+        case 'p':
+            pin = optarg;
+            break;
+        case 'k':
+            kekek_fn = optarg;
+            break;
+        case 'h':
+        case '?':
+            printf(usage, argv[0]);
+            return 0;
+        }
+    }
+    key_fn = argv[optind];
+
+    if (kekek_fn == NULL)
+        lose_usage("Error: missing option -k\n");
+    if (key_fn == NULL)
+        lose_usage("Error: missing keyfile\n");
+
+    uint8_t kekek_der[HAL_KS_WRAPPED_KEYSIZE]; size_t kekek_der_len;
+
+    if (read_file(kekek_fn, kekek_der, &kekek_der_len, sizeof(kekek_der)) != 0)
+        goto fail;
+
+    uint8_t der[HAL_KS_WRAPPED_KEYSIZE]; size_t der_len;
+    uint8_t kek[HAL_KS_WRAPPED_KEYSIZE]; size_t kek_len;
+
+    if (read_buf(key_fn, ".der", der, &der_len, sizeof(der)) != 0 ||
+        read_buf(key_fn, ".kek", kek, &kek_len, sizeof(kek)) != 0)
+        goto fail;
+
+    if ((err = hal_rpc_client_init()) != HAL_OK)
+        lose("Error initializing RPC client: %s\n", hal_error_string(err));
+
+    if ((err = hal_rpc_login(client, HAL_USER_NORMAL, pin, strlen(pin))) != HAL_OK)
+        lose("Error logging into HSM: %s\n", hal_error_string(err));
+
+    hal_pkey_handle_t kekek = {HAL_HANDLE_NONE};
+    hal_uuid_t kekek_name;
+
+    if ((err = hal_rpc_pkey_load(client, session, &kekek, &kekek_name,
+                                 kekek_der, kekek_der_len,
+                                 HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT)) != HAL_OK)
+        lose("Error loading import key: %s\n", hal_error_string(err));
+
+    hal_pkey_handle_t private_key = {HAL_HANDLE_NONE};
+    hal_uuid_t private_name;
+
+    if ((err = hal_rpc_pkey_import(client, session,
+                                   &private_key, &private_name,
+                                   kekek,
+                                   der, der_len,
+                                   kek, kek_len,
+                                   HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE | HAL_KEY_FLAG_TOKEN | HAL_KEY_FLAG_EXPORTABLE)) != HAL_OK)
+        lose("Error importing private key: %s\n", hal_error_string(err));
+
+    char name_str[HAL_UUID_TEXT_SIZE];
+
+    if ((err = hal_uuid_format(&private_name, name_str, sizeof(name_str))) != HAL_OK)
+        lose("Error formatting private key name: %s\n", hal_error_string(err));
+    printf("New private key name: %s\n", name_str);
+
+    if ((err = hal_rpc_logout(client)) != HAL_OK)
+        lose("Error logging out of HSM: %s\n", hal_error_string(err));
+
+    if ((err = hal_rpc_client_close()) != HAL_OK)
+        lose("Error shutting down RPC client: %s\n", hal_error_string(err));
+
+    return 0;
+
+fail:
+    return -1;
+}



More information about the Commits mailing list