aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2015-12-22 18:55:11 -0500
committerRob Austein <sra@hactrn.net>2015-12-22 18:55:11 -0500
commit17da006ef0dcb18bf8da6ab6b90446c43a10c790 (patch)
treef6cef3ed996b6e452e18bfc2c1af350e3434185d
parent36f9b6627d41f72af38be1d819f37c20a11f43c5 (diff)
Add ASN.1 support for public keys (X.509 SubjectPublicKeyInfo format).
-rw-r--r--.gitignore1
-rw-r--r--asn1.c163
-rw-r--r--asn1_internal.h10
-rw-r--r--ecdsa.c121
-rw-r--r--hal.h24
-rw-r--r--hal_internal.h2
-rw-r--r--rpc.c6
-rw-r--r--rpc_client.c2
-rw-r--r--rpc_pkey.c99
-rw-r--r--rsa.c96
-rw-r--r--tests/test-ecdsa.c2
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];