[Cryptech-Commits] [sw/libhal] 07/11: Add ASN.1 support for public keys (X.509 SubjectPublicKeyInfo format).

git at cryptech.is git at cryptech.is
Wed Dec 23 07:22:26 UTC 2015


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

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

commit 17da006ef0dcb18bf8da6ab6b90446c43a10c790
Author: Rob Austein <sra at hactrn.net>
Date:   Tue Dec 22 18:55:11 2015 -0500

    Add ASN.1 support for public keys (X.509 SubjectPublicKeyInfo format).
---
 .gitignore         |   1 +
 asn1.c             | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 asn1_internal.h    |  10 ++++
 ecdsa.c            | 121 +++++++++++++++++++++++++++++++++++++--
 hal.h              |  24 +++++++-
 hal_internal.h     |   2 +-
 rpc.c              |   6 +-
 rpc_client.c       |   2 +-
 rpc_pkey.c         |  99 +++++++++++++++++++++++++++++---
 rsa.c              |  96 ++++++++++++++++++++++++++++++-
 tests/test-ecdsa.c |   2 +-
 11 files changed, 502 insertions(+), 24 deletions(-)

diff --git a/.gitignore b/.gitignore
index cdc0f27..32df1cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ tests/test-ecdsa
 tests/test-hash
 tests/test-pbkdf2
 tests/test-rsa
+tests/test-rsa-*.der
 tests/test-trng
 utils/cores
 utils/eim_peek_poke
diff --git a/asn1.c b/asn1.c
index 0794537..3786cbd 100644
--- a/asn1.c
+++ b/asn1.c
@@ -153,6 +153,85 @@ hal_error_t hal_asn1_encode_integer(const fp_int * const bn,
 }
 
 /*
+ * Encode a public key into an RFC 5280 SubjectPublicKeyInfo.
+ */
+
+hal_error_t hal_asn1_encode_spki(const uint8_t * const alg_oid,   const size_t alg_oid_len,
+                                 const uint8_t * const curve_oid, const size_t curve_oid_len,
+                                 const uint8_t * const pubkey,    const size_t pubkey_len,
+                                 uint8_t *der, size_t *der_len, const size_t der_max)
+{
+  if (alg_oid == NULL || alg_oid_len == 0 || pubkey == NULL || pubkey_len == 0 || (curve_oid == NULL && curve_oid_len != 0))
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  const uint8_t curve_oid_tag = curve_oid == NULL ? ASN1_NULL : ASN1_OBJECT_IDENTIFIER;
+
+  hal_error_t err;
+
+  size_t hlen, hlen_spki, hlen_algid, hlen_alg, hlen_curve, hlen_bit;
+
+  if ((err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, alg_oid_len,   NULL, &hlen_alg,   0)) != HAL_OK ||
+      (err = hal_asn1_encode_header(curve_oid_tag,          curve_oid_len, NULL, &hlen_curve, 0)) != HAL_OK ||
+      (err = hal_asn1_encode_header(ASN1_BIT_STRING,        pubkey_len,    NULL, &hlen_bit,   0)) != HAL_OK)
+    return err;
+
+  const size_t algid_len = hlen_alg + alg_oid_len + hlen_curve + curve_oid_len;
+
+  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE,          algid_len,     NULL, &hlen_algid, 0)) != HAL_OK)
+    return err;
+
+  const size_t vlen = hlen_algid + hlen_alg + alg_oid_len + hlen_curve + curve_oid_len + hlen_bit + pubkey_len;
+
+  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE,          vlen,          NULL, &hlen_spki,  0)) != HAL_OK)
+    return err;
+
+  /*
+   * Handle pubkey early, in case it was staged into our output buffer.
+   */
+  if (der != NULL && hlen_spki + vlen <= der_max)
+    memmove(der + hlen_spki + vlen - pubkey_len, pubkey, pubkey_len);
+
+  err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, der, &hlen, der_max);
+
+  if (der_len != NULL)
+    *der_len = hlen + vlen;
+
+  if (der == NULL || err != HAL_OK)
+    return err;
+
+  uint8_t *d = der + hlen;
+  memset(d, 0, vlen);
+
+  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE, algid_len, d, &hlen, der + der_max - d)) != HAL_OK)
+    return err;
+  d += hlen;
+
+  if ((err = hal_asn1_encode_header(ASN1_OBJECT_IDENTIFIER, alg_oid_len, d, &hlen, der + der_max - d)) != HAL_OK)
+    return err;
+  d += hlen;
+  memcpy(d, alg_oid, alg_oid_len);
+  d += alg_oid_len;
+
+  if ((err = hal_asn1_encode_header(curve_oid_tag, curve_oid_len, d, &hlen, der + der_max - d)) != HAL_OK)
+    return err;
+  d += hlen;
+  if (curve_oid != NULL)
+    memcpy(d, curve_oid, curve_oid_len);
+  d += curve_oid_len;
+
+  if ((err = hal_asn1_encode_header(ASN1_BIT_STRING, pubkey_len, d, &hlen, der + der_max - d)) != HAL_OK)
+    return err;
+  d += hlen;
+
+  d += pubkey_len;              /* pubkey handled early, above. */
+
+  assert(d == der + hlen_spki + vlen);
+  assert(d <= der + der_max);
+
+  return HAL_OK;
+}
+
+/*
  * Parse tag and length of an ASN.1 object.  Tag must match value
  * specified by the caller.  On success, sets hlen and vlen to lengths
  * of header and value, respectively.
@@ -219,6 +298,90 @@ hal_error_t hal_asn1_decode_integer(fp_int *bn,
 }
 
 /*
+ * Decode a public key from an RFC 5280 SubjectPublicKeyInfo.
+ */
+
+hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
+                                 const uint8_t **curve_oid, size_t *curve_oid_len,
+                                 const uint8_t **pubkey,    size_t *pubkey_len,
+                                 const uint8_t *const der,  const size_t der_len)
+{
+  if (alg_oid == NULL || alg_oid_len == NULL || curve_oid == NULL || curve_oid_len == NULL ||
+      pubkey == NULL || pubkey_len == NULL || der == NULL)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  size_t hlen, vlen;
+  hal_error_t err;
+
+  if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, der, der_len, &hlen, &vlen)) != HAL_OK)
+    return err;
+
+  const uint8_t * const der_end = der + hlen + vlen;
+  const uint8_t *d = der + hlen;
+
+  if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, der, der_end - d, &hlen, &vlen)) != HAL_OK)
+    return err;
+  d += hlen;
+
+  const uint8_t * const algid_end = d + vlen;
+
+  if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
+    return err;
+  d += hlen;
+  if (vlen > algid_end - d)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+  *alg_oid = d;
+  *alg_oid_len = vlen;
+  d += vlen;
+
+  *curve_oid = NULL;
+  *curve_oid_len = 0;
+    
+  if (d < algid_end) {
+    switch (*d) {
+
+    case ASN1_OBJECT_IDENTIFIER:
+      if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
+        return err;
+      d += hlen;
+      if (vlen > algid_end - d)
+        return HAL_ERROR_ASN1_PARSE_FAILED;
+      *curve_oid = d;
+      *curve_oid_len = vlen;
+      d += vlen;
+      break;
+
+    case ASN1_NULL:
+      if ((err = hal_asn1_decode_header(ASN1_NULL, d, algid_end - d, &hlen, &vlen)) != HAL_OK)
+        return err;
+      d += hlen;
+      if (vlen == 0)
+        break;
+
+    default:
+      return HAL_ERROR_ASN1_PARSE_FAILED;
+    }
+  }
+
+  if (d != algid_end)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+
+  if ((err = hal_asn1_decode_header(ASN1_BIT_STRING, d, der_end - d, &hlen, &vlen)) != HAL_OK)
+    return err;
+  d += hlen;
+  if (vlen >= algid_end - d || vlen == 0 || *d != 0x00)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+  *pubkey = ++d;
+  *pubkey_len = --vlen;
+  d += vlen;
+
+  if (d != der_end)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+
+  return HAL_OK;
+}
+
+/*
  * Local variables:
  * indent-tabs-mode: nil
  * End:
diff --git a/asn1_internal.h b/asn1_internal.h
index 18d4852..0b08b84 100644
--- a/asn1_internal.h
+++ b/asn1_internal.h
@@ -103,6 +103,16 @@ extern hal_error_t hal_asn1_encode_integer(const fp_int * const bn,
 extern hal_error_t hal_asn1_decode_integer(fp_int *bn,
                                            const uint8_t * const der, size_t *der_len, const size_t der_max);
 
+extern hal_error_t hal_asn1_encode_spki(const uint8_t * const alg_oid,   const size_t alg_oid_len,
+                                        const uint8_t * const curve_oid, const size_t curve_oid_len,
+                                        const uint8_t * const pubkey,    const size_t pubkey_len,
+                                        uint8_t *der, size_t *der_len, const size_t der_max);
+
+extern hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid,   size_t *alg_oid_len,
+                                        const uint8_t **curve_oid, size_t *curve_oid_len,
+                                        const uint8_t **pubkey,    size_t *pubkey_len,
+                                        const uint8_t *const der,  const size_t der_len);
+
 #endif /* _HAL_ASN1_INTERNAL_H_ */
 
 /*
diff --git a/ecdsa.c b/ecdsa.c
index b547712..6635cb0 100644
--- a/ecdsa.c
+++ b/ecdsa.c
@@ -237,6 +237,22 @@ static const ecdsa_curve_t * const get_curve(const hal_curve_name_t curve)
   }
 }
 
+static inline const ecdsa_curve_t * oid_to_curve(hal_curve_name_t *curve_name,
+                                                 const uint8_t * const oid,
+                                                 const size_t oid_len)
+{
+  assert(curve_name != NULL && oid != NULL);
+
+  const ecdsa_curve_t *curve = NULL;
+  *curve_name = HAL_CURVE_NONE;
+
+  while ((curve = get_curve(++*curve_name)) != NULL)
+    if (oid_len == curve->oid_len && memcmp(oid, curve->oid, oid_len) == 0)
+      return curve;
+
+  return NULL;
+}
+
 /*
  * Finite field operations (hence "ff_").  These are basically just
  * the usual bignum operations, constrained by the field modulus.
@@ -1192,7 +1208,7 @@ hal_error_t hal_ecdsa_private_key_to_der(const hal_ecdsa_key_t * const key,
  * take if encoded as DER.
  */
 
-size_t hal_ecdsa_key_to_der_len(const hal_ecdsa_key_t * const key)
+size_t hal_ecdsa_private_key_to_der_len(const hal_ecdsa_key_t * const key)
 {
   size_t len;
   return hal_ecdsa_private_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0;
@@ -1248,10 +1264,7 @@ hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key_,
   if ((err = hal_asn1_decode_header(ASN1_OBJECT_IDENTIFIER, d, vlen, &hlen, &vlen)) != HAL_OK)
     return err;
   d += hlen;
-  for (key->curve = HAL_CURVE_NONE; (curve = get_curve(++key->curve)) != NULL; )
-    if (vlen == curve->oid_len && memcmp(d, curve->oid, vlen) == 0)
-      break;
-  if (curve == NULL)
+  if ((curve = oid_to_curve(&key->curve, d, vlen)) == NULL)
     lose(HAL_ERROR_ASN1_PARSE_FAILED);
   d += vlen;
 
@@ -1284,6 +1297,104 @@ hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key_,
 }
 
 /*
+ * Write public key in SubjectPublicKeyInfo format, see RFCS 5280 and 5480.
+ */
+
+static const uint8_t oid_ecPublicKey[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 };
+
+hal_error_t hal_ecdsa_public_key_to_der(const hal_ecdsa_key_t * const key,
+                                        uint8_t *der, size_t *der_len, const size_t der_max)
+{
+  if (key == NULL || (key->type != HAL_KEY_TYPE_EC_PRIVATE &&
+                      key->type != HAL_KEY_TYPE_EC_PUBLIC))
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  const ecdsa_curve_t * const curve = get_curve(key->curve);
+  if (curve == NULL)
+    return HAL_ERROR_IMPOSSIBLE;
+
+  const size_t q_len  = fp_unsigned_bin_size(unconst_fp_int(curve->q));
+  const size_t Qx_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->x));
+  const size_t Qy_len = fp_unsigned_bin_size(unconst_fp_int(key->Q->y));
+  const size_t ecpoint_len = q_len * 2 + 1;
+  assert(q_len >= Qx_len && q_len >= Qy_len);
+
+  if (der != NULL && ecpoint_len < der_max) {
+    memset(der, 0, ecpoint_len);
+
+    uint8_t *d = der;
+    *d++ = 0x04;                /* Uncompressed */
+
+    fp_to_unsigned_bin(unconst_fp_int(key->Q->x), d + q_len - Qx_len);
+    d += q_len;
+
+    fp_to_unsigned_bin(unconst_fp_int(key->Q->y), d + q_len - Qy_len);
+    d += q_len;
+
+    assert(d < der + der_max);
+  }
+
+  return hal_asn1_encode_spki(oid_ecPublicKey, sizeof(oid_ecPublicKey),
+                              curve->oid, curve->oid_len,
+                              der, ecpoint_len,
+                              der, der_len, der_max);
+}
+
+/*
+ * Convenience wrapper to return how many bytes a public key would
+ * take if encoded as DER.
+ */
+
+size_t hal_ecdsa_public_key_to_der_len(const hal_ecdsa_key_t * const key)
+{
+  size_t len;
+  return hal_ecdsa_public_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0;
+}
+
+/*
+ * Read public key in SubjectPublicKeyInfo format, see RFCS 5280 and 5480.
+ */
+
+hal_error_t hal_ecdsa_public_key_from_der(hal_ecdsa_key_t **key_,
+                                           void *keybuf, const size_t keybuf_len,
+                                           const uint8_t * const der, const size_t der_len)
+{
+  hal_ecdsa_key_t *key = keybuf;
+
+  if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key))
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  memset(keybuf, 0, keybuf_len);
+  key->type = HAL_KEY_TYPE_EC_PUBLIC;
+
+  const uint8_t *alg_oid = NULL, *curve_oid = NULL, *pubkey = NULL;
+  size_t         alg_oid_len,     curve_oid_len,     pubkey_len;
+  const ecdsa_curve_t *curve;
+  hal_error_t err;
+
+  if ((err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, &pubkey, &pubkey_len,
+                                  der, der_len)) != HAL_OK)
+    return err;
+
+  if (alg_oid == NULL || curve_oid == NULL || pubkey == NULL ||
+      alg_oid_len != sizeof(oid_ecPublicKey) || memcmp(alg_oid, oid_ecPublicKey, alg_oid_len) != 0 ||
+      (curve = oid_to_curve(&key->curve, curve_oid, curve_oid_len)) == NULL ||
+      pubkey_len < 3 || (pubkey_len & 1) == 0 || pubkey[0] != 0x04 ||
+      pubkey_len / 2 != fp_unsigned_bin_size(unconst_fp_int(curve->q)))
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+
+  const uint8_t * const Qx = pubkey + 1;
+  const uint8_t * const Qy = Qx + pubkey_len / 2;
+
+  fp_read_unsigned_bin(key->Q->x, unconst_uint8_t(Qx), pubkey_len / 2);
+  fp_read_unsigned_bin(key->Q->y, unconst_uint8_t(Qy), pubkey_len / 2);
+  fp_set(key->Q->z, 1);
+
+  *key_ = key;
+  return HAL_OK;
+}
+
+/*
  * Encode a signature in PKCS #11 format: an octet string consisting
  * of concatenated values for r and s, each padded (if necessary) out
  * to the byte length of the order of the base point.
diff --git a/hal.h b/hal.h
index 36c56b7..eefaa55 100644
--- a/hal.h
+++ b/hal.h
@@ -425,12 +425,21 @@ extern hal_error_t hal_rsa_key_gen(const hal_core_t *core,
 extern hal_error_t hal_rsa_private_key_to_der(const hal_rsa_key_t * const key,
                                               uint8_t *der, size_t *der_len, const size_t der_max);
 
-extern size_t hal_rsa_key_to_der_len(const hal_rsa_key_t * const key);
+extern size_t hal_rsa_private_key_to_der_len(const hal_rsa_key_t * const key);
 
 extern hal_error_t hal_rsa_private_key_from_der(hal_rsa_key_t **key,
                                                 void *keybuf, const size_t keybuf_len,
                                                 const uint8_t * const der, const size_t der_len);
 
+extern hal_error_t hal_rsa_public_key_to_der(const hal_rsa_key_t * const key,
+                                             uint8_t *der, size_t *der_len, const size_t der_max);
+
+extern size_t hal_rsa_public_key_to_der_len(const hal_rsa_key_t * const key);
+
+extern hal_error_t hal_rsa_public_key_from_der(hal_rsa_key_t **key,
+                                               void *keybuf, const size_t keybuf_len,
+                                               const uint8_t * const der, const size_t der_len);
+
 /*
  * ECDSA.
  */
@@ -474,12 +483,21 @@ extern hal_error_t hal_ecdsa_key_gen(const hal_core_t *core,
 extern hal_error_t hal_ecdsa_private_key_to_der(const hal_ecdsa_key_t * const key,
                                                 uint8_t *der, size_t *der_len, const size_t der_max);
 
-extern size_t hal_ecdsa_key_to_der_len(const hal_ecdsa_key_t * const key);
+extern size_t hal_ecdsa_private_key_to_der_len(const hal_ecdsa_key_t * const key);
 
 extern hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key,
                                                   void *keybuf, const size_t keybuf_len,
                                                   const uint8_t * const der, const size_t der_len);
 
+extern hal_error_t hal_ecdsa_public_key_to_der(const hal_ecdsa_key_t * const key,
+                                               uint8_t *der, size_t *der_len, const size_t der_max);
+
+extern size_t hal_ecdsa_public_key_to_der_len(const hal_ecdsa_key_t * const key);
+
+extern hal_error_t hal_ecdsa_public_key_from_der(hal_ecdsa_key_t **key,
+                                                 void *keybuf, const size_t keybuf_len,
+                                                 const uint8_t * const der, const size_t der_len);
+
 extern hal_error_t hal_ecdsa_key_to_ecpoint(const hal_ecdsa_key_t * const key,
                                             uint8_t *der, size_t *der_len, const size_t der_max);
 
@@ -659,7 +677,7 @@ extern hal_error_t hal_rpc_pkey_get_key_flags(const hal_rpc_pkey_handle_t pkey,
 extern size_t hal_rpc_pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey);
 
 extern hal_error_t hal_rpc_pkey_get_public_key(const hal_rpc_pkey_handle_t pkey,
-                                               uint8_t *der, size_t *der_len, const size_t der_len_max);
+                                               uint8_t *der, size_t *der_len, const size_t der_max);
 
 extern hal_error_t hal_rpc_pkey_sign(const hal_rpc_session_handle_t session,
                                      const hal_rpc_pkey_handle_t pkey,
diff --git a/hal_internal.h b/hal_internal.h
index 3f7f600..edb0d8f 100644
--- a/hal_internal.h
+++ b/hal_internal.h
@@ -159,7 +159,7 @@ typedef struct {
   size_t (*get_public_key_len)(const hal_rpc_pkey_handle_t pkey);
 
   hal_error_t  (*get_public_key)(const hal_rpc_pkey_handle_t pkey,
-                                 uint8_t *der, size_t *der_len, const size_t der_len_max);
+                                 uint8_t *der, size_t *der_len, const size_t der_max);
 
   hal_error_t  (*sign)(const hal_rpc_session_handle_t session,
                        const hal_rpc_pkey_handle_t pkey,
diff --git a/rpc.c b/rpc.c
index e0cb869..b76c9cf 100644
--- a/rpc.c
+++ b/rpc.c
@@ -283,11 +283,11 @@ size_t hal_rpc_pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey)
 }
 
 hal_error_t hal_rpc_pkey_get_public_key(const hal_rpc_pkey_handle_t pkey,
-					uint8_t *der, size_t *der_len, const size_t der_len_max)
+					uint8_t *der, size_t *der_len, const size_t der_max)
 {
-  if (der == NULL || der_len == NULL || der_len_max == 0)
+  if (der == NULL || der_len == NULL || der_max == 0)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->get_public_key(pkey, der, der_len, der_len_max);
+  return pkey_dispatch->get_public_key(pkey, der, der_len, der_max);
 }
 
 hal_error_t hal_rpc_pkey_sign(const hal_rpc_session_handle_t session,
diff --git a/rpc_client.c b/rpc_client.c
index 0b13e58..74ea92e 100644
--- a/rpc_client.c
+++ b/rpc_client.c
@@ -172,7 +172,7 @@ static size_t pkey_get_public_key_len(const hal_rpc_pkey_handle_t pkey)
 }
 
 static hal_error_t pkey_get_public_key(const hal_rpc_pkey_handle_t pkey,
-                                       uint8_t *der, size_t *der_len, const size_t der_len_max)
+                                       uint8_t *der, size_t *der_len, const size_t der_max)
 {
   return HAL_ERROR_IMPOSSIBLE;
 }
diff --git a/rpc_pkey.c b/rpc_pkey.c
index d9ee53c..3488537 100644
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@ -293,7 +293,7 @@ static hal_error_t generate_rsa(const hal_rpc_client_handle_t client,
                              public_exponent, public_exponent_len)) != HAL_OK)
     return err;
 
-  uint8_t der[hal_rsa_key_to_der_len(key)];
+  uint8_t der[hal_rsa_private_key_to_der_len(key)];
   size_t der_len;
 
   if ((err = hal_rsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK)
@@ -344,7 +344,7 @@ static hal_error_t generate_ec(const hal_rpc_client_handle_t client,
   if ((err = hal_ecdsa_key_gen(NULL, &key, keybuf, sizeof(keybuf), curve)) != HAL_OK)
     return err;
 
-  uint8_t der[hal_ecdsa_key_to_der_len(key)];
+  uint8_t der[hal_ecdsa_private_key_to_der_len(key)];
   size_t der_len;
 
   if ((err = hal_ecdsa_private_key_to_der(key, der, &der_len, sizeof(der))) == HAL_OK)
@@ -450,7 +450,47 @@ static hal_error_t get_key_flags(const hal_rpc_pkey_handle_t pkey,
 
 static size_t get_public_key_len(const hal_rpc_pkey_handle_t pkey)
 {
-  return 0;
+  pkey_slot_t *slot = find_handle(pkey);
+
+  if (slot == NULL)
+    return 0;
+
+  size_t result = 0;
+
+  uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size];
+  hal_rsa_key_t   *rsa_key   = NULL;
+  hal_ecdsa_key_t *ecdsa_key = NULL;
+  uint8_t der[HAL_KS_WRAPPED_KEYSIZE];
+  size_t der_len;
+
+  if (hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL,
+                   der, &der_len, sizeof(der), &slot->ks_hint) == HAL_OK) {
+    switch (slot->type) {
+
+    case HAL_KEY_TYPE_RSA_PUBLIC:
+    case HAL_KEY_TYPE_EC_PUBLIC:
+      result = der_len;
+      break;
+
+    case HAL_KEY_TYPE_RSA_PRIVATE:
+      if (hal_rsa_private_key_from_der(&rsa_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK)
+        result = hal_rsa_public_key_to_der_len(rsa_key);
+      break;
+
+    case HAL_KEY_TYPE_EC_PRIVATE:
+      if (hal_ecdsa_private_key_from_der(&ecdsa_key, keybuf, sizeof(keybuf), der, der_len) == HAL_OK)
+        result = hal_ecdsa_public_key_to_der_len(ecdsa_key);
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  memset(keybuf, 0, sizeof(keybuf));
+  memset(der,    0, sizeof(der));
+
+  return result;
 }
 
 /*
@@ -458,13 +498,54 @@ static size_t get_public_key_len(const hal_rpc_pkey_handle_t pkey)
  */
 
 static hal_error_t get_public_key(const hal_rpc_pkey_handle_t pkey,
-                                  uint8_t *der, size_t *der_len, const size_t der_len_max)
+                                  uint8_t *der, size_t *der_len, const size_t der_max)
 {
-  /*
-   * Still missing some of the public key format ASN.1 stuff, apparently.  Feh.
-   */
-  return HAL_ERROR_IMPOSSIBLE;
-#warning get_public_key() not implemented
+  pkey_slot_t *slot = find_handle(pkey);
+
+  if (slot == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  uint8_t keybuf[hal_rsa_key_t_size > hal_ecdsa_key_t_size ? hal_rsa_key_t_size : hal_ecdsa_key_t_size];
+  hal_rsa_key_t   *rsa_key   = NULL;
+  hal_ecdsa_key_t *ecdsa_key = NULL;
+  uint8_t buf[HAL_KS_WRAPPED_KEYSIZE];
+  size_t buf_len;
+  hal_error_t err;
+
+  if ((err = hal_ks_fetch(slot->type, slot->name, slot->name_len, NULL, NULL,
+                          buf, &buf_len, sizeof(buf), &slot->ks_hint)) == HAL_OK) {
+    switch (slot->type) {
+
+    case HAL_KEY_TYPE_RSA_PUBLIC:
+    case HAL_KEY_TYPE_EC_PUBLIC:
+      if (der_len != NULL)
+        *der_len = buf_len;
+      if (der != NULL && der_max < buf_len)
+        err = HAL_ERROR_RESULT_TOO_LONG;
+      else if (der != NULL)
+        memcpy(der, buf, buf_len);
+      break;
+
+    case HAL_KEY_TYPE_RSA_PRIVATE:
+      if ((err = hal_rsa_private_key_from_der(&rsa_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK)
+        err = hal_rsa_public_key_to_der(rsa_key, der, der_len, der_max);
+      break;
+
+    case HAL_KEY_TYPE_EC_PRIVATE:
+      if ((err = hal_ecdsa_private_key_from_der(&ecdsa_key, keybuf, sizeof(keybuf), buf, buf_len)) == HAL_OK)
+        err = hal_ecdsa_public_key_to_der(ecdsa_key, der, der_len, der_max);
+      break;
+
+    default:
+      err = HAL_ERROR_UNSUPPORTED_KEY;
+      break;
+    }
+  }
+
+  memset(keybuf, 0, sizeof(keybuf));
+  memset(buf,    0, sizeof(buf));
+
+  return err;
 }
 
 /*
diff --git a/rsa.c b/rsa.c
index e6d70db..86c891a 100644
--- a/rsa.c
+++ b/rsa.c
@@ -733,7 +733,7 @@ hal_error_t hal_rsa_private_key_to_der(const hal_rsa_key_t * const key,
   return HAL_OK;
 }
 
-size_t hal_rsa_key_to_der_len(const hal_rsa_key_t * const key)
+size_t hal_rsa_private_key_to_der_len(const hal_rsa_key_t * const key)
 {
   size_t len = 0;
   return hal_rsa_private_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0;
@@ -775,6 +775,100 @@ hal_error_t hal_rsa_private_key_from_der(hal_rsa_key_t **key_,
 }
 
 /*
+ * ASN.1 public keys in SubjectPublicKeyInfo form, see RFCs 2313, 4055, and 5280.
+ */
+
+static const uint8_t oid_rsaEncryption[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
+
+hal_error_t hal_rsa_public_key_to_der(const hal_rsa_key_t * const key,
+                                      uint8_t *der, size_t *der_len, const size_t der_max)
+{
+  if (key == NULL || (key->type != HAL_KEY_TYPE_RSA_PRIVATE &&
+                      key->type != HAL_KEY_TYPE_RSA_PUBLIC))
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  size_t hlen, n_len, e_len;
+  hal_error_t err;
+
+  if ((err = hal_asn1_encode_integer(key->n, NULL, &n_len, 0)) != HAL_OK ||
+      (err = hal_asn1_encode_integer(key->e, NULL, &e_len, 0)) != HAL_OK)
+    return err;
+
+  const size_t vlen = n_len + e_len;
+
+  if ((err = hal_asn1_encode_header(ASN1_SEQUENCE, vlen, der, &hlen, der_max)) != HAL_OK)
+    return err;
+
+  if (der != NULL) {
+    uint8_t * const n_out = der + hlen;
+    uint8_t * const e_out = n_out + n_len;
+
+    if ((err = hal_asn1_encode_integer(key->n, n_out, NULL, der + der_max - n_out)) != HAL_OK ||
+        (err = hal_asn1_encode_integer(key->e, e_out, NULL, der + der_max - e_out)) != HAL_OK)
+      return err;
+  }
+
+  return hal_asn1_encode_spki(oid_rsaEncryption, sizeof(oid_rsaEncryption),
+                              NULL, 0, der, hlen + vlen,
+                              der, der_len, der_max);
+
+}
+
+size_t hal_rsa_public_key_to_der_len(const hal_rsa_key_t * const key)
+{
+  size_t len = 0;
+  return hal_rsa_public_key_to_der(key, NULL, &len, 0) == HAL_OK ? len : 0;
+}
+
+hal_error_t hal_rsa_public_key_from_der(hal_rsa_key_t **key_,
+                                        void *keybuf, const size_t keybuf_len,
+                                        const uint8_t * const der, const size_t der_len)
+{
+  hal_rsa_key_t *key = keybuf;
+
+  if (key_ == NULL || key == NULL || keybuf_len < sizeof(*key) || der == NULL)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  memset(keybuf, 0, keybuf_len);
+
+  key->type = HAL_KEY_TYPE_RSA_PUBLIC;
+
+  const uint8_t *alg_oid = NULL, *null = NULL, *pubkey = NULL;
+  size_t         alg_oid_len,     null_len,     pubkey_len;
+  hal_error_t err;
+
+  if ((err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &null, &null_len, &pubkey, &pubkey_len, der, der_len)) != HAL_OK)
+    return err;
+
+  if (null != NULL || null_len != 0 || alg_oid == NULL ||
+      alg_oid_len != sizeof(oid_rsaEncryption) || memcmp(alg_oid, oid_rsaEncryption, alg_oid_len) != 0)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+  
+  size_t len, hlen, vlen;
+
+  if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, pubkey, pubkey_len, &hlen, &vlen)) != HAL_OK)
+    return err;
+
+  const uint8_t * const pubkey_end = pubkey + hlen + vlen;
+  const uint8_t *d = pubkey + hlen;
+
+  if ((err = hal_asn1_decode_integer(key->n, d, &len, pubkey_end - d)) != HAL_OK)
+    return err;
+  d += len;
+
+  if ((err = hal_asn1_decode_integer(key->e, d, &len, pubkey_end - d)) != HAL_OK)
+    return err;
+  d += len;
+
+  if (d != pubkey_end)
+    return HAL_ERROR_ASN1_PARSE_FAILED;
+
+  *key_ = key;
+
+  return HAL_OK;
+}
+
+/*
  * Local variables:
  * indent-tabs-mode: nil
  * End:
diff --git a/tests/test-ecdsa.c b/tests/test-ecdsa.c
index d88aeb2..7201648 100644
--- a/tests/test-ecdsa.c
+++ b/tests/test-ecdsa.c
@@ -127,7 +127,7 @@ static int test_against_static_vectors(const ecdsa_tc_t * const tc)
   if (tc->Qy_len != Qy_len || memcmp(tc->Qy, Qy, Qy_len) != 0)
     return printf("Qy mismatch\n"), 0;
 
-  if (hal_ecdsa_key_to_der_len(key1) != tc->key_len)
+  if (hal_ecdsa_private_key_to_der_len(key1) != tc->key_len)
     return printf("DER Key length mismatch\n"), 0;
 
   uint8_t keyder[tc->key_len];



More information about the Commits mailing list