diff options
-rw-r--r-- | asn1.c | 83 | ||||
-rw-r--r-- | asn1_internal.h | 6 | ||||
-rw-r--r-- | ecdsa.c | 25 | ||||
-rw-r--r-- | hal.h | 19 | ||||
-rw-r--r-- | hal_internal.h | 28 | ||||
-rw-r--r-- | rpc_api.c | 24 | ||||
-rw-r--r-- | rpc_client.c | 81 | ||||
-rw-r--r-- | rpc_pkey.c | 292 |
8 files changed, 505 insertions, 53 deletions
@@ -52,7 +52,7 @@ #include <assert.h> #include "hal.h" - +#include "hal_internal.h" #include "asn1_internal.h" #define INIT_FP_INT {{{0}}} @@ -67,6 +67,16 @@ const size_t hal_asn1_oid_rsaEncryption_len = sizeof(hal_asn1_oid_rsaEncryption const uint8_t hal_asn1_oid_ecPublicKey[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }; const size_t hal_asn1_oid_ecPublicKey_len = sizeof(hal_asn1_oid_ecPublicKey); +#if KEK_LENGTH == (bitsToBytes(128)) +const uint8_t hal_asn1_oid_aesKeyWrap[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x08 }; +const size_t hal_asn1_oid_aesKeyWrap_len = sizeof(hal_asn1_oid_aesKeyWrap); +#endif + +#if KEK_LENGTH == (bitsToBytes(256)) +const uint8_t hal_asn1_oid_aesKeyWrap[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x30 }; +const size_t hal_asn1_oid_aesKeyWrap_len = sizeof(hal_asn1_oid_aesKeyWrap); +#endif + /* * Encode tag and length fields of an ASN.1 object. * @@ -482,8 +492,7 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid, size_t *alg_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) + if (der == NULL) return HAL_ERROR_BAD_ARGUMENTS; const uint8_t * const der_end = der + der_len; @@ -510,12 +519,16 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid, size_t *alg_oid_len, d += hlen; if (vlen > algid_end - d) return HAL_ERROR_ASN1_PARSE_FAILED; - *alg_oid = d; - *alg_oid_len = vlen; + if (alg_oid != NULL) + *alg_oid = d; + if (alg_oid_len != NULL) + *alg_oid_len = vlen; d += vlen; - *curve_oid = NULL; - *curve_oid_len = 0; + if (curve_oid != NULL) + *curve_oid = NULL; + if (curve_oid_len != NULL) + *curve_oid_len = 0; if (d < algid_end) { switch (*d) { @@ -526,8 +539,10 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid, size_t *alg_oid_len, d += hlen; if (vlen > algid_end - d) return HAL_ERROR_ASN1_PARSE_FAILED; - *curve_oid = d; - *curve_oid_len = vlen; + if (curve_oid != NULL) + *curve_oid = d; + if (curve_oid_len != NULL) + *curve_oid_len = vlen; d += vlen; break; @@ -551,8 +566,11 @@ hal_error_t hal_asn1_decode_spki(const uint8_t **alg_oid, size_t *alg_oid_len, 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 (pubkey != NULL) + *pubkey = d; + if (pubkey_len != NULL) + *pubkey_len = vlen; d += vlen; if (d != der_end) @@ -721,6 +739,49 @@ hal_error_t hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(const uint8_t **alg_oi } /* + * Attempt to guess what kind of key we're looking at. + */ + +hal_error_t hal_asn1_guess_key_type(hal_key_type_t *type, + hal_curve_name_t *curve, + const uint8_t *const der, const size_t der_len) +{ + if (type == NULL || curve == NULL || der == NULL) + return HAL_ERROR_BAD_ARGUMENTS; + + const uint8_t *alg_oid, *curve_oid; + size_t alg_oid_len, curve_oid_len; + hal_error_t err; + int public = 0; + + err = hal_asn1_decode_pkcs8_privatekeyinfo(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, NULL, 0, der, der_len); + + if (err == HAL_ERROR_ASN1_PARSE_FAILED && + (err = hal_asn1_decode_spki(&alg_oid, &alg_oid_len, &curve_oid, &curve_oid_len, NULL, 0, der, der_len)) == HAL_OK) + public = 1; + + if (err != HAL_OK) + return err; + + if (alg_oid_len == hal_asn1_oid_rsaEncryption_len && memcmp(alg_oid, hal_asn1_oid_rsaEncryption, alg_oid_len) == 0) { + *type = public ? HAL_KEY_TYPE_RSA_PUBLIC : HAL_KEY_TYPE_RSA_PRIVATE; + *curve = HAL_CURVE_NONE; + return HAL_OK; + } + + if (alg_oid_len == hal_asn1_oid_ecPublicKey_len && memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) == 0) { + *type = public ? HAL_KEY_TYPE_EC_PUBLIC : HAL_KEY_TYPE_EC_PRIVATE; + if ((err = hal_ecdsa_oid_to_curve(curve, curve_oid, curve_oid_len)) != HAL_OK) + *curve = HAL_CURVE_NONE; + return err; + } + + *type = HAL_KEY_TYPE_NONE; + *curve = HAL_CURVE_NONE; + return HAL_ERROR_UNSUPPORTED_KEY; +} + +/* * Local variables: * indent-tabs-mode: nil * End: diff --git a/asn1_internal.h b/asn1_internal.h index c337d4b..fe2f293 100644 --- a/asn1_internal.h +++ b/asn1_internal.h @@ -99,6 +99,9 @@ extern const size_t hal_asn1_oid_rsaEncryption_len; extern const uint8_t hal_asn1_oid_ecPublicKey[]; extern const size_t hal_asn1_oid_ecPublicKey_len; +extern const uint8_t hal_asn1_oid_aesKeyWrap[]; +extern const size_t hal_asn1_oid_aesKeyWrap_len; + /* * Transcoding functions. */ @@ -145,6 +148,9 @@ extern hal_error_t hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(const uint8_t * const uint8_t **data, size_t *data_len, const uint8_t *const der, const size_t der_len); +extern hal_error_t hal_asn1_guess_key_type(hal_key_type_t *type, hal_curve_name_t *curve, + const uint8_t *const der, const size_t der_len); + #endif /* _HAL_ASN1_INTERNAL_H_ */ /* @@ -258,20 +258,21 @@ 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) +hal_error_t hal_ecdsa_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); + if (curve_name == NULL || oid == NULL) + return HAL_ERROR_BAD_ARGUMENTS; - const ecdsa_curve_t *curve = NULL; *curve_name = HAL_CURVE_NONE; + const ecdsa_curve_t *curve; while ((curve = get_curve(++*curve_name)) != NULL) if (oid_len == curve->oid_len && memcmp(oid, curve->oid, oid_len) == 0) - return curve; + return HAL_OK; - return NULL; + return HAL_ERROR_UNSUPPORTED_KEY; } /* @@ -1395,7 +1396,7 @@ hal_error_t hal_ecdsa_private_key_from_der(hal_ecdsa_key_t **key_, if (alg_oid_len != hal_asn1_oid_ecPublicKey_len || memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) != 0 || - oid_to_curve(&key->curve, curve_oid, curve_oid_len) == NULL) + hal_ecdsa_oid_to_curve(&key->curve, curve_oid, curve_oid_len) != HAL_OK) return HAL_ERROR_ASN1_PARSE_FAILED; if ((err = hal_asn1_decode_header(ASN1_SEQUENCE, privkey, privkey_len, &hlen, &vlen)) != HAL_OK) @@ -1516,19 +1517,19 @@ hal_error_t hal_ecdsa_public_key_from_der(hal_ecdsa_key_t **key_, 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, + 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 != hal_asn1_oid_ecPublicKey_len || memcmp(alg_oid, hal_asn1_oid_ecPublicKey, alg_oid_len) != 0 || - (curve = oid_to_curve(&key->curve, curve_oid, curve_oid_len)) == NULL || + hal_ecdsa_oid_to_curve(&key->curve, curve_oid, curve_oid_len) != HAL_OK || pubkey_len < 3 || (pubkey_len & 1) == 0 || pubkey[0] != 0x04 || - pubkey_len / 2 != fp_unsigned_bin_size(unconst_fp_int(curve->q))) + pubkey_len / 2 != fp_unsigned_bin_size(unconst_fp_int(get_curve(key->curve)->q))) return HAL_ERROR_ASN1_PARSE_FAILED; const uint8_t * const Qx = pubkey + 1; @@ -505,6 +505,10 @@ extern const size_t hal_ecdsa_key_t_size; extern void hal_ecdsa_set_debug(const int onoff); +extern hal_error_t hal_ecdsa_oid_to_curve(hal_curve_name_t *curve, + const uint8_t * const oid, + const size_t oid_len); + extern hal_error_t hal_ecdsa_key_load_private(hal_ecdsa_key_t **key, void *keybuf, const size_t keybuf_len, const hal_curve_name_t curve, @@ -725,6 +729,7 @@ typedef uint32_t hal_key_flags_t; #define HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT (1 << 2) #define HAL_KEY_FLAG_TOKEN (1 << 3) #define HAL_KEY_FLAG_PUBLIC (1 << 4) +#define HAL_KEY_FLAG_EXPORTABLE (1 << 5) /* * hal_pkey_attribute_t.length would be size_t, except that we also @@ -821,6 +826,20 @@ extern hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey, uint8_t *attributes_buffer, const size_t attributes_buffer_len); +extern hal_error_t hal_rpc_pkey_export(const hal_pkey_handle_t pkey, + const hal_pkey_handle_t kekek, + uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max, + uint8_t *kek, size_t *kek_len, const size_t kek_max); + +extern hal_error_t hal_rpc_pkey_import(const hal_client_handle_t client, + const hal_session_handle_t session, + hal_pkey_handle_t *pkey, + hal_uuid_t *name, + const hal_pkey_handle_t kekek, + const uint8_t * const pkcs8, const size_t pkcs8_len, + const uint8_t * const kek, const size_t kek_len, + const hal_key_flags_t flags); + extern hal_error_t hal_rpc_client_init(void); extern hal_error_t hal_rpc_client_close(void); diff --git a/hal_internal.h b/hal_internal.h index 65f6ae7..8130801 100644 --- a/hal_internal.h +++ b/hal_internal.h @@ -269,6 +269,20 @@ typedef struct { uint8_t *attributes_buffer, const size_t attributes_buffer_len); + hal_error_t (*export)(const hal_pkey_handle_t pkey_handle, + const hal_pkey_handle_t kekek_handle, + uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max, + uint8_t *kek, size_t *kek_len, const size_t kek_max); + + hal_error_t (*import)(const hal_client_handle_t client, + const hal_session_handle_t session, + hal_pkey_handle_t *pkey, + hal_uuid_t *name, + const hal_pkey_handle_t kekek_handle, + const uint8_t * const pkcs8, const size_t pkcs8_len, + const uint8_t * const kek, const size_t kek_len, + const hal_key_flags_t flags); + } hal_rpc_pkey_dispatch_t; @@ -459,11 +473,11 @@ struct hal_ks_driver { hal_error_t (*store)(hal_ks_t *ks, hal_pkey_slot_t *slot, - const uint8_t * const der, const size_t der_len); + const uint8_t * const der, const size_t der_len); hal_error_t (*fetch)(hal_ks_t *ks, hal_pkey_slot_t *slot, - uint8_t *der, size_t *der_len, const size_t der_max); + uint8_t *der, size_t *der_len, const size_t der_max); hal_error_t (*delete)(hal_ks_t *ks, hal_pkey_slot_t *slot); @@ -537,7 +551,7 @@ static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver) } static inline hal_error_t hal_ks_open(const hal_ks_driver_t * const driver, - hal_ks_t **ks) + hal_ks_t **ks) { if (driver == NULL || ks == NULL) return HAL_ERROR_BAD_ARGUMENTS; @@ -863,6 +877,8 @@ typedef enum { RPC_FUNC_PKEY_GET_KEY_CURVE, RPC_FUNC_PKEY_SET_ATTRIBUTES, RPC_FUNC_PKEY_GET_ATTRIBUTES, + RPC_FUNC_PKEY_EXPORT, + RPC_FUNC_PKEY_IMPORT, } rpc_func_num_t; #define RPC_VERSION 0x01010000 /* 1.1.0.0 */ @@ -898,7 +914,7 @@ typedef enum { */ #ifndef HAL_CLIENT_SERIAL_DEFAULT_DEVICE -#define HAL_CLIENT_SERIAL_DEFAULT_DEVICE "/dev/ttyUSB0" +#define HAL_CLIENT_SERIAL_DEFAULT_DEVICE "/dev/ttyUSB0" #endif #ifndef HAL_CLIENT_SERIAL_DEFAULT_SPEED @@ -909,8 +925,8 @@ typedef enum { * Names of environment variables for setting the above in RPC clients. */ -#define HAL_CLIENT_SERIAL_DEVICE_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_DEVICE" -#define HAL_CLIENT_SERIAL_SPEED_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_SPEED" +#define HAL_CLIENT_SERIAL_DEVICE_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_DEVICE" +#define HAL_CLIENT_SERIAL_SPEED_ENVVAR "CRYPTECH_RPC_CLIENT_SERIAL_SPEED" #endif /* _HAL_INTERNAL_H_ */ @@ -381,6 +381,30 @@ hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey, attributes_buffer, attributes_buffer_len); } +hal_error_t hal_rpc_pkey_export(const hal_pkey_handle_t pkey, + const hal_pkey_handle_t kekek, + uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max, + uint8_t *kek, size_t *kek_len, const size_t kek_max) +{ + if (pkcs8 == NULL || pkcs8_len == NULL || kek == NULL || kek_len == NULL || kek_max <= KEK_LENGTH) + return HAL_ERROR_BAD_ARGUMENTS; + return hal_rpc_pkey_dispatch->export(pkey, kekek, pkcs8, pkcs8_len, pkcs8_max, kek, kek_len, kek_max); +} + +hal_error_t hal_rpc_pkey_import(const hal_client_handle_t client, + const hal_session_handle_t session, + hal_pkey_handle_t *pkey, + hal_uuid_t *name, + const hal_pkey_handle_t kekek, + const uint8_t * const pkcs8, const size_t pkcs8_len, + const uint8_t * const kek, const size_t kek_len, + const hal_key_flags_t flags) +{ + if (pkey == NULL || name == NULL || pkcs8 == NULL || kek == NULL || kek_len <= 2) + return HAL_ERROR_BAD_ARGUMENTS; + return hal_rpc_pkey_dispatch->import(client, session, pkey, name, kekek, pkcs8, pkcs8_len, kek, kek_len, flags); +} + /* * Local variables: * indent-tabs-mode: nil diff --git a/rpc_client.c b/rpc_client.c index 4adf247..5729b6f 100644 --- a/rpc_client.c +++ b/rpc_client.c @@ -915,6 +915,78 @@ static hal_error_t pkey_remote_get_attributes(const hal_pkey_handle_t pkey, return rpc_ret; } +static hal_error_t pkey_remote_export(const hal_pkey_handle_t pkey, + const hal_pkey_handle_t kekek, + uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max, + uint8_t *kek, size_t *kek_len, const size_t kek_max) +{ + uint8_t outbuf[nargs(6)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(5) + pad(pkcs8_max) + pad(kek_max)]; + const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + hal_client_handle_t dummy_client = {0}; + hal_error_t rpc_ret; + + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_EXPORT)); + check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle)); + check(hal_xdr_encode_int(&optr, olimit, pkey.handle)); + check(hal_xdr_encode_int(&optr, olimit, kekek.handle)); + check(hal_xdr_encode_int(&optr, olimit, pkcs8_max)); + check(hal_xdr_encode_int(&optr, olimit, kek_max)); + check(hal_rpc_send(outbuf, optr - outbuf)); + + check(read_matching_packet(RPC_FUNC_PKEY_EXPORT, inbuf, sizeof(inbuf), &iptr, &ilimit)); + + check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + if (rpc_ret == HAL_OK) { + uint32_t len; + len = pkcs8_max; + check(hal_xdr_decode_buffer(&iptr, ilimit, pkcs8, &len)); + *pkcs8_len = (size_t) len; + len = kek_max; + check(hal_xdr_decode_buffer(&iptr, ilimit, kek, &len)); + *kek_len = (size_t) len; + } + return rpc_ret; +} + +static hal_error_t pkey_remote_import(const hal_client_handle_t client, + const hal_session_handle_t session, + hal_pkey_handle_t *pkey, + hal_uuid_t *name, + const hal_pkey_handle_t kekek, + const uint8_t * const pkcs8, const size_t pkcs8_len, + const uint8_t * const kek, const size_t kek_len, + const hal_key_flags_t flags) +{ + uint8_t outbuf[nargs(7) + pad(pkcs8_len) + pad(kek_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf); + uint8_t inbuf[nargs(5) + pad(sizeof(name->uuid))]; + const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf); + uint32_t name_len = sizeof(name->uuid); + hal_error_t rpc_ret; + + check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_IMPORT)); + check(hal_xdr_encode_int(&optr, olimit, client.handle)); + check(hal_xdr_encode_int(&optr, olimit, session.handle)); + check(hal_xdr_encode_int(&optr, olimit, kekek.handle)); + check(hal_xdr_encode_buffer(&optr, olimit, pkcs8, pkcs8_len)); + check(hal_xdr_encode_buffer(&optr, olimit, kek, kek_len)); + check(hal_xdr_encode_int(&optr, olimit, flags)); + check(hal_rpc_send(outbuf, optr - outbuf)); + + check(read_matching_packet(RPC_FUNC_PKEY_IMPORT, inbuf, sizeof(inbuf), &iptr, &ilimit)); + + check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret)); + + if (rpc_ret == HAL_OK) { + check(hal_xdr_decode_int(&iptr, ilimit, &pkey->handle)); + check(hal_xdr_decode_buffer(&iptr, ilimit, name->uuid, &name_len)); + if (name_len != sizeof(name->uuid)) + return HAL_ERROR_KEY_NAME_TOO_LONG; + } + + return rpc_ret; +} + #if RPC_CLIENT == RPC_CLIENT_MIXED /* @@ -1043,7 +1115,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_remote_pkey_dispatch = { .verify = pkey_remote_verify, .match = pkey_remote_match, .set_attributes = pkey_remote_set_attributes, - .get_attributes = pkey_remote_get_attributes + .get_attributes = pkey_remote_get_attributes, + .export = pkey_remote_export, + .import = pkey_remote_import }; #if RPC_CLIENT == RPC_CLIENT_MIXED @@ -1063,7 +1137,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = { .verify = pkey_mixed_verify, .match = pkey_remote_match, .set_attributes = pkey_remote_set_attributes, - .get_attributes = pkey_remote_get_attributes + .get_attributes = pkey_remote_get_attributes, + .export = pkey_remote_export, + .import = pkey_remote_import }; #endif /* RPC_CLIENT == RPC_CLIENT_MIXED */ @@ -1100,7 +1176,6 @@ hal_error_t hal_rpc_client_close(void) #endif } - /* * Local variables: * indent-tabs-mode: nil @@ -117,10 +117,10 @@ static inline hal_pkey_slot_t *find_handle(const hal_pkey_handle_t handle) * * That's almost the rule that PKCS #11 follows for so-called * "private" objects (CKA_PRIVATE = CK_TRUE), but PKCS #11 has a more - * model which not only allows wider visibility to "public" objects - * (CKA_PRIVATE = CK_FALSE) but also allows write access to "public - * session" (CKA_PRIVATE = CK_FALSE, CKA_TOKEN = CK_FALSE) objects - * regardless of login state. + * complex model which not only allows wider visibility to "public" + * objects (CKA_PRIVATE = CK_FALSE) but also allows write access to + * "public session" (CKA_PRIVATE = CK_FALSE, CKA_TOKEN = CK_FALSE) + * objects regardless of login state. * * PKCS #11 also has a concept of read-only sessions, which we don't * bother to implement at all on the HSM, since the PIN is required to @@ -173,29 +173,55 @@ static inline hal_error_t check_writable(const hal_client_handle_t client, } /* + * PKCS #1.5 encryption requires non-zero random bytes, which is a bit + * messy if done in place, so make it a separate function for readability. + */ + +static inline hal_error_t get_nonzero_random(uint8_t *buffer, size_t n) +{ + assert(buffer != NULL); + + uint32_t word = 0; + hal_error_t err; + + while (n > 0) { + + while ((word & 0xFF) == 0) + if ((word & ~0xFF) != 0) + word >>= 8; + else if ((err = hal_get_random(NULL, &word, sizeof(word))) != HAL_OK) + return err; + + *buffer++ = word & 0xFF; + word >>= 8; + n--; + } + + return HAL_OK; +} + +/* * Pad an octet string with PKCS #1.5 padding for use with RSA. * - * For the moment, this only handles type 01 encryption blocks, thus - * is only suitable for use with signature and verification. If and - * when we add support for encryption and decryption, this function - * should be extended to take an argument specifying the block type - * and include support for generating type 02 encryption blocks. - * Other than the block type code, the only difference is the padding - * value: for type 01 it's constant (0xFF), for type 02 it should be - * non-zero random bytes from the CSPRNG. + * This handles type 01 and type 02 encryption blocks. The formats + * are identical, except that the padding string is constant 0xFF + * bytes for type 01 and non-zero random bytes for type 02. * * We use memmove() instead of memcpy() so that the caller can * construct the data to be padded in the same buffer. */ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len, - uint8_t *block, const size_t block_len) + uint8_t *block, const size_t block_len, + const uint8_t type) { - assert(data != NULL && block != NULL); + assert(data != NULL && block != NULL && (type == 0x01 || type == 0x02)); + + hal_error_t err; /* * Congregation will now please turn to RFC 2313 8.1 as we - * construct a PKCS #1.5 type 01 encryption block. + * construct a PKCS #1.5 type 01 or type 02 encryption block. */ if (data_len > block_len - 11) @@ -204,10 +230,20 @@ static hal_error_t pkcs1_5_pad(const uint8_t * const data, const size_t data_len memmove(block + block_len - data_len, data, data_len); block[0] = 0x00; - block[1] = 0x01; + block[1] = type; + + switch (type) { + + case 0x01: /* Signature */ + memset(block + 2, 0xFF, block_len - 3 - data_len); + break; + + case 0x02: /* Encryption */ + if ((err = get_nonzero_random(block + 2, block_len - 3 - data_len)) != HAL_OK) + return err; + break; - /* This is where we'd use non-zero random bytes if constructing a type 02 block. */ - memset(block + 2, 0xFF, block_len - 3 - data_len); + } block[block_len - data_len - 1] = 0x00; @@ -231,6 +267,8 @@ static inline hal_error_t ks_open_from_flags(hal_ks_t **ks, const hal_key_flags_ * return a key handle and the name. */ +#warning Convert hal_rpc_pkey_load() to use hal-asn1_guess_key_type()? + static hal_error_t pkey_local_load(const hal_client_handle_t client, const hal_session_handle_t session, hal_pkey_handle_t *pkey, @@ -699,7 +737,7 @@ static hal_error_t pkey_local_sign_rsa(uint8_t *keybuf, const size_t keybuf_len, input = signature; } - if ((err = pkcs1_5_pad(input, input_len, signature, *signature_len)) != HAL_OK || + if ((err = pkcs1_5_pad(input, input_len, signature, *signature_len, 0x01)) != HAL_OK || (err = hal_rsa_decrypt(NULL, key, signature, *signature_len, signature, *signature_len)) != HAL_OK) return err; @@ -831,7 +869,7 @@ static hal_error_t pkey_local_verify_rsa(uint8_t *keybuf, const size_t keybuf_le input = expected; } - if ((err = pkcs1_5_pad(input, input_len, expected, sizeof(expected))) != HAL_OK || + if ((err = pkcs1_5_pad(input, input_len, expected, sizeof(expected), 0x01)) != HAL_OK || (err = hal_rsa_encrypt(NULL, key, signature, signature_len, received, sizeof(received))) != HAL_OK) return err; @@ -1023,6 +1061,216 @@ static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey, return err; } +/* + * This is an RPC function, so the NULL pointer input convention for + * querying required buffer length isn't all that useful, but buffer + * lengths are predictable anyway: + * + * Size of the pkcs8 buffer is a constant, determined by + * oid_aes_aesKeyWrap_len, HAL_KS_WRAPPED_KEYSIZE, and some ASN.1 + * overhead; + * + * Size of the kek buffer is the same as the length of the + * modulus of the RSA public key indicated by wrap_handle. + * + * Except that we might want ASN.1 around the KEK, something like: + * + * SEQUENCE { + * keyEncryptionAlgorithm AlgorithmIdentifier { rsaEncryption }, + * encryptedKey OCTET STRING + * } + * + * which would still be constant-length, just a bit more verbose. + * + * Oddly enough, this is exactly the syntax of PKCS #8 + * EncryptedPrivateKeyInfo, which we already use for other purposes. + * Using it to wrap an AES key encrypted with an RSA key seems a bit + * odd, but it's a good fit and lets us reuse ASN.1 code. Cool. + */ + +static hal_error_t pkey_local_export(const hal_pkey_handle_t pkey_handle, + const hal_pkey_handle_t kekek_handle, + uint8_t *pkcs8, size_t *pkcs8_len, const size_t pkcs8_max, + uint8_t *kek, size_t *kek_len, const size_t kek_max) +{ + assert(pkcs8 != NULL && pkcs8_len != NULL && kek != NULL && kek_len != NULL && kek_max > KEK_LENGTH); + + uint8_t rsabuf[hal_rsa_key_t_size]; + hal_rsa_key_t *rsa = NULL; + hal_ks_t *ks = NULL; + hal_error_t err; + size_t len; + + hal_pkey_slot_t * const pkey = find_handle(pkey_handle); + hal_pkey_slot_t * const kekek = find_handle(kekek_handle); + + if (pkey == NULL || kekek == NULL) + return HAL_ERROR_KEY_NOT_FOUND; + + if ((pkey->flags & HAL_KEY_FLAG_EXPORTABLE) == 0) + return HAL_ERROR_FORBIDDEN; + + if (kekek->type != HAL_KEY_TYPE_RSA_PRIVATE && kekek->type != HAL_KEY_TYPE_RSA_PUBLIC) + return HAL_ERROR_UNSUPPORTED_KEY; + + if (pkcs8_max < HAL_KS_WRAPPED_KEYSIZE) + return HAL_ERROR_RESULT_TOO_LONG; + + if ((err = ks_open_from_flags(&ks, kekek->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, kekek, pkcs8, &len, pkcs8_max)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + if (err != HAL_OK) + goto fail; + + switch (kekek->type) { + case HAL_KEY_TYPE_RSA_PRIVATE: + err = hal_rsa_private_key_from_der(&rsa, rsabuf, sizeof(rsabuf), pkcs8, len); + break; + case HAL_KEY_TYPE_RSA_PUBLIC: + err = hal_rsa_public_key_from_der(&rsa, rsabuf, sizeof(rsabuf), pkcs8, len); + break; + default: + err = HAL_ERROR_IMPOSSIBLE; + } + if (err != HAL_OK) + goto fail; + + if ((err = hal_rsa_get_modulus(rsa, NULL, kek_len, 0)) != HAL_OK) + goto fail; + + if (*kek_len > kek_max) { + err = HAL_ERROR_RESULT_TOO_LONG; + goto fail; + } + + if ((err = ks_open_from_flags(&ks, pkey->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, pkey, pkcs8, &len, pkcs8_max)) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + + if (err != HAL_OK) + goto fail; + + if ((err = hal_get_random(NULL, kek, KEK_LENGTH)) != HAL_OK) + goto fail; + + if ((err = hal_aes_keywrap(NULL, kek, KEK_LENGTH, pkcs8, len, pkcs8, &len)) != HAL_OK) + goto fail; + + if ((err = hal_asn1_encode_pkcs8_encryptedprivatekeyinfo(hal_asn1_oid_aesKeyWrap, hal_asn1_oid_aesKeyWrap_len, + pkcs8, len, pkcs8, pkcs8_len, pkcs8_max)) != HAL_OK) + goto fail; + + if ((err = pkcs1_5_pad(kek, KEK_LENGTH, kek, *kek_len, 0x02)) != HAL_OK) + goto fail; + + if ((err = hal_rsa_encrypt(NULL, rsa, kek, *kek_len, kek, *kek_len)) != HAL_OK) + goto fail; + + if ((err = hal_asn1_encode_pkcs8_encryptedprivatekeyinfo(hal_asn1_oid_rsaEncryption, hal_asn1_oid_rsaEncryption_len, + kek, *kek_len, kek, *kek_len, kek_max)) != HAL_OK) + goto fail; + + memset(rsabuf, 0, sizeof(rsabuf)); + return HAL_OK; + + fail: + memset(pkcs8, 0, pkcs8_max); + memset(kek, 0, kek_max); + memset(rsabuf, 0, sizeof(rsabuf)); + *pkcs8_len = *kek_len = 0; + return err; +} + +static hal_error_t pkey_local_import(const hal_client_handle_t client, + const hal_session_handle_t session, + hal_pkey_handle_t *pkey, + hal_uuid_t *name, + const hal_pkey_handle_t kekek_handle, + const uint8_t * const pkcs8, const size_t pkcs8_len, + const uint8_t * const kek_, const size_t kek_len, + const hal_key_flags_t flags) +{ + assert(pkey != NULL && name != NULL && pkcs8 != NULL && kek_ != NULL && kek_len > 2); + + uint8_t kek[KEK_LENGTH], rsabuf[hal_rsa_key_t_size], der[HAL_KS_WRAPPED_KEYSIZE], *d, *oid, *data; + size_t der_len, oid_len, data_len; + hal_rsa_key_t *rsa = NULL; + hal_curve_name_t curve; + hal_key_type_t type; + hal_ks_t *ks = NULL; + hal_error_t err; + + hal_pkey_slot_t * const kekek = find_handle(kekek_handle); + + if (kekek == NULL) + return HAL_ERROR_KEY_NOT_FOUND; + + if (kekek->type != HAL_KEY_TYPE_RSA_PRIVATE) + return HAL_ERROR_UNSUPPORTED_KEY; + + if ((err = ks_open_from_flags(&ks, kekek->flags)) == HAL_OK && + (err = hal_ks_fetch(ks, kekek, der, &der_len, sizeof(der))) == HAL_OK) + err = hal_ks_close(ks); + else if (ks != NULL) + (void) hal_ks_close(ks); + if (err != HAL_OK) + goto fail; + + if ((err = hal_rsa_private_key_from_der(&rsa, rsabuf, sizeof(rsabuf), der, der_len)) != HAL_OK) + goto fail; + + if ((err = hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(&oid, &oid_len, &data, &data_len, kek_, kek_len)) != HAL_OK) + goto fail; + + if (oid_len != hal_asn1_oid_rsaEncryption_len || + memcmp(oid, hal_asn1_oid_rsaEncryption, oid_len) != 0 || + data_len > sizeof(der) || + data_len < 2) { + err = HAL_ERROR_ASN1_PARSE_FAILED; + goto fail; + } + + if ((err = hal_rsa_decrypt(NULL, rsa, data, data_len, der, data_len)) != HAL_OK) + goto fail; + + d = memchr(der + 2, 0x00, data_len - 2); + + if (der[0] != 0x00 || der[1] != 0x02 || d == NULL || der + data_len != d + 1 + KEK_LENGTH) { + err = HAL_ERROR_ASN1_PARSE_FAILED; + goto fail; + } + + memcpy(kek, d + 1, sizeof(kek)); + + if ((err = hal_asn1_decode_pkcs8_encryptedprivatekeyinfo(&oid, &oid_len, &data, &data_len, pkcs8, pkcs8_len)) != HAL_OK) + goto fail; + + if (oid_len != hal_asn1_oid_aesKeyWrap_len || + memcmp(oid, hal_asn1_oid_aesKeyWrap, oid_len) != 0 || + data_len > sizeof(der)) { + err = HAL_ERROR_ASN1_PARSE_FAILED; + goto fail; + } + + if ((err = hal_aes_keyunwrap(NULL, kek, sizeof(kek), data, data_len, der, &der_len)) != HAL_OK) + goto fail; + + if ((err = hal_asn1_guess_key_type(&type, &curve, der, der_len)) != HAL_OK) + goto fail; + + err = pkey_local_load(client, session, pkey, type, curve, name, der, der_len, flags); + + fail: + memset(rsabuf, 0, sizeof(rsabuf)); + memset(kek, 0, sizeof(kek)); + memset(der, 0, sizeof(der)); + return err; +} + const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = { .load = pkey_local_load, .open = pkey_local_open, @@ -1039,7 +1287,9 @@ const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = { .verify = pkey_local_verify, .match = pkey_local_match, .set_attributes = pkey_local_set_attributes, - .get_attributes = pkey_local_get_attributes + .get_attributes = pkey_local_get_attributes, + .export = pkey_local_export, + .import = pkey_local_import }; /* |