From 17da006ef0dcb18bf8da6ab6b90446c43a10c790 Mon Sep 17 00:00:00 2001 From: Rob Austein Date: Tue, 22 Dec 2015 18:55:11 -0500 Subject: 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 @@ -152,6 +152,85 @@ hal_error_t hal_asn1_encode_integer(const fp_int * const bn, return HAL_OK; } +/* + * 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 @@ -218,6 +297,90 @@ hal_error_t hal_asn1_decode_integer(fp_int *bn, return HAL_OK; } +/* + * 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 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; @@ -1283,6 +1296,104 @@ hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key_, return err; } +/* + * 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 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; @@ -774,6 +774,100 @@ hal_error_t hal_rsa_private_key_from_der(hal_rsa_key_t **key_, return HAL_OK; } +/* + * 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 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]; -- cgit v1.2.3