[Cryptech-Commits] [user/paul/hashsig-naive] 01/01: Naive implementation of draft-mcgrew-hash-sigs-10.txt

git at cryptech.is git at cryptech.is
Thu Mar 15 21:53:21 UTC 2018


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

paul at psgd.org pushed a commit to branch master
in repository user/paul/hashsig-naive.

commit 8ba92fb36610120f7dc9a6fd71ac4780eae3beba
Author: Paul Selkirk <paul at psgd.org>
AuthorDate: Thu Mar 15 17:27:39 2018 -0400

    Naive implementation of draft-mcgrew-hash-sigs-10.txt
---
 Makefile        |   16 +
 README          |   12 +
 hashsig_naive.c | 1977 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2005 insertions(+)

diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c07470a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+LIBHAL = ../../../sw/libhal
+CFLAGS = -Wall -Wextra -g -I$(LIBHAL)
+
+all: hashsig
+
+hashsig: hashsig_naive.o xdr.o errorstrings.o
+	$(CC) -o $@ $^ -lcrypto
+
+xdr.o: $(LIBHAL)/xdr.c
+	$(CC) -c $(CFLAGS) -o $@ $^
+
+errorstrings.o: $(LIBHAL)/errorstrings.c
+	$(CC) -c $(CFLAGS) -o $@ $^
+
+clean:
+	rm -f hashsig hashsig_naive.o xdr.o errorstrings.o
diff --git a/README b/README
new file mode 100644
index 0000000..bec81a5
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+Reference implementation of of the LMS Hash Based Signature Scheme from
+draft-mcgrew-hash-sigs-10.
+
+This is a naive implementation, which hews as closely as possible to the
+text of the draft, without any regard for performance (or security - keys
+are stored unencrypted in the local file system). It is intended to show
+that the draft can be implemented as written (except I found a few
+omissions in the text), and can interoperate with the official reference
+implementation at http://github.com/cisco/hash-sigs.
+
+The user interface is modeled on demo.c from the Cisco implementation,
+although all code was written independently.
diff --git a/hashsig_naive.c b/hashsig_naive.c
new file mode 100644
index 0000000..aa590bf
--- /dev/null
+++ b/hashsig_naive.c
@@ -0,0 +1,1977 @@
+/*
+ * hashsig.c
+ * ---------
+ * Naive implementation of draft-mcgrew-hash-sigs-10.txt
+ *
+ * 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.
+ */
+
+/*
+ * This is a naive implementation of McGrew et al's Hash-Based Signature
+ * scheme, which hews as closely as possible to the text of the draft,
+ * without any regard for performance (or security - keys are stored
+ * unencrypted in the local file system).
+ *
+ * For simplicity, this implemention restricts all LMS keys in the HSS scheme
+ * to use the same LMS type and LM-OTS type.
+ */
+
+#include <arpa/inet.h>		/* htonl and friends */
+
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+
+#include "hal.h"
+#include "hashsig.h"
+#include "xdr_internal.h"
+
+typedef struct { uint8_t bytes[32]; } bytestring32;
+typedef struct { uint8_t bytes[16]; } bytestring16;
+
+#define check(op) do { hal_error_t _err = (op); if (_err != HAL_OK) return _err; } while (0)
+#define checkssl(op) do { if ((op) == 0) { return HAL_ERROR_IMPOSSIBLE; } } while (0)
+
+/* ---------------------------------------------------------------- */
+
+/*
+ * XDR extensions
+ */
+
+static inline hal_error_t hal_xdr_encode_bytestring32(uint8_t **outbuf, const uint8_t * const limit, const bytestring32 * const value)
+{
+    return hal_xdr_encode_fixed_opaque(outbuf, limit, (const uint8_t *)value, sizeof(bytestring32));
+}
+
+static inline hal_error_t hal_xdr_decode_bytestring32(const uint8_t ** const inbuf, const uint8_t * const limit, bytestring32 *value)
+{
+    return hal_xdr_decode_fixed_opaque(inbuf, limit, (uint8_t *)value, sizeof(bytestring32));
+}
+
+static inline hal_error_t hal_xdr_decode_bytestring32_ptr(const uint8_t ** const inbuf, const uint8_t * const limit, bytestring32 **value)
+{
+    return hal_xdr_decode_fixed_opaque_ptr(inbuf, limit, (const uint8_t ** const)value, sizeof(bytestring32));
+}
+
+static inline hal_error_t hal_xdr_encode_bytestring16(uint8_t **outbuf, const uint8_t * const limit, const bytestring16 *value)
+{
+    return hal_xdr_encode_fixed_opaque(outbuf, limit, (const uint8_t *)value, sizeof(bytestring16));
+}
+
+static inline hal_error_t hal_xdr_decode_bytestring16(const uint8_t ** const inbuf, const uint8_t * const limit, bytestring16 *value)
+{
+    return hal_xdr_decode_fixed_opaque(inbuf, limit, (uint8_t *)value, sizeof(bytestring16));
+}
+
+static inline hal_error_t hal_xdr_decode_bytestring16_ptr(const uint8_t ** const inbuf, const uint8_t * const limit, bytestring16 **value)
+{
+    return hal_xdr_decode_fixed_opaque_ptr(inbuf, limit, (const uint8_t ** const)value, sizeof(bytestring16));
+}
+
+/* ---------------------------------------------------------------- */
+
+// 3.1.  Data Types
+
+#define u32str(X) htonl(X)
+#define u16str(X) htons(X)
+#define u8str(X) (X & 0xff)
+
+// 3.1.3.  Strings of w-bit elements
+
+static uint8_t coef(const uint8_t * const S, const size_t i, size_t w)
+{
+    switch (w) {
+    case 1:
+        return (S[i/8] >> (7 - (i % 8))) & 0x01;
+    case 2:
+        return (S[i/4] >> (6 - (2 * (i % 4)))) & 0x03;
+    case 4:
+        return (S[i/2] >> (4 - (4 * (i % 2)))) & 0x0f;
+    case 8:
+        return S[i];
+    default:
+        return 0;
+    }
+}
+
+// 3.2.  Security string
+
+#define D_PBLC 0x8080
+#define D_MESG 0x8181
+#define D_LEAF 0x8282
+#define D_INTR 0x8383
+
+/* ---------------------------------------------------------------- */
+
+// 4.  LM-OTS One-Time Signatures
+
+// 4.2.  Parameter Sets
+
+static inline size_t lmots_type_to_n(const lmots_algorithm_t lmots_type)
+{
+    switch (lmots_type) {
+    case lmots_sha256_n32_w1:
+    case lmots_sha256_n32_w2:
+    case lmots_sha256_n32_w4:
+    case lmots_sha256_n32_w8: return 32;
+    default: return 0;
+    }
+}
+
+static inline size_t lmots_type_to_w(const lmots_algorithm_t lmots_type)
+{
+    switch (lmots_type) {
+    case lmots_sha256_n32_w1: return 1;
+    case lmots_sha256_n32_w2: return 2;
+    case lmots_sha256_n32_w4: return 4;
+    case lmots_sha256_n32_w8: return 8;
+    default: return 0;
+    }
+}
+
+static inline size_t lmots_type_to_p(const lmots_algorithm_t lmots_type)
+{
+    switch (lmots_type) {
+    case lmots_sha256_n32_w1: return 265;
+    case lmots_sha256_n32_w2: return 133;
+    case lmots_sha256_n32_w4: return  67;
+    case lmots_sha256_n32_w8: return  34;
+    default: return 0;
+    }
+}
+
+static inline size_t lmots_type_to_ls(const lmots_algorithm_t lmots_type)
+{
+    switch (lmots_type) {
+    case lmots_sha256_n32_w1: return 7;
+    case lmots_sha256_n32_w2: return 6;
+    case lmots_sha256_n32_w4: return 4;
+    case lmots_sha256_n32_w8: return 0;
+    default: return 0;
+    }
+}
+
+// 4.3.  Private Key
+
+static inline size_t lmots_private_key_len(const lmots_algorithm_t lmots_type)
+{
+    /* u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1] */
+    return 2 * sizeof(uint32_t) + sizeof(bytestring16) + lmots_type_to_p(lmots_type) * lmots_type_to_n(lmots_type);
+}
+
+static hal_error_t lmots_generate_private_key(const lmots_algorithm_t lmots_type,
+                                              const bytestring16 * const I, const size_t q,
+                                              uint8_t *key, size_t *key_len, const size_t key_max)
+{
+    if (I == NULL || key == NULL|| key_max < lmots_private_key_len(lmots_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 0: Generating a Private Key
+
+//  1. retrieve the value p from the type, and the value I the 16 byte
+//     identifier of the LMS public/private keypair that this LM-OTS private
+//     key will be used with
+
+//  2. set type to the typecode of the algorithm
+
+//  3. set n and p according to the typecode and Table 1
+
+    size_t n = lmots_type_to_n(lmots_type);
+    size_t p = lmots_type_to_p(lmots_type);
+
+//  4. compute the array x as follows:
+//     for ( i = 0; i < p; i = i + 1 ) {
+//       set x[i] to a uniformly random n-byte string
+//     }
+
+    bytestring32 x[p];
+    for (size_t i = 0; i < p; ++i)
+        checkssl(RAND_bytes((unsigned char *)&x[i], n));
+
+//  5. return u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1]
+
+    uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_max;
+    check(hal_xdr_encode_int(&keyptr, keylim, lmots_type));
+    check(hal_xdr_encode_bytestring16(&keyptr, keylim, I));
+    check(hal_xdr_encode_int(&keyptr, keylim, q));
+    for (size_t i = 0; i < p; ++i)
+        check(hal_xdr_encode_bytestring32(&keyptr, keylim, &x[i]));
+
+    if (key_len != NULL)
+        *key_len = keyptr - key;
+
+    return HAL_OK;
+}
+
+// 4.4.  Public Key
+
+static inline size_t lmots_public_key_len(const lmots_algorithm_t lmots_type)
+{
+    /* u32str(type) || I || u32str(q) || K */
+    return ((2 * sizeof(uint32_t)) + sizeof(bytestring16) + lmots_type_to_n(lmots_type));
+}
+
+static hal_error_t lmots_generate_public_key(const uint8_t * const key, const size_t key_len,
+                                             uint8_t *pub, size_t *pub_len, const size_t pub_max)
+{
+    if (key == NULL || pub == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 1: Generating a One Time Signature Public Key From a
+//   Private Key
+//
+//   1. set type to the typecode of the algorithm
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+    /* private key format
+     * u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1]
+     */
+
+//   2. set the integers n, p, and w according to the typecode and Table 1
+
+    /* XXX n is not used here */
+    size_t p = lmots_type_to_p(lmots_type);
+    size_t w = lmots_type_to_w(lmots_type);
+
+//   3. determine x, I and q from the private key
+
+    bytestring16 I;
+    check(hal_xdr_decode_bytestring16(&keyptr, keylim, &I));
+
+    uint32_t q;
+    check(hal_xdr_decode_int(&keyptr, keylim, &q));
+
+    bytestring32 x[p];
+    for (size_t i = 0; i < p; ++i)
+        check(hal_xdr_decode_bytestring32(&keyptr, keylim, &x[i]));
+
+//   4. compute the string K as follows:
+//      for ( i = 0; i < p; i = i + 1 ) {
+//        tmp = x[i]
+//        for ( j = 0; j < 2^w - 1; j = j + 1 ) {
+//           tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
+//        }
+//        y[i] = tmp
+//      }
+//      K = H(I || u32str(q) || u16str(D_PBLC) || y[0] || ... || y[p-1])
+
+    SHA256_CTX ctx;
+    bytestring32 y[p];
+    for (size_t i = 0; i < p; ++i) {
+        bytestring32 tmp;
+        memcpy(&tmp, &x[i], sizeof(tmp));
+        for (size_t j = 0; j < (1U << w) - 1; ++j) {
+            checkssl(SHA256_Init(&ctx));
+            checkssl(SHA256_Update(&ctx, &I, sizeof(I)));
+            uint32_t l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+            uint16_t s = u16str(i); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+            uint8_t b = u8str(j); checkssl(SHA256_Update(&ctx, &b, sizeof(b)));
+            checkssl(SHA256_Update(&ctx, &tmp, sizeof(tmp)));
+            checkssl(SHA256_Final((unsigned char *)&tmp, &ctx));
+        }
+        memcpy(&y[i], &tmp, sizeof(tmp));
+    }
+    bytestring32 K;
+    checkssl(SHA256_Init(&ctx));
+    checkssl(SHA256_Update(&ctx, &I, sizeof(I)));
+    uint32_t l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+    uint16_t s = u16str(D_PBLC); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+    for (size_t i = 0; i < p; ++i)
+        checkssl(SHA256_Update(&ctx, (uint8_t *)&y[i], sizeof(y[i])));
+    checkssl(SHA256_Final((unsigned char *)&K, &ctx));
+
+//   5. return u32str(type) || I || u32str(q) || K
+
+    uint8_t *pubptr = pub;
+    const uint8_t * const publim = pub + pub_max;
+    check(hal_xdr_encode_int(&pubptr, publim, lmots_type));
+    check(hal_xdr_encode_bytestring16(&pubptr, publim, &I));
+    check(hal_xdr_encode_int(&pubptr, publim, q));
+    check(hal_xdr_encode_bytestring32(&pubptr, publim, &K));
+
+    if (pub_len != NULL)
+        *pub_len = pubptr - pub;
+
+    return HAL_OK;
+}
+
+// 4.5.  Checksum
+
+static uint16_t Cksm(const uint8_t * const S, const lmots_algorithm_t lmots_type)
+{
+//   Algorithm 2: Checksum Calculation
+//     sum = 0
+//     for ( i = 0; i < (n*8/w); i = i + 1 ) {
+//       sum = sum + (2^w - 1) - coef(S, i, w)
+//     }
+//     return (sum << ls)
+
+    size_t n = lmots_type_to_n(lmots_type);
+    size_t w = lmots_type_to_w(lmots_type);
+    size_t ls = lmots_type_to_ls(lmots_type);
+
+    uint16_t sum = 0;
+    for (size_t i = 0; i < (n * 8 / w); ++i)
+        sum += ((1 << w) - 1) - coef(S, i, w);
+    return (sum << ls);
+}
+
+// 4.6.  Signature Generation
+
+static inline size_t lmots_signature_len(const lmots_algorithm_t lmots_type)
+{
+    /* u32str(type) || C || y[0] || ... || y[p-1] */
+    return (sizeof(uint32_t) + ((lmots_type_to_p(lmots_type) + 1) * lmots_type_to_n(lmots_type)));
+}
+
+static hal_error_t lmots_sign(const uint8_t * const key, const size_t key_len,
+                              const uint8_t * const msg, const size_t msg_len,
+                              uint8_t *sig, size_t *sig_len, const size_t sig_max)
+{
+    if (key == NULL ||  msg == NULL || sig == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 3: Generating a One Time Signature From a Private Key and a
+//   Message
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    /* private key format
+     * u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1]
+     */
+
+//     1. set type to the typecode of the algorithm
+
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+//     2. set n, p, and w according to the typecode and Table 1
+
+    size_t n = lmots_type_to_n(lmots_type);
+    size_t p = lmots_type_to_p(lmots_type);
+    size_t w = lmots_type_to_w(lmots_type);
+
+//     3. determine x, I and q from the private key
+
+    bytestring16 I;
+    check(hal_xdr_decode_bytestring16(&keyptr, keylim, &I));
+
+    uint32_t q;
+    check(hal_xdr_decode_int(&keyptr, keylim, &q));
+
+    bytestring32 x[p];
+    for (size_t i = 0; i < p; ++i)
+        check(hal_xdr_decode_bytestring32(&keyptr, keylim, &x[i]));
+
+//     4. set C to a uniformly random n-byte string
+
+    bytestring32 C;
+    checkssl(RAND_bytes((unsigned char *)&C, n));
+
+//     5. compute the array y as follows:
+//        Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
+//        for ( i = 0; i < p; i = i + 1 ) {
+//          a = coef(Q || Cksm(Q), i, w)
+//          tmp = x[i]
+//          for ( j = 0; j < a; j = j + 1 ) {
+//             tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
+//          }
+//          y[i] = tmp
+//        }
+
+    SHA256_CTX ctx;
+    uint8_t Q[n + 2];           /* hash || 16-bit checksum */
+
+    checkssl(SHA256_Init(&ctx));
+    checkssl(SHA256_Update(&ctx, &I, sizeof(I)));
+    uint32_t l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+    uint16_t s = u16str(D_MESG); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+    checkssl(SHA256_Update(&ctx, &C, sizeof(C)));
+    checkssl(SHA256_Update(&ctx, msg, msg_len));
+    checkssl(SHA256_Final((unsigned char *)Q, &ctx));
+
+    /* append checksum */
+    *(uint16_t *)&Q[n] = u16str(Cksm((uint8_t *)Q, lmots_type));
+
+    bytestring32 y[p];
+    for (size_t i = 0; i < p; ++i) {
+        size_t a = coef(Q, i, w);
+        bytestring32 tmp;
+        memcpy(&tmp, &x[i], sizeof(tmp));
+        for (size_t j = 0; j < a; ++j) {
+            checkssl(SHA256_Init(&ctx));
+            checkssl(SHA256_Update(&ctx, &I, sizeof(I)));
+            uint32_t l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+            uint16_t s = u16str(i); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+            uint8_t b = u8str(j); checkssl(SHA256_Update(&ctx, &b, sizeof(b)));
+            checkssl(SHA256_Update(&ctx, &tmp, sizeof(tmp)));
+            checkssl(SHA256_Final((unsigned char *)&tmp, &ctx));
+        }
+        memcpy(&y[i], &tmp, sizeof(tmp));
+    }
+
+//      6. return u32str(type) || C || y[0] || ... || y[p-1]
+
+    uint8_t *sigptr = sig;
+    const uint8_t * const siglim = sig + sig_max;
+    check(hal_xdr_encode_int(&sigptr, siglim, lmots_type));
+    check(hal_xdr_encode_bytestring32(&sigptr, siglim, &C));
+    for (size_t i = 0; i < p; ++i)
+        check(hal_xdr_encode_bytestring32(&sigptr, siglim, &y[i]));
+
+    if (sig_len != NULL)
+        *sig_len = sigptr - sig;
+
+    return HAL_OK;
+}
+
+// 4.7.  Signature Verification
+
+static hal_error_t lmots_public_key_candidate(const lmots_algorithm_t pubtype,
+                                              const bytestring16 * const I, const size_t q,
+                                              const uint8_t * const msg, const size_t msg_len,
+                                              const uint8_t * const sig, const size_t sig_len,
+                                              bytestring32 * Kc);
+
+#if 0 /* unused */
+static hal_error_t lmots_verify(const uint8_t * const key, const size_t key_len,
+                                const uint8_t * const msg, const size_t msg_len,
+                                const uint8_t * const sig, const size_t sig_len)
+{
+    if (key == NULL || key_len < lmots_private_key_len(lmots_type) ||
+        msg == NULL || sig == NULL || sig_len < lmots_signature_len(lmots_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 4a: Verifying a Signature and Message Using a Public Key
+//
+//    1. if the public key is not at least four bytes long, return INVALID
+
+    if (key_len < 4)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//    2. parse pubtype, I, q, and K from the public key as follows:
+//       a. pubtype = strTou32(first 4 bytes of public key)
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+    /* XXX missing from draft */
+    size_t n = lmots_type_to_n(lmots_type);
+
+//       b. if the public key is not exactly 24 + n bytes long,
+//          return INVALID
+
+    if (key_len != 24 + n)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//       c. I = next 16 bytes of public key
+
+    bytestring16 I;
+    check(hal_xdr_decode_bytestring16(&keyptr, keylim, &I));
+
+//       d. q = strTou32(next 4 bytes of public key)
+
+    uint32_t q;
+    check(hal_xdr_decode_int(&keyptr, keylim, &q));
+
+//       e. K = next n bytes of public key
+
+    bytestring32 K;
+    check(hal_xdr_decode_bytestring32(&keyptr, keylim, &K));
+
+//    3. compute the public key candidate Kc from the signature,
+//       message, and the identifiers I and q obtained from the
+//       public key, using Algorithm 4b.  If Algorithm 4b returns
+//       INVALID, then return INVALID.
+
+    bytestring32 Kc;
+    check(lmots_public_key_candidate(lmots_type, &I, q, msg, msg_len, sig, sig_len, &Kc));
+
+//    4. if Kc is equal to K, return VALID; otherwise, return INVALID
+
+    return (memcmp(&Kc, &K, sizeof(Kc)) ? HAL_ERROR_INVALID_SIGNATURE : HAL_OK);
+}
+#endif
+
+static hal_error_t lmots_public_key_candidate(const lmots_algorithm_t lmots_type,
+                                              const bytestring16 * const I, const size_t q,
+                                              const uint8_t * const msg, const size_t msg_len,
+                                              const uint8_t * const sig, const size_t sig_len,
+                                              bytestring32 * const Kc)
+{
+    if (I == NULL || msg == NULL || sig == NULL || Kc == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 4b: Computing a Public Key Candidate Kc from a Signature,
+//   Message, Signature Typecode Type, and identifiers I, q
+//
+//  1. if the signature is not at least four bytes long, return INVALID
+
+    if (sig_len < 4)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//  2. parse sigtype, C, and y from the signature as follows:
+//     a. sigtype = strTou32(first 4 bytes of signature)
+
+    const uint8_t *sigptr = sig;
+    const uint8_t * const siglim = sig + sig_len;
+
+    lmots_algorithm_t sigtype;
+    check(hal_xdr_decode_int(&sigptr, siglim, &sigtype));
+
+//     b. if sigtype is not equal to pubtype, return INVALID
+
+    if (sigtype != lmots_type)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//     c. set n and p according to the pubtype and Table 1;  if the
+//     signature is not exactly 4 + n * (p+1) bytes long, return INVALID
+
+    size_t n = lmots_type_to_n(lmots_type);
+    size_t p = lmots_type_to_p(lmots_type);
+
+    if (sig_len != 4 + n * (p + 1))
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//     d. C = next n bytes of signature
+
+    bytestring32 C;
+    check(hal_xdr_decode_bytestring32(&sigptr, siglim, &C));
+
+//     e.  y[0] = next n bytes of signature
+//         y[1] = next n bytes of signature
+//         ...
+//       y[p-1] = next n bytes of signature
+
+    bytestring32 y[p];
+    for (size_t i = 0; i < p; ++i)
+        check(hal_xdr_decode_bytestring32(&sigptr, siglim, &y[i]));
+
+//  3. compute the string Kc as follows
+//     Q = H(I || u32str(q) || u16str(D_MESG) || C || message)
+//     for ( i = 0; i < p; i = i + 1 ) {
+//       a = coef(Q || Cksm(Q), i, w)
+//       tmp = y[i]
+//       for ( j = a; j < 2^w - 1; j = j + 1 ) {
+//          tmp = H(I || u32str(q) || u16str(i) || u8str(j) || tmp)
+//       }
+//       z[i] = tmp
+//     }
+//     Kc = H(I || u32str(q) || u16str(D_PBLC) || z[0] || z[1] || ... || z[p-1])
+
+    SHA256_CTX ctx;
+    uint8_t Q[n + 2];           /* hash || 16-bit checksum */
+
+    checkssl(SHA256_Init(&ctx));
+    checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+    uint32_t l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+    uint16_t s = u16str(D_MESG); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+    checkssl(SHA256_Update(&ctx, &C, sizeof(C)));
+    checkssl(SHA256_Update(&ctx, msg, msg_len));
+    checkssl(SHA256_Final((unsigned char *)Q, &ctx));
+
+    /* append checksum */
+    *(uint16_t *)&Q[n] = u16str(Cksm((uint8_t *)Q, lmots_type));
+
+    size_t w = lmots_type_to_w(lmots_type);
+    bytestring32 z[p];
+
+    for (size_t i = 0; i < p; ++i) {
+        uint8_t a = coef(Q, i, w);
+        bytestring32 tmp;
+        memcpy(&tmp, &y[i], sizeof(tmp));
+        for (size_t j = (size_t)a; j < (1U << w) - 1; ++j) {
+            checkssl(SHA256_Init(&ctx));
+            checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+            l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+            s = u16str(i); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+            uint8_t b = u8str(j); checkssl(SHA256_Update(&ctx, &b, sizeof(b)));
+            checkssl(SHA256_Update(&ctx, &tmp, sizeof(tmp)));
+            checkssl(SHA256_Final((unsigned char *)&tmp, &ctx));
+        }
+        memcpy(&z[i], &tmp, sizeof(tmp));
+    }
+
+    checkssl(SHA256_Init(&ctx));
+    checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+    l = u32str(q); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+    s = u16str(D_PBLC); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+    for (size_t i = 0; i < p; ++i)
+        checkssl(SHA256_Update(&ctx, &z[i], sizeof(z[i])));
+    checkssl(SHA256_Final((unsigned char *)Kc, &ctx));
+
+//  4. return Kc
+
+    return HAL_OK;
+}
+
+/* ---------------------------------------------------------------- */
+
+// 5.  Leighton Micali Signatures
+
+// 5.1.  Parameters
+
+static inline size_t lms_type_to_h(const lms_algorithm_t lms_type)
+{
+    switch (lms_type) {
+    case lms_sha256_n32_h5:  return  5;
+    case lms_sha256_n32_h10: return 10;
+    case lms_sha256_n32_h15: return 15;
+    case lms_sha256_n32_h20: return 20;
+    case lms_sha256_n32_h25: return 25;
+    default: return 0;
+    }
+}
+
+static inline size_t lms_type_to_m(const lms_algorithm_t lms_type)
+{
+    switch (lms_type) {
+    case lms_sha256_n32_h5:
+    case lms_sha256_n32_h10:
+    case lms_sha256_n32_h15:
+    case lms_sha256_n32_h20:
+    case lms_sha256_n32_h25: return 32;
+    default: return 0;
+    }
+}
+
+// 5.2.  LMS Private Key
+
+static inline size_t lms_private_key_len(const lms_algorithm_t lms_type, const lmots_algorithm_t lmots_type)
+{
+    /* u32str(type) || u32str(q) || OTS_PRIV[0] || OTS_PRIV[1] || ... || OTS_PRIV[2^h - 1] */
+    return ((2 * sizeof(uint32_t)) + ((1 << lms_type_to_h(lms_type)) * lmots_private_key_len(lmots_type)));
+}
+
+static hal_error_t lms_generate_private_key(const lms_algorithm_t lms_type,
+                                            const lmots_algorithm_t lmots_type,
+                                            uint8_t * const key, size_t * const key_len, const size_t key_max)
+{
+    if (key == NULL || key_max < lms_private_key_len(lms_type, lmots_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 5: Computing an LMS Private Key.
+
+//      1. determine h and m from the typecode and Table 2.
+
+    size_t h = lms_type_to_h(lms_type);
+    /* XXX m is not used here */
+
+    /* XXX missing from draft: generate I for this keypair */
+    bytestring16 I;
+    checkssl(RAND_bytes((unsigned char *)&I, sizeof(I)));
+
+//      2. compute the array OTS_PRIV[] as follows:
+//         for ( q = 0; q < 2^h; q = q + 1) {
+//            OTS_PRIV[q] = LM-OTS private key with identifiers I, q
+//         }
+
+    /* initialize the key buffer with type and q=0, so we can generate the
+     * lmots private keys directly in the key buffer
+     */
+    uint8_t *keyptr = key;
+    uint8_t *keylim = key + key_max;
+    check(hal_xdr_encode_int(&keyptr, keylim, lms_type));
+    check(hal_xdr_encode_int(&keyptr, keylim, 0));
+
+    for (size_t q = 0; q < (1U << h); ++q) {
+        size_t ots_len;
+        check(lmots_generate_private_key(lmots_type, &I, q, keyptr, &ots_len, keylim - keyptr));
+        keyptr += ots_len;
+    }
+
+//      3. q = 0
+
+    /* XXX missing from draft: return the key in output format */
+    /* 4. return u32str(type) || u32str(q) || OTS_PRIV[0] || OTS_PRIV[1] || ... || OTS_PRIV[2^h - 1] */
+
+    if (key_len != NULL)
+        *key_len = keyptr - key;
+
+    return HAL_OK;
+}
+
+// 5.3.  LMS Public Key
+
+static inline size_t lms_public_key_len(const lms_algorithm_t lms_type)
+{
+    /* u32str(type) || u32str(otstype) || I || T[1] */
+    return ((2 * sizeof(uint32_t)) + 16 + lms_type_to_m(lms_type));
+}
+
+static hal_error_t lms_generate_T(const lms_algorithm_t lms_type,
+                                  const lmots_algorithm_t lmots_type,
+                                  const bytestring16 * const I,
+                                  const uint8_t * const key,
+                                  bytestring32 * const T)
+{
+    size_t h = lms_type_to_h(lms_type);
+    size_t lmots_prv_len = lmots_private_key_len(lmots_type);
+    size_t lmots_pub_len = lmots_public_key_len(lmots_type);
+    const uint8_t *keyptr = key;
+    SHA256_CTX ctx;
+
+//  T[r]=/ H(I||u32str(r)||u16str(D_LEAF)||OTS_PUB_HASH[r-2^h])   if r >= 2^h,
+//       \ H(I||u32str(r)||u16str(D_INTR)||T[2*r]||T[2*r+1]) otherwise.
+
+    /* first generate the leaf-node T values */
+    for (size_t q = 0; q < (1U << h); ++q) {
+        /* generate OTS_PUB_HASH[q] */
+        size_t r = (1 << h) + q;
+        uint8_t pub[lmots_pub_len];
+        check(lmots_generate_public_key(keyptr, lmots_prv_len, pub, NULL, lmots_pub_len));
+        keyptr += lmots_prv_len;
+
+        checkssl(SHA256_Init(&ctx));
+        checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+        uint32_t l = u32str(r); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+        uint16_t s = u16str(D_LEAF); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+        checkssl(SHA256_Update(&ctx, &pub[24], sizeof(bytestring32)));
+        checkssl(SHA256_Final((unsigned char *)&T[r], &ctx));
+    }
+
+    /* then generate the rest of T[], from bottom to top */
+    for (size_t r = (1 << h) - 1; r > 0; --r) {
+        checkssl(SHA256_Init(&ctx));
+        checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+        uint32_t l = u32str(r); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+        uint16_t s = u16str(D_INTR); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+        checkssl(SHA256_Update(&ctx, &T[2*r], sizeof(T[r])));
+        checkssl(SHA256_Update(&ctx, &T[2*r+1], sizeof(T[r])));
+        checkssl(SHA256_Final((unsigned char *)&T[r], &ctx));
+    }
+
+    return HAL_OK;
+}
+
+static hal_error_t lms_generate_public_key(const uint8_t * const key, const size_t key_len,
+                                           uint8_t *pub, size_t *pub_len, const size_t pub_max)
+{
+    if (key == NULL ||  pub == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    /* private key format
+     * u32str(type) || u32str(q) || OTS_PRIV[0] || OTS_PRIV[1] || ... || OTS_PRIV[2^h - 1]
+     */
+
+    lms_algorithm_t lms_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lms_type));
+    size_t h = lms_type_to_h(lms_type);
+
+    /* skip over q, which will be 0 */
+    keyptr += 4;
+
+    /* peek into the first lmots private key for type and I */
+    /* u32str(type) || I || u32str(q) || x[0] || x[1] || ... || x[p-1] */
+    const uint8_t *lookahead = keyptr;
+    lmots_algorithm_t lmots_type;
+    bytestring16 I;
+    check(hal_xdr_decode_int(&lookahead, keylim, &lmots_type));
+    check(hal_xdr_decode_bytestring16(&lookahead, keylim, &I));
+
+//   The LMS public key is the string u32str(type) || u32str(otstype) || I || T[1].
+
+    uint8_t *pubptr = pub;
+    const uint8_t * const publim = pub + pub_max;
+    check(hal_xdr_encode_int(&pubptr, publim, lms_type));
+    check(hal_xdr_encode_int(&pubptr, publim, lmots_type));
+    check(hal_xdr_encode_bytestring16(&pubptr, publim, &I));
+    bytestring32 T[1 << (h + 1)];
+    check(lms_generate_T(lms_type, lmots_type, &I, keyptr, T));
+    check(hal_xdr_encode_bytestring32(&pubptr, publim, &T[1]));
+
+    if (pub_len != NULL)
+        *pub_len = pubptr - pub;
+
+    return HAL_OK;
+}
+
+// 5.4.  LMS Signature
+
+static inline size_t lms_signature_len(const lms_algorithm_t lms_type, const lmots_algorithm_t lmots_type)
+{
+    /* u32str(q) || ots_signature || u32str(type) || path[0] || path[1] || ... || path[h-1] */
+    return ((2 * sizeof(uint32_t)) + lmots_signature_len(lmots_type) + (lms_type_to_h(lms_type) * lms_type_to_m(lms_type)));
+}
+
+static hal_error_t lms_sign(const uint8_t * const key, const size_t key_len,
+                            const uint8_t * const msg, const size_t msg_len,
+                            uint8_t *sig, size_t *sig_len, const size_t sig_max)
+{
+//   An LMS signature consists of
+//
+//      the number q of the leaf associated with the LM-OTS signature, as
+//      a four-byte unsigned integer in network byte order,
+//
+//      an LM-OTS signature, and
+//
+//      a typecode indicating the particular LMS algorithm,
+//
+//      an array of h m-byte values that is associated with the path
+//      through the tree from the leaf associated with the LM-OTS
+//      signature to the root.
+//
+//   Symbolically, the signature can be represented as u32str(q) ||
+//   ots_signature || u32str(type) || path[0] || path[1] || ... ||
+//   path[h-1].
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    /* private key format
+     * u32str(type) || u32str(q) || OTS_PRIV[0] || OTS_PRIV[1] || ... || OTS_PRIV[2^h - 1]
+     */
+
+    lms_algorithm_t lms_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lms_type));
+    size_t h = lms_type_to_h(lms_type);
+
+    uint32_t q;
+    check(hal_xdr_decode_int(&keyptr, keylim, &q));
+    if (q >= (1U << h))
+        return HAL_ERROR_HASHSIG_KEY_EXHAUSTED;
+
+    /* peek into the first lmots private key for lmots_type and I */
+    const uint8_t *lookahead = keyptr;
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&lookahead, keylim, &lmots_type));
+    bytestring16 I;
+    check(hal_xdr_decode_bytestring16(&lookahead, keylim, &I));
+
+    if (sig_max < lms_signature_len(lms_type, lmots_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    uint8_t *sigptr = sig;
+    const uint8_t * const siglim = sig + sig_max;
+    check(hal_xdr_encode_int(&sigptr, siglim, q));
+
+    /* locate the lmots signing key OTS_PRIV[q] */
+    const size_t lmots_key_len = lmots_private_key_len(lmots_type);
+    const uint8_t * const lmots_key = keyptr + q * lmots_key_len;
+
+    /* generate the lmots signature */
+    size_t lmots_sig_len;
+    check(lmots_sign(lmots_key, lmots_key_len, msg, msg_len, sigptr, &lmots_sig_len, siglim - sigptr));
+    sigptr += lmots_sig_len;
+
+    check(hal_xdr_encode_int(&sigptr, siglim, lms_type));
+
+    /* generate the path array */
+    bytestring32 T[1 << (h + 1)];
+    check(lms_generate_T(lms_type, lmots_type, &I, keyptr, T));
+    for (size_t r = (1 << h) + q; r > 1; r /= 2)
+        check(hal_xdr_encode_bytestring32(&sigptr, siglim, ((r & 1) ? &T[r-1] : &T[r+1])));
+
+    if (sig_len != NULL)
+        *sig_len = sigptr - sig;
+
+    /* update q before returning the signature */
+    keyptr = key + 4;
+    check(hal_xdr_encode_int((uint8_t ** const)&keyptr, keylim, ++q));
+
+    return HAL_OK;
+}
+
+// 5.5.  LMS Signature Verification
+
+static hal_error_t lms_public_key_candidate(const lms_algorithm_t pubtype,
+                                            const lmots_algorithm_t pubotstype,
+                                            const bytestring16 * const I,
+                                            const uint8_t * const msg, const size_t msg_len,
+                                            const uint8_t * const sig, const size_t sig_len,
+                                            bytestring32 * Tc);
+
+static hal_error_t lms_verify(const uint8_t * const key, const size_t key_len,
+                              const uint8_t * const msg, const size_t msg_len,
+                              const uint8_t * const sig, const size_t sig_len)
+{
+    if (key == NULL || msg == NULL || sig == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   Algorithm 6: LMS Signature Verification
+
+//     1. if the public key is not at least eight bytes long, return
+//        INVALID
+
+    if (key_len < 8)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//     2. parse pubtype, I, and T[1] from the public key as follows:
+//
+//        a. pubtype = strTou32(first 4 bytes of public key)
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    lms_algorithm_t pubtype;
+    check(hal_xdr_decode_int(&keyptr, keylim, &pubtype));
+
+//       b. ots_typecode = strTou32(next 4 bytes of public key)
+
+    lmots_algorithm_t pubotstype;
+    check(hal_xdr_decode_int(&keyptr, keylim, &pubotstype));
+
+//        c. set m according to pubtype, based on Table 2
+
+    size_t m = lms_type_to_m(pubtype);
+
+//        d. if the public key is not exactly 24 + m bytes
+//           long, return INVALID
+
+    if (key_len != 24 + m)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//        e. I = next 16 bytes of the public key
+
+    bytestring16 I;
+    check(hal_xdr_decode_bytestring16(&keyptr, keylim, &I));
+
+//        f. T[1] = next m bytes of the public key
+
+    bytestring32 T1;
+    check(hal_xdr_decode_bytestring32(&keyptr, keylim, &T1));
+
+//     3. compute the candidate LMS root value Tc from the signature,
+//        message, identifier and pubtype using Algorithm 6b.
+
+    bytestring32 Tc;
+    check(lms_public_key_candidate(pubtype, pubotstype, &I, msg, msg_len, sig, sig_len, &Tc));
+
+//     4. if Tc is equal to T[1], return VALID; otherwise, return INVALID
+
+    return (memcmp(&Tc, &T1, sizeof(Tc)) ? HAL_ERROR_INVALID_SIGNATURE : HAL_OK);
+}
+
+static hal_error_t lms_public_key_candidate(const lms_algorithm_t pubtype,
+                                            const lmots_algorithm_t pubotstype, // XXX missing from draft
+                                            const bytestring16 * const I,
+                                            const uint8_t * const msg, const size_t msg_len,
+                                            const uint8_t * const sig, const size_t sig_len,
+                                            bytestring32 * Tc)
+{
+//   Algorithm 6b: Computing an LMS Public Key Candidate from a Signature,
+//   Message, Identifier, and algorithm typecode
+
+//  1. if the signature is not at least eight bytes long, return INVALID
+
+    if (sig_len < 8)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//  2. parse sigtype, q, ots_signature, and path from the signature as
+//     follows:
+    const uint8_t *sigptr = sig;
+    const uint8_t * const siglim = sig + sig_len;
+
+//    a. q = strTou32(first 4 bytes of signature)
+
+    uint32_t q;
+    check(hal_xdr_decode_int(&sigptr, siglim, &q));
+
+//    b. otssigtype = strTou32(next 4 bytes of signature)
+
+    lmots_algorithm_t otssigtype;
+    check(hal_xdr_decode_int_peek(&sigptr, siglim, &otssigtype));
+
+//    c. if otssigtype is not the OTS typecode from the public key, return INVALID
+
+    if (otssigtype != pubotstype)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//    d. set n, p according to otssigtype and Table 1; if the
+//    signature is not at least 12 + n * (p + 1) bytes long, return INVALID
+
+    size_t n = lmots_type_to_n(otssigtype);
+    size_t p = lmots_type_to_p(otssigtype);
+    if (sig_len < 12 + n * (p + 1))
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//    e. ots_signature = bytes 8 through 8 + n * (p + 1) - 1 of signature
+
+    /* XXX This is the remainder of ots_signature after otssigtype.
+     * The full ots_signature would be bytes 4 through 8 + n * (p + 1) - 1.
+     */
+
+    const uint8_t * const ots_signature = sigptr;
+    sigptr += lmots_signature_len(otssigtype);
+
+//    f. sigtype = strTou32(4 bytes of signature at location 8 + n * (p + 1))
+
+    lms_algorithm_t sigtype;
+    check(hal_xdr_decode_int(&sigptr, siglim, &sigtype));
+
+//    f. if sigtype is not the LM typecode from the public key, return INVALID
+
+    if (sigtype != pubtype)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//    g. set m, h according to sigtype and Table 2
+
+    size_t m = lms_type_to_m(sigtype);
+    size_t h = lms_type_to_h(sigtype);
+
+//    h. if q >= 2^h or the signature is not exactly 12 + n * (p + 1) + m * h bytes long, return INVALID
+
+    if (q >= (1U << h) || sig_len != 12 + n * (p + 1) + m * h)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//    i. set path as follows:
+//          path[0] = next m bytes of signature
+//          path[1] = next m bytes of signature
+//          ...
+//          path[h-1] = next m bytes of signature
+
+    bytestring32 path[h];
+    for (size_t i = 0; i < h; ++i)
+        check(hal_xdr_decode_bytestring32(&sigptr, siglim, &path[i]));
+
+//  3. Kc = candidate public key computed by applying Algorithm 4b
+//     to the signature ots_signature, the message, and the
+//     identifiers I, q
+
+    bytestring32 Kc;
+    check(lmots_public_key_candidate(pubotstype, I, q, msg, msg_len, ots_signature, lmots_signature_len(otssigtype), &Kc));
+
+//  4. compute the candidate LMS root value Tc as follows:
+//     node_num = 2^h + q
+    size_t r = (1 << h) + q;
+
+//     tmp = H(I || u32str(node_num) || u16str(D_LEAF) || Kc)
+    bytestring32 tmp;
+    SHA256_CTX ctx;
+    checkssl(SHA256_Init(&ctx));
+    checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+    uint32_t l = u32str(r); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+    uint16_t s = u16str(D_LEAF); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+    checkssl(SHA256_Update(&ctx, &Kc, sizeof(Kc)));
+    checkssl(SHA256_Final((unsigned char *)&tmp, &ctx));
+
+//     i = 0
+//     while (node_num > 1) {
+//       if (node_num is odd):
+//         tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||path[i]||tmp)
+//       else:
+//         tmp = H(I||u32str(node_num/2)||u16str(D_INTR)||tmp||path[i])
+//       node_num = node_num/2
+//       i = i + 1
+//     }
+    for (size_t i = 0; r > 1; r /= 2, ++i) {
+        checkssl(SHA256_Init(&ctx));
+        checkssl(SHA256_Update(&ctx, I, sizeof(*I)));
+        uint32_t l = u32str(r/2); checkssl(SHA256_Update(&ctx, &l, sizeof(l)));
+        uint16_t s = u16str(D_INTR); checkssl(SHA256_Update(&ctx, &s, sizeof(s)));
+        if (r & 1) {
+            checkssl(SHA256_Update(&ctx, &path[i], m));
+            checkssl(SHA256_Update(&ctx, &tmp, sizeof(tmp)));
+        }
+        else {
+            checkssl(SHA256_Update(&ctx, &tmp, sizeof(tmp)));
+            checkssl(SHA256_Update(&ctx, &path[i], m));
+        }
+        checkssl(SHA256_Final((unsigned char *)&tmp, &ctx));
+    }
+
+//     Tc = tmp
+    memcpy(Tc, &tmp, sizeof(*Tc));
+
+    return HAL_OK;
+}
+
+/* ---------------------------------------------------------------- */
+
+//6.  Hierarchical signatures
+
+// 6.1.  Key Generation
+
+static inline size_t hss_private_key_len(const size_t L, const lms_algorithm_t lms_type, const lmots_algorithm_t lmots_type)
+{
+    /* u32str(L) || lms_priv[0] || lms_priv[1] || ... || lms_priv[L-1] || sig[0] || pub[1] || ... || sig[L-2] || pub[L-1] */
+    return (4 + (L * lms_private_key_len(lms_type, lmots_type)) +
+            ((L - 1) * (lms_signature_len(lms_type, lmots_type) + lms_public_key_len(lms_type))));
+}
+
+static hal_error_t hss_generate_private_key(const size_t L,
+                                            const lms_algorithm_t lms_type,
+                                            const lmots_algorithm_t lmots_type,
+                                            uint8_t * const key, size_t * const key_len, const size_t key_max)
+{
+    if (L == 0 || L > 8 ||
+        lms_type < lms_sha256_n32_h5 || lms_type > lms_sha256_n32_h25 ||
+        lmots_type < lmots_sha256_n32_w1 || lmots_type > lmots_sha256_n32_w8 ||
+        key == NULL || key_max < hss_private_key_len(L, lms_type, lmots_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_max;
+
+//   The HSS private key consists of prv[0], ... , prv[L-1].
+    /* XXX This omits L */
+
+    check(hal_xdr_encode_int(&keyptr, keylim, L));
+
+    size_t lms_prv_len = lms_private_key_len(lms_type, lmots_type);
+    size_t lms_pub_len = lms_public_key_len(lms_type);
+    size_t lms_sig_len = lms_signature_len(lms_type, lmots_type);
+
+    for (size_t i = 0; i < L; ++i, keyptr += lms_prv_len) {
+        check(lms_generate_private_key(lms_type, lmots_type, keyptr, NULL, lms_prv_len));
+        if (i > 0) {
+            /* Generate the public key, and sign it with the previous private
+             * key. Stash the signed public keys at the end of the private
+             * key file, so we can copy them wholesale into the signature.
+             */
+            uint8_t *sigptr = key + 4 + (L * lms_prv_len) + ((i - 1) * (lms_sig_len + lms_pub_len));
+            uint8_t *pubptr = sigptr + lms_sig_len;
+            check(lms_generate_public_key(keyptr, lms_prv_len, pubptr, NULL, lms_pub_len));
+            check(lms_sign(keyptr - lms_prv_len, lms_prv_len, pubptr, lms_pub_len, sigptr, NULL, lms_sig_len));
+        }
+    }
+    keyptr += (L - 1) * (lms_sig_len + lms_pub_len);
+
+    if (key_len != NULL)
+        *key_len = keyptr - key;
+
+    return HAL_OK;
+}
+
+static inline size_t hss_public_key_len(const lms_algorithm_t lms_type)
+{
+    /* L || pub[0] */
+    return (sizeof(uint32_t) + lms_public_key_len(lms_type));
+}
+
+static hal_error_t hss_generate_public_key(const size_t L,
+                                           const lms_algorithm_t lms_type,
+                                           const lmots_algorithm_t lmots_type,
+                                           const uint8_t * const key, const size_t key_len,
+                                           uint8_t *pub, size_t *pub_len, const size_t pub_max)
+{
+    if (key == NULL || key_len < hss_private_key_len(L, lms_type, lmots_type) ||
+        pub == NULL || pub_max < hss_public_key_len(lms_type))
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+    uint8_t *pubptr = pub;
+    const uint8_t * const publim = pub + pub_max;
+
+//   The public key of the HSS scheme consists of the number of levels L,
+//   followed by pub[0], the public key of the top level.
+
+    uint32_t L_key;
+    check(hal_xdr_decode_int(&keyptr, keylim, &L_key));
+    if ((size_t)L_key != L)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    check(hal_xdr_encode_int(&pubptr, publim, L));
+
+    /* generate the lms public key from the first lms private key */
+    size_t len;
+    check(lms_generate_public_key(keyptr, lms_private_key_len(lms_type, lmots_type), pubptr, &len, publim - pubptr));
+    pubptr += len;
+
+    if (pub_len != NULL)
+        *pub_len = pubptr - pub;
+
+    return HAL_OK;
+}
+
+//6.2.  Signature Generation
+
+static inline size_t hss_signature_len(const size_t L, const lms_algorithm_t lms_type, const lmots_algorithm_t lmots_type)
+{
+    /* u32str(Nspk) || sig[0] || pub[1] || ... || sig[Nspk-1] || pub[Nspk] || sig[Nspk] */
+    return (sizeof(uint32_t) +
+            (L * lms_signature_len(lms_type, lmots_type)) +
+            ((L - 1) * lms_public_key_len(lms_type)));
+}
+
+static hal_error_t hss_sign(const uint8_t * const key, const size_t key_len,
+                            const uint8_t * const msg, const size_t msg_len,
+                            uint8_t *sig, size_t *sig_len, const size_t sig_max)
+{
+    if (key == NULL || msg == NULL || sig == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+
+    /* private key format:
+     * u32str(L) || lms_priv[0] || lms_priv[1] || ... || lms_priv[L-1]
+     */
+
+    uint32_t L;
+    check(hal_xdr_decode_int(&keyptr, keylim, &L));
+
+    /* peek into the first lms private key for lms and lmots types */
+    lms_algorithm_t lms_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lms_type));
+    keyptr += 4;	/* skip over q */
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+    size_t h = lms_type_to_h(lms_type);
+    size_t lms_prv_len = lms_private_key_len(lms_type, lmots_type);
+    size_t lms_pub_len = lms_public_key_len(lms_type);
+    size_t lms_sig_len = lms_signature_len(lms_type, lmots_type);
+
+//   To sign a message using the private key prv, the following steps are
+//   performed:
+//
+//      If prv[L-1] is exhausted, then determine the smallest integer d
+//      such that all of the private keys prv[d], prv[d+1], ... , prv[L-1]
+//      are exhausted.  If d is equal to zero, then the HSS key pair is
+//      exhausted, and it MUST NOT generate any more signatures.
+//      Otherwise, the key pairs for levels d through L-1 must be
+//      regenerated during the signature generation process, as follows.
+//      For i from d to L-1, a new LMS public and private key pair with a
+//      new identifier is generated, pub[i] and prv[i] are set to those
+//      values, then the public key pub[i] is signed with prv[i-1], and
+//      sig[i-1] is set to the resulting value.
+
+    uint32_t q;
+    keyptr = key + 4 + ((L - 1) * lms_prv_len) + 4;
+    check(hal_xdr_decode_int_peek(&keyptr, keylim, &q));
+    if (q >= (1U << h)) {
+        size_t d;
+        for (d = L - 1; d > 0; --d) {
+            keyptr = key + 4 + ((d -1) * lms_prv_len) + 4;
+            check(hal_xdr_decode_int_peek(&keyptr, keylim, &q));
+            if (q < (1U << h))
+                break;
+        }
+        if (d == 0)
+            return HAL_ERROR_HASHSIG_KEY_EXHAUSTED;
+        for ( ; d < L; ++d) {
+            keyptr = key + 4 + (d * lms_prv_len);
+            /* generate the new private key */
+            check(lms_generate_private_key(lms_type, lmots_type, (uint8_t * const)keyptr, NULL, lms_prv_len));
+            /* generate the new signed public key, after all the private keys */
+            uint8_t *sigptr = (uint8_t *)key + 4 + (L * lms_prv_len) + ((d - 1) * (lms_sig_len + lms_pub_len));
+            uint8_t *pubptr = sigptr + lms_pub_len;
+            check(lms_generate_public_key(keyptr, lms_prv_len, pubptr, NULL, lms_pub_len));
+            check(lms_sign(keyptr - lms_prv_len, lms_prv_len, pubptr, lms_pub_len, sigptr, NULL, lms_sig_len));
+        }
+    }
+
+    uint8_t *sigptr = sig;
+    const uint8_t * const siglim = sig + sig_max;
+    check(hal_xdr_encode_int(&sigptr, siglim, L - 1));
+
+//      The message is signed with prv[L-1], and the value sig[L-1] is set
+//      to that result.
+//
+//      The value of the HSS signature is set as follows.  We let
+//      signed_pub_key denote an array of octet strings, where
+//      signed_pub_key[i] = sig[i] || pub[i+1], for i between 0 and Nspk-
+//      1, inclusive, where Nspk = L-1 denotes the number of signed public
+//      keys.  Then the HSS signature is u32str(Nspk) ||
+//      signed_pub_key[0] || ... || signed_pub_key[Nspk-1] || sig[Nspk].
+
+    /* copy the precomputed signed public keys from the end of the private key */
+    keyptr = key + 4 + (L * lms_prv_len);
+    check(hal_xdr_encode_fixed_opaque(&sigptr, siglim, keyptr, (L - 1) * (lms_sig_len + lms_pub_len)));
+
+    /* sign the message with the last lms private key */
+    keyptr -= lms_prv_len;
+    check(lms_sign(keyptr, lms_prv_len, msg, msg_len, sigptr, NULL, lms_sig_len));
+    sigptr += lms_sig_len;
+
+    if (sig_len != NULL)
+        *sig_len = sigptr - sig;
+
+    return HAL_OK;
+}
+
+//6.3.  Signature Verification
+
+static hal_error_t hss_verify(const uint8_t * const pub, const size_t pub_len,
+                              const uint8_t * const message, const size_t message_len,
+                              const uint8_t * const signature, const size_t signature_len)
+{
+    if (pub == NULL || message == NULL || signature == NULL)
+        return HAL_ERROR_BAD_ARGUMENTS;
+
+//   To verify a signature sig and message using the public key pub, the
+//   following steps are performed:
+//
+//      The signature S is parsed into its components as follows:
+
+//      Nspk = strTou32(first four bytes of S)
+
+    const uint8_t * sigptr = signature;
+    const uint8_t * const siglim = signature + signature_len;
+    uint32_t Nspk;
+    check(hal_xdr_decode_int(&sigptr, siglim, &Nspk));
+
+//      if Nspk+1 is not equal to the number of levels L in pub:
+//         return INVALID
+
+    const uint8_t * pubptr = pub;
+    const uint8_t * const publim = pub + pub_len;
+    uint32_t L;
+    check(hal_xdr_decode_int(&pubptr, publim, &L));
+    if (Nspk + 1 != L)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//      key = pub
+    const uint8_t *key = pubptr; size_t key_len = pub_len - sizeof(uint32_t);
+    const uint8_t *sig; size_t sig_len;
+    const uint8_t *msg; size_t msg_len;
+
+//      for (i = 0; i < Nspk; i = i + 1) {
+    for (size_t i = 0; i < Nspk; ++i) {
+
+        /* peek into the lms public key for lms and lmots types */
+        const uint8_t *keyptr = key;
+        const uint8_t * const keylim = key + key_len;
+        lms_algorithm_t lms_type;
+        check(hal_xdr_decode_int(&keyptr, keylim, &lms_type));
+        lmots_algorithm_t lmots_type;
+        check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+//         sig = next LMS signature parsed from S
+//         msg = next LMS public key parsed from S
+        sig = sigptr; sig_len = lms_signature_len(lms_type, lmots_type); sigptr += sig_len;
+        msg = sigptr; msg_len = lms_public_key_len(lms_type); sigptr += msg_len;
+
+        if (sigptr > siglim)
+            return HAL_ERROR_INVALID_SIGNATURE;
+
+//         if (lms_verify(msg, key, sig) != VALID):
+//             return INVALID
+        check(lms_verify(key, key_len, msg, msg_len, sig, sig_len));
+
+//         key = msg
+        key = msg; key_len = msg_len;
+//      }
+    }
+
+    /* peek into the lms public key for lms and lmots types */
+    const uint8_t *keyptr = key;
+    const uint8_t * const keylim = key + key_len;
+    lms_algorithm_t lms_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lms_type));
+    lmots_algorithm_t lmots_type;
+    check(hal_xdr_decode_int(&keyptr, keylim, &lmots_type));
+
+//      sig = next LMS signature parsed from S
+    sig = sigptr; sig_len = lms_signature_len(lms_type, lmots_type); sigptr += sig_len;
+
+    if (sigptr != siglim)
+        return HAL_ERROR_INVALID_SIGNATURE;
+
+//      return lms_verify(message, key, sig)
+    check(lms_verify(key, key_len, message, message_len, sig, sig_len));
+
+    return HAL_OK;
+}
+
+/* ---------------------------------------------------------------- */
+
+/*
+ * driver code, roughly like hash-sigs-c/demo.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static void hexdump(const char * const label, const void * const buf, const size_t len)
+{
+    printf("%-11s ", label);
+
+    const uint8_t *b = buf;
+    for (size_t i = 0; i < len; ++i, ++b) {
+        printf("%02x", *b);
+        if ((i & 0x0f) == 0x0f) {
+            printf("\n");
+            if (i < len - 1)
+                printf("            ");
+        }
+    }
+    if ((len & 0x0f) != 0)
+        printf("\n");
+}
+
+static hal_error_t dump_lmots_prv(const uint8_t * const buf, size_t * const len, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("lmots type", bufptr, 4);
+    uint32_t lmots_type;
+    check(hal_xdr_decode_int(&bufptr, buflim, &lmots_type));
+    hexdump("I", bufptr, 16);
+    bufptr += 16;
+    hexdump("q", bufptr, 4);
+    bufptr += 4;
+    size_t p = lmots_type_to_p(lmots_type);
+    for (size_t i = 0; i < p && bufptr < buflim; ++i) {
+        char label[16];
+        sprintf(label, "x[%lu]", i);
+        hexdump(label, bufptr, 32);
+        bufptr += 32;
+    }
+
+    if (len != NULL)
+        *len = bufptr - buf;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_lmots_sig(const uint8_t * const buf, size_t * const len, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("lmots type", bufptr, 4);
+    uint32_t lmots_type;
+    check(hal_xdr_decode_int(&bufptr, buflim, &lmots_type));
+    hexdump("C", bufptr, 32);
+    bufptr += 32;
+    size_t p = lmots_type_to_p(lmots_type);
+    for (size_t i = 0; i < p && bufptr < buflim; ++i) {
+        char label[16];
+        sprintf(label, "y[%lu]", i);
+        hexdump(label, bufptr, 32);
+        bufptr += 32;
+    }
+
+    if (len != NULL)
+        *len = bufptr - buf;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_lms_prv(const uint8_t * const buf, size_t * const len, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("lms type", bufptr, 4);
+    uint32_t lms_type;
+    check(hal_xdr_decode_int(&bufptr, buflim, &lms_type));
+    hexdump("q", bufptr, 4);
+    uint32_t q;
+    check(hal_xdr_decode_int(&bufptr, buflim, &q));
+    size_t h = lms_type_to_h(lms_type);
+    for (size_t i = 0; i < (1U << h) && bufptr < buflim; ++i) {
+        printf("--------------------------------------------\notsprv[%lu]\n", i);
+        size_t lmots_len;
+        check(dump_lmots_prv(bufptr, &lmots_len, buflim - bufptr));
+        bufptr += lmots_len;
+    }
+
+    if (len != NULL)
+        *len = bufptr - buf;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_lms_pub(const uint8_t * const buf, size_t * const len, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("lms type", bufptr, 4);
+    uint32_t lms_type;
+    check(hal_xdr_decode_int(&bufptr, buflim, &lms_type));
+    hexdump("lmots type", bufptr, 4);
+    bufptr += 4;
+    hexdump("I", bufptr, 16);
+    bufptr += 16;
+    hexdump("T[1]", bufptr, 32);
+    bufptr += 32;
+
+    if (len != NULL)
+        *len = bufptr - buf;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_lms_sig(const uint8_t * const buf, size_t * const len, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("q", bufptr, 4);
+    bufptr += 4;
+    size_t lmots_len;
+    check(dump_lmots_sig(bufptr, &lmots_len, buflim - bufptr));
+    bufptr += lmots_len;
+    hexdump("lms type", bufptr, 4);
+    uint32_t lms_type;
+    check(hal_xdr_decode_int(&bufptr, buflim, &lms_type));
+    size_t h = lms_type_to_h(lms_type);
+    for (size_t i = 0; i < h && bufptr < buflim; ++i) {
+        char label[16];
+        sprintf(label, "path[%lu]", i);
+        hexdump(label, bufptr, 32);
+        bufptr += 32;
+    }
+
+    if (len != NULL)
+        *len = bufptr - buf;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_hss_prv(const uint8_t * const buf, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+    size_t len;
+
+    hexdump("L", bufptr, 4);
+    uint32_t L;
+    check(hal_xdr_decode_int(&bufptr, buflim, &L));
+
+    for (size_t i = 0; i < L && bufptr < buflim; ++i) {
+        printf("--------------------------------------------\nlmsprv[%lu]\n", i);
+        check(dump_lms_prv(bufptr, &len, buflim - bufptr));
+        bufptr += len;
+    }
+
+    printf("--------------------------------------------\n");
+    for (size_t i = 0; i < L - 1 && bufptr < buflim; ++i) {
+        printf("--------------------------------------------\nsig[%lu]\n", i);
+        check(dump_lms_sig(bufptr, &len, buflim - bufptr));
+        bufptr += len;
+        printf("--------------------------------------------\npub[%lu]\n", i + 1);
+        check(dump_lms_pub(bufptr, &len, buflim - bufptr));
+        bufptr += len;
+    }
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_hss_pub(const uint8_t * const buf, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("L", bufptr, 4);
+    uint32_t L;
+    check(hal_xdr_decode_int(&bufptr, buflim, &L));
+
+    printf("--------------------------------------------\npubkey[0]\n");
+    size_t lms_len;
+    check(dump_lms_pub(bufptr, &lms_len, buflim - bufptr));
+    bufptr += lms_len;
+
+    return HAL_OK;
+}
+
+static hal_error_t dump_hss_sig(const uint8_t * const buf, const size_t max)
+{
+    const uint8_t *bufptr = buf;
+    const uint8_t * const buflim = buf + max;
+
+    hexdump("Nspk", bufptr, 4);
+    uint32_t Nspk;
+    check(hal_xdr_decode_int(&bufptr, buflim, &Nspk));
+
+    for (size_t i = 0; i <= Nspk && bufptr < buflim; ++i) {
+        printf("--------------------------------------------\nsig[%lu]\n", i);
+        size_t lms_len;
+        check(dump_lms_sig(bufptr, &lms_len, buflim - bufptr));
+        bufptr += lms_len;
+        if (bufptr < buflim) {
+            printf("--------------------------------------------\npubkey[%lu]\n", i + 1);
+            check(dump_lms_pub(bufptr, &lms_len, buflim - bufptr));
+            bufptr += lms_len;
+        }
+    }
+
+    return HAL_OK;
+}
+
+int main(int argc, char *argv[])
+{
+    char usage[] = "\
+Usage: %s genkey <keyname> [parameters]\n\
+       %s sign <keyname> <files to sign>\n\
+       %s verify <keyname> <files to sign>\n";
+
+    int fd;
+    struct stat statbuf;
+    size_t len;
+    hal_error_t err;
+
+    if (argc < 3) {
+        fprintf(stderr, usage, argv[0], argv[0], argv[0]);
+        return 1;
+    }
+
+    if (strcmp(argv[1], "genkey") == 0) {
+        /* default parameter set */
+        size_t L = 2;
+        lms_algorithm_t lms_type = lms_sha256_n32_h5;
+        lmots_algorithm_t lmots_type = lmots_sha256_n32_w8;
+        if (argc > 3) {
+            int val[3];
+            for (int i = 0; i < 3; ++i) {
+                char *str = strtok(i == 0 ? argv[3] : NULL, "/");
+                if (str == NULL) {
+                    fprintf(stderr, "genkey parameters are of the form \"L/h/w\", e.g. \"3/10/4\"\n");
+                    return 1;
+                }
+                val[i] = atoi(str);
+            }
+            L = (size_t)val[0];
+            if (L < 1 || L > 8) {
+                fprintf(stderr, "unsupported value of L (%d), must be 1-8\n", val[0]);
+                return 1;
+            }
+            switch (val[1]) {
+            case 5: lms_type = lms_sha256_n32_h5; break;
+            case 10: lms_type = lms_sha256_n32_h10; break;
+            case 15: lms_type = lms_sha256_n32_h15; break;
+            case 20: lms_type = lms_sha256_n32_h20; break;
+            case 25: lms_type = lms_sha256_n32_h25; break;
+            default:
+                fprintf(stderr, "unsupported value of h (%d), must be 5,10,15,20,25\n", val[1]);
+                return 1;
+            }
+            switch (val[2]) {
+            case 1: lmots_type = lmots_sha256_n32_w1; break;
+            case 2: lmots_type = lmots_sha256_n32_w2; break;
+            case 4: lmots_type = lmots_sha256_n32_w4; break;
+            case 8: lmots_type = lmots_sha256_n32_w8; break;
+            default:
+                fprintf(stderr, "unsupported value of w (%d), must be 1,2,4,8\n", val[2]);
+                return 1;
+            }
+        }
+        uint8_t prv[hss_private_key_len(L, lms_type, lmots_type)];
+        size_t prv_len;
+        if ((err = hss_generate_private_key(L, lms_type, lmots_type, prv, &prv_len, sizeof(prv))) != HAL_OK) {
+            fprintf(stderr, "hss_generate_private_key: %s\n", hal_error_string(err));
+            return 1;
+        }
+        if (prv_len != sizeof(prv)) {
+            fprintf(stderr, "hss_generate_private_key returned length %lu, expected %lu\n", prv_len, sizeof(prv));
+            return 1;
+        }
+        char fn[strlen(argv[2]) + 5];
+        strcpy(fn, argv[2]);
+        strcat(fn, ".prv");
+        if ((fd = creat(fn, S_IRUSR|S_IWUSR)) == -1) {
+            perror("creat prv");
+            return 1;
+        }
+        if ((len = write(fd, prv, sizeof(prv))) != sizeof(prv)) {
+            fprintf(stderr, "write prv returned %lu, expected %lu\n", len, sizeof(prv));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close prv");
+            return 1;
+        }
+        uint8_t pub[hss_public_key_len(lms_type)];
+        if ((err = hss_generate_public_key(L, lms_type, lmots_type, prv, sizeof(prv), pub, &len, sizeof(pub))) != HAL_OK) {
+            fprintf(stderr, "hss_generate_public_key: %s\n", hal_error_string(err));
+            return 1;
+        }
+        if (len != sizeof(pub)) {
+            fprintf(stderr, "hss_generate_public_key returned length %lu, expected %lu\n", len, sizeof(pub));
+            return 1;
+        }
+        strcpy(fn, argv[2]);
+        strcat(fn, ".pub");
+        if ((fd = creat(fn, S_IRUSR|S_IWUSR)) == -1) {
+            perror("creat pub");
+            return 1;
+        }
+        if ((len = write(fd, pub, sizeof(pub))) != sizeof(pub)) {
+            fprintf(stderr, "write pub returned %lu, expected %lu\n", len, sizeof(pub));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close pub");
+            return 1;
+        }
+        return 0;
+    }
+
+    else if (strcmp(argv[1], "sign") == 0) {
+        if (argc < 4) {
+            fprintf(stderr, usage, argv[0], argv[0], argv[0]);
+            return 1;
+        }
+        char fn[strlen(argv[2]) + 5];
+        strcpy(fn, argv[2]);
+        strcat(fn, ".prv");
+        if (stat(fn, &statbuf) != 0) {
+            perror("stat prv");
+            return 1;
+        }
+        if ((fd = open(fn, O_RDONLY)) == -1) {
+            perror("open prv");
+            return 1;
+        }
+        uint8_t prv[statbuf.st_size];
+        if ((len = read(fd, prv, sizeof(prv))) != sizeof(prv)) {
+            fprintf(stderr, "read prv returned %lu, expected %lu\n", len, sizeof(prv));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close prv");
+            return 1;
+        }
+        strcpy(fn, argv[2]);
+        strcat(fn, ".pub");
+        if (stat(fn, &statbuf) != 0) {
+            perror("stat pub");
+            return 1;
+        }
+        if ((fd = open(fn, O_RDONLY)) == -1) {
+            perror("open pub");
+            return 1;
+        }
+        uint8_t pub[statbuf.st_size];
+        if ((len = read(fd, pub, sizeof(pub))) != sizeof(pub)) {
+            fprintf(stderr, "read pub returned %lu, expected %lu\n", len, sizeof(pub));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close pub");
+            return 1;
+        }
+        for (int i = 3; i < argc; ++i) {
+            char *fn = argv[i];
+            printf("signing %s\n", fn);
+            if (stat(fn, &statbuf) != 0) {
+                perror("stat msg");
+                return 1;
+            }
+            if ((fd = open(fn, O_RDONLY)) == -1) {
+                perror("open msg");
+                return 1;
+            }
+            uint8_t msg[statbuf.st_size];
+            if ((len = read(fd, msg, sizeof(msg))) != sizeof(msg)) {
+                fprintf(stderr, "read msg returned %lu, expected %lu\n", len, sizeof(msg));
+                return 1;
+            }
+            if (close(fd) != 0) {
+                perror("close msg");
+                return 1;
+            }
+            const uint8_t * pubptr = pub;
+            const uint8_t * const publim = pub + sizeof(pub);
+            uint32_t L;
+            check(hal_xdr_decode_int(&pubptr, publim, &L));
+            lms_algorithm_t lms_type;
+            check(hal_xdr_decode_int(&pubptr, publim, &lms_type));
+            lmots_algorithm_t lmots_type;
+            check(hal_xdr_decode_int(&pubptr, publim, &lmots_type));
+            uint8_t sig[hss_signature_len(L, lms_type, lmots_type)];
+            if ((err = hss_sign(prv, sizeof(prv), msg, sizeof(msg), sig, &len, sizeof(sig))) != HAL_OK) {
+                fprintf(stderr, "hss_sign: %s\n", hal_error_string(err));
+                return 1;
+            }
+            if (len != sizeof(sig)) {
+                fprintf(stderr, "hss_sign returned length %lu, expected %lu\n", len, sizeof(sig));
+                return 1;
+            }
+            char sn[strlen(fn) + 5];
+            strcpy(sn, fn);
+            strcat(sn, ".sig");
+            if ((fd = creat(sn, S_IRUSR|S_IWUSR)) == -1) {
+                perror("creat sig");
+                return 1;
+            }
+            if ((len = write(fd, sig, sizeof(sig))) != sizeof(sig)) {
+                fprintf(stderr, "write sig returned %lu, expected %lu\n", len, sizeof(sig));
+                return 1;
+            }
+            if (close(fd) != 0) {
+                perror("close sig");
+                return 1;
+            }
+        }
+        strcpy(fn, argv[2]);
+        strcat(fn, ".prv");
+        if ((fd = creat(fn, S_IRUSR|S_IWUSR)) == -1) {
+            perror("creat prv");
+            return 1;
+        }
+        if ((len = write(fd, prv, sizeof(prv))) != sizeof(prv)) {
+            fprintf(stderr, "write prv returned %lu, expected %lu\n", len, sizeof(prv));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close prv");
+            return 1;
+        }
+        return 0;
+    }
+
+    else if (strcmp(argv[1], "verify") == 0) {
+        if (argc < 4) {
+            fprintf(stderr, usage, argv[0], argv[0], argv[0]);
+            return 1;
+        }
+        char fn[strlen(argv[2]) + 5];
+        strcpy(fn, argv[2]);
+        strcat(fn, ".pub");
+        if (stat(fn, &statbuf) != 0) {
+            perror("stat pub");
+            return 1;
+        }
+        if ((fd = open(fn, O_RDONLY)) == -1) {
+            perror("open pub");
+            return 1;
+        }
+        uint8_t pub[statbuf.st_size];
+        if ((len = read(fd, pub, sizeof(pub))) != sizeof(pub)) {
+            fprintf(stderr, "read pub returned %lu, expected %lu\n", len, sizeof(pub));
+            return 1;
+        }
+        if (close(fd) != 0) {
+            perror("close pub");
+            return 1;
+        }
+        int ret = 0;
+        for (int i = 3; i < argc; ++i) {
+            char *fn = argv[i];
+            printf("verifying %s\n", fn);
+            if (stat(fn, &statbuf) != 0) {
+                perror("stat msg");
+                return 1;
+            }
+            if ((fd = open(fn, O_RDONLY)) == -1) {
+                perror("open msg");
+                return 1;
+            }
+            uint8_t msg[statbuf.st_size];
+            if ((len = read(fd, msg, sizeof(msg))) != sizeof(msg)) {
+                fprintf(stderr, "read msg returned %lu, expected %lu\n", len, sizeof(msg));
+                return 1;
+            }
+            if (close(fd) != 0) {
+                perror("close msg");
+                return 1;
+            }
+            char sn[strlen(fn) + 5];
+            strcpy(sn, fn);
+            strcat(sn, ".sig");
+            if (stat(sn, &statbuf) != 0) {
+                perror("stat sig");
+                return 1;
+            }
+            uint8_t sig[statbuf.st_size];
+            if ((fd = open(sn, O_RDONLY)) == -1) {
+                perror("open sig");
+                return 1;
+            }
+            if ((len = read(fd, sig, sizeof(sig))) != sizeof(sig)) {
+                fprintf(stderr, "write sig returned %lu, expected %lu\n", len, sizeof(sig));
+                return 1;
+            }
+            if (close(fd) != 0) {
+                perror("close sig");
+                return 1;
+            }
+            if ((err = hss_verify(pub, sizeof(pub), msg, sizeof(msg), sig, sizeof(sig))) != HAL_OK) {
+                fprintf(stderr, "hss_verify: %s\n", hal_error_string(err));
+                ret++;
+            }
+            else
+                printf("signature verified\n");
+        }
+        return ret;
+    }
+
+    else if (strcmp(argv[1], "read") == 0) {
+        if (argc < 3) {
+            fprintf(stderr, usage, argv[0], argv[0], argv[0]);
+            return 1;
+        }
+        for (int i = 2; i < argc; ++i) {
+            char *fn = argv[i];
+            printf("reading %s\n", fn);
+            if (stat(fn, &statbuf) != 0) {
+                perror("stat");
+                return 1;
+            }
+            if ((fd = open(fn, O_RDONLY)) == -1) {
+                perror("open");
+                return 1;
+            }
+            uint8_t buf[statbuf.st_size];
+            if ((len = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
+                fprintf(stderr, "read returned %lu, expected %lu\n", len, sizeof(buf));
+                return 1;
+            }
+            if (close(fd) != 0) {
+                perror("close");
+                return 1;
+            }
+            char *ext = strrchr(fn, '.');
+            if (strcmp(ext, ".prv") == 0) {
+                if (dump_hss_prv(buf, sizeof(buf)) != HAL_OK)
+                    return 1;
+            }
+            else if (strcmp(ext, ".pub") == 0) {
+                if (dump_hss_pub(buf, sizeof(buf)) != HAL_OK)
+                    return 1;
+            }
+            else if (strcmp(ext, ".sig") == 0) {
+                if (dump_hss_sig(buf, sizeof(buf)) != HAL_OK)
+                    return 1;
+            }
+            else {
+                fprintf(stderr, "unknown file type\n");
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    fprintf(stderr, usage, argv[0], argv[0], argv[0]);
+    return 1;
+}



More information about the Commits mailing list