From 080e32d73617ff70b0021a87e8330eac16c0581f Mon Sep 17 00:00:00 2001 From: Aveen Ismail <42414844+aveenismail@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:56:43 +0200 Subject: [PATCH] Asymmetric wrap support (#25) Add support for asymmetric wrap functionality --- .github/workflows/build_and_test.yml | 48 +- .github/workflows/integration_test.yml | 17 +- .github/workflows/release.yml | 21 +- lib/error.c | 2 +- lib/scp.h | 2 +- lib/yubihsm.c | 458 +++++++++++- lib/yubihsm.h | 243 ++++++- pkcs11/pkcs11y.h | 11 + pkcs11/tests/CMakeLists.txt | 21 +- pkcs11/tests/asym_wrap_test.c | 653 +++++++++++++++++ pkcs11/util_pkcs11.c | 434 ++++++++++-- pkcs11/util_pkcs11.h | 6 + pkcs11/yubihsm_pkcs11.c | 930 +++++++++++++++++-------- resources/tests/bash/test_wrapkey.sh | 43 +- src/cmdline.ggo | 9 + src/commands.c | 395 ++++++++++- src/commands.h | 10 + src/main.c | 287 +++++++- 18 files changed, 3118 insertions(+), 472 deletions(-) create mode 100644 pkcs11/tests/asym_wrap_test.c diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index ba35a84ab..92f0b8bce 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -8,16 +8,16 @@ jobs: fail-fast: false matrix: include: - - environment: "ubuntu:23.10" + - environment: "ubuntu:24.04" cc: "gcc" upload_for_test: "false" - - environment: "ubuntu:23.10" + - environment: "ubuntu:24.04" cc: "clang" upload_for_test: "false" - - environment: "ubuntu:23.04" + - environment: "ubuntu:23.10" cc: "gcc" upload_for_test: "false" - - environment: "ubuntu:23.04" + - environment: "ubuntu:23.10" cc: "clang" upload_for_test: "false" - environment: "ubuntu:22.04" @@ -32,15 +32,15 @@ jobs: - environment: "ubuntu:20.04" cc: "clang" upload_for_test: "false" - - environment: "ubuntu:18.04" - cc: "gcc" - upload_for_test: "false" - - environment: "ubuntu:16.04" - cc: "gcc" - upload_for_test: "false" - - environment: "ubuntu:14.04" - cc: "gcc" - upload_for_test: "false" +# - environment: "ubuntu:18.04" +# cc: "gcc" +# upload_for_test: "false" +# - environment: "ubuntu:16.04" +# cc: "gcc" +# upload_for_test: "false" +# - environment: "ubuntu:14.04" +# cc: "gcc" +# upload_for_test: "false" - environment: "debian:12" cc: "gcc" upload_for_test: "false" @@ -53,9 +53,6 @@ jobs: - environment: "debian:11" cc: "clang" upload_for_test: "false" - - environment: "debian:10" - cc: "gcc" - upload_for_test: "false" name: build on ${{ matrix.environment }} (${{ matrix.cc }},${{ matrix.upload_for_test}}) runs-on: ubuntu-latest @@ -87,7 +84,7 @@ jobs: fi - name: clone the Yubico/yubihsm-shell repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: yubihsm-shell @@ -169,21 +166,18 @@ jobs: matrix: include: # we do not perform clang builds for all environments, only fedora - - environment: "fedora:38" + - environment: "fedora:40" cc: "gcc" - upload_for_test: "false" - - environment: "fedora:38" + upload_for_test: "true" + - environment: "fedora:40" cc: "clang" upload_for_test: "false" - - environment: "fedora:37" + - environment: "fedora:39" cc: "gcc" upload_for_test: "false" - - environment: "fedora:37" + - environment: "fedora:39" cc: "clang" upload_for_test: "false" - - environment: "centos:7" - cc: "gcc" - upload_for_test: "true" name: build on ${{ matrix.environment }} (${{ matrix.cc }}, ${{ matrix.upload_for_test }}) runs-on: ubuntu-latest @@ -352,7 +346,7 @@ jobs: include: - environment: "ubuntu:22.04" cc: "clang" - - environment: "centos:7" + - environment: "fedora:40" cc: "gcc" name: run unit tests @@ -371,7 +365,7 @@ jobs: apt install -q -y build-essential cmake python3 python3-pip python3-setuptools curl libedit2 libpcsclite1 libengine-pkcs11-openssl opensc swig openjdk-11-jdk-headless libssl3 - name: install dependencies from package management (rpm based) - if: ${{ matrix.environment == 'centos:7' }} + if: ${{ matrix.environment == 'fedora:40' }} run: | yum install -y gcc gcc-c++ cmake python3-devel python3-pip python3-setuptools curl libedit gengetopt openssl libcurl pcsc-lite swig java-11-openjdk-headless which diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index ec7945876..903c2e49f 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -1,9 +1,6 @@ name: Run Integration Tests -on: - schedule: - # Run this every wednesday at 3:40. https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule - - cron: '40 3 * * 3' +on: [push, pull_request] jobs: main: @@ -77,12 +74,12 @@ jobs: working-directory: yubihsm-shell/resources/tests/bash run: ./cmdline_test.sh $GITHUB_WORKSPACE/yubihsm-shell/build/src/yubihsm-shell $DEFAULT_CONNECTOR_URL - - name: run pkcs11-tool tests - working-directory: yubihsm-shell/resources/tests/bash - run: | - echo "connector=$DEFAULT_CONNECTOR_URL" > yubihsm_pkcs11.conf - export YUBIHSM_PKCS11_CONF=`pwd`/yubihsm_pkcs11.conf - ./opensc_test.sh $GITHUB_WORKSPACE/yubihsm-shell/build/pkcs11/yubihsm_pkcs11.so +# - name: run pkcs11-tool tests +# working-directory: yubihsm-shell/resources/tests/bash +# run: | +# echo "connector=$DEFAULT_CONNECTOR_URL" > yubihsm_pkcs11.conf +# export YUBIHSM_PKCS11_CONF=`pwd`/yubihsm_pkcs11.conf +# ./opensc_test.sh $GITHUB_WORKSPACE/yubihsm-shell/build/pkcs11/yubihsm_pkcs11.so - name: clone the YubicoLabs/yubihsm_sunpkcs11_tests repository uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e132ef6c4..61701464e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,16 +141,15 @@ jobs: fail-fast: false matrix: environment: [ + "ubuntu:24.04", "ubuntu:23.10", - "ubuntu:23.04", "ubuntu:22.04", "ubuntu:20.04", - "ubuntu:18.04", - "ubuntu:16.04", - "ubuntu:14.04", +# "ubuntu:18.04", +# "ubuntu:16.04", +# "ubuntu:14.04", "debian:12", "debian:11", - "debian:10" ] # perform gcc builds for all environments cc: [ "gcc" ] @@ -208,6 +207,13 @@ jobs: # append the following flags: -Wno-missing-braces -Wno-missing-field-initializers -Wno-implicit-function-declaration sed -i 's/-Wall -Wextra -Werror/-Wall -Wextra -Werror -Wno-missing-braces -Wno-missing-field-initializers -Wno-implicit-function-declaration/' cmake/SecurityFlags.cmake + - name: apply environment specific changes to CMakeLists.txt + working-directory: yubihsm-shell + if: ${{ matrix.environment == 'ubuntu:24.04' }} + run: | + # ubuntu 24.04 comes with _FORTIFY_SOURCE already set + sed -i 's/add_definitions (-D_FORTIFY_SOURCE=2)/add_definitions (-D_FORTIFY_SOURCE=3)/' cmake/SecurityFlags.cmake + - name: extract platform name env: DOCKER_IMAGE: ${{ matrix.environment }} @@ -278,9 +284,8 @@ jobs: fail-fast: false matrix: environment: [ - "fedora:37", - "fedora:38", - "centos:7" + "fedora:39", + "fedora:40", ] name: build on ${{ matrix.environment }} diff --git a/lib/error.c b/lib/error.c index 71a3c33a0..7e8a98889 100644 --- a/lib/error.c +++ b/lib/error.c @@ -39,7 +39,7 @@ static const err_t errors[] = { ERR(YHR_SESSION_AUTHENTICATION_FAILED, "Unable to authenticate session"), ERR(YHR_MAC_MISMATCH, "Unable to verify MAC"), ERR(YHR_DEVICE_OK, "No error"), - ERR(YHR_DEVICE_INVALID_COMMAND, "Invalid command"), + ERR(YHR_DEVICE_INVALID_COMMAND, "Unrecognized command"), ERR(YHR_DEVICE_INVALID_DATA, "Malformed command / invalid data"), ERR(YHR_DEVICE_INVALID_SESSION, "Invalid session"), ERR(YHR_DEVICE_AUTHENTICATION_FAILED, diff --git a/lib/scp.h b/lib/scp.h index b9f673c93..242397765 100644 --- a/lib/scp.h +++ b/lib/scp.h @@ -49,7 +49,7 @@ #define SCP_AUTHKEY_ID_LEN 2 -#define SCP_MSG_BUF_SIZE 2048 +#define SCP_MSG_BUF_SIZE 4096 // Message #pragma pack(push, 1) diff --git a/lib/yubihsm.c b/lib/yubihsm.c index b0e8a1aba..096ee574e 100644 --- a/lib/yubihsm.c +++ b/lib/yubihsm.c @@ -1351,6 +1351,45 @@ yh_rc yh_get_connector_address(yh_connector *connector, char **const address) { return YHR_SUCCESS; } +yh_rc yh_util_get_partnumber(yh_connector *connector, char *part_number, + size_t *part_number_len) { + if (part_number == NULL || part_number_len == NULL) { + // Nothing to read + return YHR_SUCCESS; + } + + if (connector == NULL) { + DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); + return YHR_INVALID_PARAMETERS; + } + + uint8_t response[256] = {0}; + size_t response_len = sizeof(response); + yh_cmd response_cmd = 0; + + uint8_t data[1] = {1}; + yh_rc yrc = + yh_send_plain_msg(connector, YHC_GET_DEVICE_INFO, data, sizeof(data), + &response_cmd, response, &response_len); + if (yrc != YHR_SUCCESS) { + DBG_ERR("Failed to send GET DEVICE INFO command for page 1: %s", + yh_strerror(yrc)); + *part_number_len = 0; + return yrc; + } + + if (*part_number_len < (response_len + 1)) { + DBG_ERR("Response buffer too small"); + *part_number_len = 0; + return YHR_BUFFER_TOO_SMALL; + } + memcpy(part_number, response, response_len); + part_number[response_len] = 0; + *part_number_len = response_len; + + return YHR_SUCCESS; +} + yh_rc yh_util_get_device_info(yh_connector *connector, uint8_t *major, uint8_t *minor, uint8_t *patch, uint32_t *serial, uint8_t *log_total, uint8_t *log_used, @@ -1609,22 +1648,28 @@ yh_rc yh_util_get_object_info(yh_session *session, uint16_t id, return YHR_SUCCESS; } -yh_rc yh_util_get_public_key(yh_session *session, uint16_t id, uint8_t *data, - size_t *data_len, yh_algorithm *algorithm) { +yh_rc yh_util_get_public_key_ex(yh_session *session, yh_object_type type, + uint16_t id, uint8_t *data, size_t *data_len, + yh_algorithm *algorithm) { if (session == NULL || data == NULL || data_len == NULL) { DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); return YHR_INVALID_PARAMETERS; } - uint8_t cmd[2] = {id >> 8, id & 0xff}; + if (type == YH_PUBLIC_KEY) { + type = YH_ASYMMETRIC_KEY; + } + + uint8_t cmd[] = {id >> 8, id & 0xff, type}; yh_cmd response_cmd = 0; uint8_t response[YH_MSG_BUF_SIZE] = {0}; size_t response_len = sizeof(response); - yh_rc yrc = yh_send_secure_msg(session, YHC_GET_PUBLIC_KEY, cmd, sizeof(cmd), + yh_rc yrc = yh_send_secure_msg(session, YHC_GET_PUBLIC_KEY, cmd, + type == YH_ASYMMETRIC_KEY ? sizeof(cmd) - 1 + : sizeof(cmd), &response_cmd, response, &response_len); - if (yrc != YHR_SUCCESS) { DBG_ERR("Failed to send GET PUBLIC KEY command: %s", yh_strerror(yrc)); return yrc; @@ -1648,6 +1693,12 @@ yh_rc yh_util_get_public_key(yh_session *session, uint16_t id, uint8_t *data, return YHR_SUCCESS; } + +yh_rc yh_util_get_public_key(yh_session *session, uint16_t id, uint8_t *data, + size_t *data_len, yh_algorithm *algorithm) { + return yh_util_get_public_key_ex(session, YH_ASYMMETRIC_KEY, id, data, data_len, algorithm); +} + yh_rc yh_util_close_session(yh_session *session) { if (session == NULL) { @@ -1705,7 +1756,7 @@ yh_rc yh_util_sign_pkcs1v1_5(yh_session *session, uint16_t key_id, bool hashed, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len; + size_t data_len; yh_cmd response_cmd = 0; @@ -1756,7 +1807,7 @@ yh_rc yh_util_sign_pss(yh_session *session, uint16_t key_id, const uint8_t *in, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = in_len; + size_t data_len = in_len; yh_cmd response_cmd = 0; @@ -1812,7 +1863,7 @@ yh_rc yh_util_sign_ecdsa(yh_session *session, uint16_t key_id, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = in_len; + size_t data_len = in_len; yh_cmd response_cmd = 0; @@ -1853,7 +1904,7 @@ yh_rc yh_util_sign_eddsa(yh_session *session, uint16_t key_id, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = in_len; + size_t data_len = in_len; yh_cmd response_cmd = 0; @@ -1885,7 +1936,7 @@ yh_rc yh_util_sign_hmac(yh_session *session, uint16_t key_id, const uint8_t *in, } uint8_t data[YH_MSG_BUF_SIZE] = {0}; - uint16_t data_len = 2; + size_t data_len = 2; yh_cmd response_cmd = 0; @@ -1934,7 +1985,7 @@ static yh_rc import_key(yh_cmd cmd, yh_session *session, uint16_t *key_id, const char *label, uint16_t domains, const yh_capabilities *capabilities, yh_algorithm algorithm, const uint8_t *key, - uint16_t key_len) { + size_t key_len) { if (cmd != YHC_PUT_ASYMMETRIC_KEY && cmd != YHC_PUT_SYMMETRIC_KEY) { DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); return YHR_INVALID_PARAMETERS; @@ -1977,7 +2028,7 @@ static yh_rc import_key(yh_cmd cmd, yh_session *session, uint16_t *key_id, } memcpy(k.bytes, key, key_len); - uint16_t len = sizeof(k.key_id) + sizeof(k.domains) + sizeof(k.capabilities) + + size_t len = sizeof(k.key_id) + sizeof(k.domains) + sizeof(k.capabilities) + sizeof(k.algo) + sizeof(k.label) + key_len; yh_rc yrc = yh_send_secure_msg(session, cmd, k.buf, len, &response_cmd, response.buf, &response_len); @@ -2006,7 +2057,7 @@ yh_rc yh_util_import_aes_key(yh_session *session, uint16_t *key_id, return YHR_INVALID_PARAMETERS; } - uint16_t len; + size_t len; if (algorithm == YH_ALGO_AES128) { len = 16; @@ -2036,7 +2087,7 @@ yh_rc yh_util_import_rsa_key(yh_session *session, uint16_t *key_id, } uint8_t keybuf[256 * 2] = {0}; - uint16_t component_len; + size_t component_len; if (algorithm == YH_ALGO_RSA_2048) { component_len = 128; @@ -2069,7 +2120,7 @@ yh_rc yh_util_import_ec_key(yh_session *session, uint16_t *key_id, return YHR_INVALID_PARAMETERS; } - uint16_t component_len; + size_t component_len; switch (algorithm) { case YH_ALGO_EC_P224: component_len = 28; @@ -2107,7 +2158,7 @@ yh_rc yh_util_import_ed_key(yh_session *session, uint16_t *key_id, return YHR_INVALID_PARAMETERS; } - uint16_t component_len; + size_t component_len; switch (algorithm) { case YH_ALGO_EC_ED25519: component_len = 32; @@ -2153,7 +2204,7 @@ yh_rc yh_util_import_hmac_key(yh_session *session, uint16_t *key_id, size_t response_len = sizeof(response); yh_cmd response_cmd = 0; size_t max_len = 64; - int len = sizeof(k) - sizeof(k.key); + size_t len = sizeof(k) - sizeof(k.key); k.key_id = htons(*key_id); @@ -2220,7 +2271,7 @@ static yh_rc generate_key(yh_cmd cmd, yh_session *session, uint16_t *key_id, }; uint8_t buf[1]; } data = {0}; - int data_len = sizeof(data); + size_t data_len = sizeof(data); union { struct { uint16_t key_id; @@ -2336,7 +2387,7 @@ yh_rc yh_util_verify_hmac(yh_session *session, uint16_t key_id, } uint8_t cmd_data[YH_MSG_BUF_SIZE] = {0}; - int cmd_data_len; + size_t cmd_data_len; uint8_t response[3] = {0}; size_t response_len = sizeof(response); @@ -2485,7 +2536,7 @@ yh_rc yh_util_decrypt_oaep(yh_session *session, uint16_t key_id, #pragma pack(pop) yh_cmd response_cmd = 0; - uint16_t len = 0; + size_t len = 0; data.key_id = htons(key_id); len += sizeof(data.key_id); @@ -2600,6 +2651,12 @@ yh_rc yh_util_delete_object(yh_session *session, uint16_t id, yh_rc yh_util_export_wrapped(yh_session *session, uint16_t wrapping_key_id, yh_object_type target_type, uint16_t target_id, uint8_t *out, size_t *out_len) { + return yh_util_export_wrapped_ex(session, wrapping_key_id, target_type, target_id, 0, out, out_len); +} + +yh_rc yh_util_export_wrapped_ex(yh_session *session, uint16_t wrapping_key_id, + yh_object_type target_type, uint16_t target_id, + uint8_t format, uint8_t *out, size_t *out_len) { if (session == NULL || out == NULL || out_len == NULL) { DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); @@ -2612,6 +2669,7 @@ yh_rc yh_util_export_wrapped(yh_session *session, uint16_t wrapping_key_id, uint16_t key_id; uint8_t type; uint16_t tgt_id; + uint8_t format; }; uint8_t buf[1]; } data = {0}; @@ -2622,9 +2680,11 @@ yh_rc yh_util_export_wrapped(yh_session *session, uint16_t wrapping_key_id, data.key_id = htons(wrapping_key_id); data.type = (uint8_t) target_type; data.tgt_id = htons(target_id); + data.format = format; yh_rc yrc = yh_send_secure_msg(session, YHC_EXPORT_WRAPPED, data.buf, - sizeof(data), &response_cmd, out, out_len); + format == 0 ? sizeof(data) - 1 : sizeof(data), + &response_cmd, out, out_len); if (yrc != YHR_SUCCESS) { DBG_ERR("Failed to send EXPORT WRAPPED command: %s", yh_strerror(yrc)); return yrc; @@ -2657,7 +2717,7 @@ yh_rc yh_util_import_wrapped(yh_session *session, uint16_t wrapping_key_id, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = 2 + in_len; + size_t data_len = 2 + in_len; uint8_t response[YH_MSG_BUF_SIZE] = {0}; size_t response_len = sizeof(response); @@ -2680,6 +2740,237 @@ yh_rc yh_util_import_wrapped(yh_session *session, uint16_t wrapping_key_id, return YHR_SUCCESS; } +yh_rc yh_util_import_rsa_wrapped(yh_session *session, uint16_t wrapping_key_id, + yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *label, size_t label_len, + const uint8_t *in, size_t in_len, + yh_object_type *target_type, uint16_t *target_id) { + + if (session == NULL || in == NULL || target_type == NULL || + target_id == NULL) { + DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); + return YHR_INVALID_PARAMETERS; + } + + if (in_len + label_len > YH_MSG_BUF_SIZE) { + DBG_ERR("Too much data, must be < %d", YH_MSG_BUF_SIZE - 2); + return YHR_INVALID_PARAMETERS; + } + +#pragma pack(push, 1) + union { + struct { + uint16_t wrap_key_id; + uint8_t hash; + uint8_t mgf1; + uint8_t bytes[YH_MSG_BUF_SIZE]; + }; + uint8_t buf[1]; + } data = { + { + htons(wrapping_key_id), + hash, + mgf1, + { 0 } + } + }; +#pragma pack(pop) + size_t data_len = 4; + uint8_t response[YH_MSG_BUF_SIZE] = {0}; + size_t response_len = sizeof(response); + yh_cmd response_cmd = 0; + + memcpy(data.bytes, in, in_len); + data_len += in_len; + memcpy(data.bytes + in_len, label, label_len); + data_len += label_len; + + yh_rc yrc = + yh_send_secure_msg(session, YHC_IMPORT_RSA_WRAPPED, data.buf, data_len, + &response_cmd, response, &response_len); + if (yrc != YHR_SUCCESS) { + DBG_ERR("Failed to send IMPORT RSA WRAPPED command: %s", yh_strerror(yrc)); + return yrc; + } + + *target_type = response[0]; + *target_id = ntohs(*((uint16_t *) (response + 1))); + + return YHR_SUCCESS; +} + +yh_rc yh_util_put_rsa_wrapped_key(yh_session *session, uint16_t wrapping_key_id, + yh_object_type type, uint16_t *target_id, yh_algorithm algo, const char *label, + uint16_t domains, const yh_capabilities *capabilities, + yh_algorithm hash, yh_algorithm mgf1, const uint8_t *oaep_label, size_t oaep_label_len, + const uint8_t *in, size_t in_len) { + + if (session == NULL || in == NULL || target_id == NULL || label == NULL || strlen(label) > YH_OBJ_LABEL_LEN) { + DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); + return YHR_INVALID_PARAMETERS; + } + + if (in_len + oaep_label_len > YH_MSG_BUF_SIZE) { + DBG_ERR("Too much data, must be < %d", YH_MSG_BUF_SIZE - 2); + return YHR_INVALID_PARAMETERS; + } + +#pragma pack(push, 1) + union { + struct { + uint16_t wrap_key_id; + uint8_t target_type; + uint16_t target_id; + uint8_t label[YH_OBJ_LABEL_LEN]; + uint16_t domains; + uint8_t capabilities[YH_CAPABILITIES_LEN]; + uint8_t algorithm; + uint8_t hash; + uint8_t mgf1; + uint8_t bytes[YH_MSG_BUF_SIZE]; + }; + uint8_t buf[1]; + } data = { + { + htons(wrapping_key_id), + type, + htons(*target_id), + { 0 }, // label + htons(domains), + { 0 }, // capabilities + algo, + hash, + mgf1, + { 0 } + } + }; +#pragma pack(pop) + + // 2 bytes wrap key ID + + // 1 byte target type + + // 2 bytes target ID + + // 2 bytes domains + + // 1 byte algorithm + + // 1 byte hash algorithm + + // 1 byte MGF1 algorithm + // = 10 bytes + size_t data_len = 10 + YH_OBJ_LABEL_LEN + YH_CAPABILITIES_LEN; + uint8_t response[YH_MSG_BUF_SIZE] = {0}; + size_t response_len = sizeof(response); + yh_cmd response_cmd = 0; + + memcpy(data.label, label, strlen(label)); + memcpy(data.capabilities, capabilities, YH_CAPABILITIES_LEN); + memcpy(data.bytes, in, in_len); + data_len += in_len; + memcpy(data.bytes + in_len, oaep_label, oaep_label_len); + data_len += oaep_label_len; + + yh_rc yrc = + yh_send_secure_msg(session, YHC_PUT_RSA_WRAPPED_KEY, data.buf, data_len, + &response_cmd, response, &response_len); + if (yrc != YHR_SUCCESS) { + DBG_ERR("Failed to send IMPORT WRAPPED command: %s", yh_strerror(yrc)); + return yrc; + } + + *target_id = ntohs(*((uint16_t *) (response + 1))); + + if (response[0] != type) { + DBG_ERR("Imported key type does not match stated key. Removing key."); + yh_util_delete_object(session, *target_id, response[0]); + *target_id = 0; + return YHR_INVALID_PARAMETERS; + } + + return YHR_SUCCESS; +} + +static yh_rc +do_rsa_wrap(yh_cmd cmd, + yh_session *session, uint16_t wrap_key_id, + yh_object_type target_type, uint16_t target_id, + yh_algorithm aes, yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *oaep_label, size_t oaep_label_len, + uint8_t *out, size_t *out_len) { + + if (session == NULL || out == NULL || out_len == NULL || + oaep_label == NULL) { + DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); + return YHR_INVALID_PARAMETERS; + } + +#pragma pack(push, 1) + union { + struct { + uint16_t wrap_key_id; + uint8_t target_type; + uint16_t target_id; + uint8_t aes; + uint8_t hash; + uint8_t mgf1; + uint8_t label[64]; + }; + uint8_t buf[1]; + } data = { + { + htons(wrap_key_id), + target_type, + htons(target_id), + aes, + hash, + mgf1, + { 0 } + } + }; +#pragma pack(pop) + + yh_cmd response_cmd = 0; + size_t len = sizeof(data); + + memcpy(data.label, oaep_label, oaep_label_len); + len -= sizeof(data.label); + len += oaep_label_len; + + yh_rc yrc = yh_send_secure_msg(session, cmd, data.buf, len, + &response_cmd, out, out_len); + if (yrc != YHR_SUCCESS) { + DBG_ERR("Failed to send 0x%02x command: %s", cmd, yh_strerror(yrc)); + return yrc; + } + + return YHR_SUCCESS; +} + +yh_rc yh_util_get_rsa_wrapped_key(yh_session *session, uint16_t wrap_key_id, + yh_object_type target_type, + uint16_t target_id, yh_algorithm aes, + yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *oaep_label, + size_t oaep_label_len, uint8_t *out, + size_t *out_len) { + + if (target_type != YH_ASYMMETRIC_KEY && target_type != YH_SYMMETRIC_KEY) { + DBG_ERR("Only symmetric or asymmetric keys are supported"); + return YHR_INVALID_PARAMETERS; + } + return do_rsa_wrap(YHC_GET_RSA_WRAPPED_KEY, session, wrap_key_id, target_type, + target_id, aes, hash, mgf1, oaep_label, oaep_label_len, + out, out_len); +} + +yh_rc yh_util_export_rsa_wrapped(yh_session *session, uint16_t wrap_key_id, + yh_object_type target_type, uint16_t target_id, + yh_algorithm aes, yh_algorithm hash, + yh_algorithm mgf1, const uint8_t *oaep_label, + size_t oaep_label_len, uint8_t *out, + size_t *out_len) { + + return do_rsa_wrap(YHC_EXPORT_RSA_WRAPPED, session, wrap_key_id, target_type, + target_id, aes, hash, mgf1, oaep_label, oaep_label_len, + out, out_len); +} + yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, const char *label, uint16_t domains, const yh_capabilities *capabilities, @@ -2703,11 +2994,10 @@ yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, uint8_t capabilities[YH_CAPABILITIES_LEN]; uint8_t algorithm; uint8_t delegated_capabilities[YH_CAPABILITIES_LEN]; - uint8_t key[32]; + uint8_t key[512]; }; uint8_t buf[1]; } data = {0}; - uint16_t data_len = sizeof(data); union { struct { uint16_t key_id; @@ -2718,27 +3008,36 @@ yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, size_t response_len = sizeof(response); yh_cmd response_cmd = 0; - uint16_t key_len; + size_t key_len = 0; switch (algorithm) { case YH_ALGO_AES128_CCM_WRAP: key_len = 16; - data_len -= 16; break; case YH_ALGO_AES192_CCM_WRAP: key_len = 24; - data_len -= 8; break; case YH_ALGO_AES256_CCM_WRAP: key_len = 32; break; + case YH_ALGO_RSA_2048: + key_len = 256; + break; + case YH_ALGO_RSA_3072: + key_len = 384; + break; + case YH_ALGO_RSA_4096: + key_len = 512; + break; default: DBG_ERR("Bad algorithm specified: %x", algorithm); return YHR_INVALID_PARAMETERS; } + size_t data_len = sizeof(data) - sizeof(data.key) + key_len; + if (in_len != key_len) { - DBG_ERR("Key length not matching, should be %d", key_len); + DBG_ERR("Key length not matching, should be %zu", key_len); return YHR_INVALID_PARAMETERS; } @@ -2766,6 +3065,91 @@ yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, return YHR_SUCCESS; } +yh_rc yh_util_import_public_wrap_key(yh_session *session, uint16_t *key_id, + const char *label, uint16_t domains, + const yh_capabilities *capabilities, + yh_algorithm algorithm, + const yh_capabilities *delegated_capabilities, + const uint8_t *in, size_t in_len) { + + if (session == NULL || key_id == NULL || label == NULL || + strlen(label) > YH_OBJ_LABEL_LEN || capabilities == NULL || + delegated_capabilities == NULL || in == NULL) { + DBG_ERR("%s", yh_strerror(YHR_INVALID_PARAMETERS)); + return YHR_INVALID_PARAMETERS; + } + +#pragma pack(push, 1) + union { + struct { + uint16_t key_id; + uint8_t label[YH_OBJ_LABEL_LEN]; + uint16_t domains; + uint8_t capabilities[YH_CAPABILITIES_LEN]; + uint8_t algorithm; + uint8_t delegated_capabilities[YH_CAPABILITIES_LEN]; + uint8_t key[512]; + }; + uint8_t buf[1]; + } data = {0}; + union { + struct { + uint16_t key_id; + }; + uint8_t buf[1]; + } response = {0}; +#pragma pack(pop) + + size_t response_len = sizeof(response); + yh_cmd response_cmd = 0; + size_t key_len; + + switch (algorithm) { + case YH_ALGO_RSA_2048: + key_len = 256; + break; + case YH_ALGO_RSA_3072: + key_len = 384; + break; + case YH_ALGO_RSA_4096: + key_len = 512; + break; + default: + DBG_ERR("Bad algorithm specified: %x", algorithm); + return YHR_INVALID_PARAMETERS; + } + + size_t data_len = sizeof(data) - sizeof(data.key) + key_len; + + if (in_len != key_len) { + DBG_ERR("Key length not matching, should be %zu", key_len); + return YHR_INVALID_PARAMETERS; + } + + data.key_id = htons(*key_id); + memcpy(data.label, label, strlen(label)); + memset(data.label + strlen(label), 0, YH_OBJ_LABEL_LEN - strlen(label)); + data.domains = htons(domains); + memcpy(data.capabilities, capabilities, YH_CAPABILITIES_LEN); + data.algorithm = algorithm; + memcpy(data.delegated_capabilities, delegated_capabilities, + YH_CAPABILITIES_LEN); + memcpy(data.key, in, key_len); + + yh_rc yrc = yh_send_secure_msg(session, YHC_PUT_PUBLIC_WRAPKEY, data.buf, data_len, + &response_cmd, response.buf, &response_len); + insecure_memzero(data.buf, data_len); + if (yrc != YHR_SUCCESS) { + DBG_ERR("Failed to send PUT PUBLIC WRAP KEY command: %s", yh_strerror(yrc)); + return yrc; + } + + *key_id = ntohs(response.key_id); + DBG_INFO("Imported public wrap key 0x%04x", *key_id); + + return YHR_SUCCESS; +} + yh_rc yh_util_generate_wrap_key(yh_session *session, uint16_t *key_id, const char *label, uint16_t domains, const yh_capabilities *capabilities, @@ -2791,7 +3175,7 @@ yh_rc yh_util_generate_wrap_key(yh_session *session, uint16_t *key_id, }; uint8_t buf[1]; } data = {0}; - uint16_t data_len = sizeof(data); + size_t data_len = sizeof(data); union { struct { uint16_t key_id; @@ -2821,7 +3205,7 @@ yh_rc yh_util_generate_wrap_key(yh_session *session, uint16_t *key_id, yh_send_secure_msg(session, YHC_GENERATE_WRAP_KEY, data.buf, data_len, &response_cmd, response.buf, &response_len); if (yrc != YHR_SUCCESS) { - DBG_ERR("Failed to send GENERATE WRAP KEY command: %s", yh_strerror(yrc)); + DBG_ERR("Failed to generate wrap key: %s", yh_strerror(yrc)); return yrc; } @@ -2982,7 +3366,7 @@ yh_rc yh_util_import_opaque(yh_session *session, uint16_t *object_id, }; uint8_t buf[1]; } data = {0}; - uint16_t data_len; + size_t data_len; union { struct { uint16_t object_id; @@ -3047,7 +3431,7 @@ yh_rc yh_util_sign_ssh_certificate(yh_session *session, uint16_t key_id, }; uint8_t buf[1]; } data = {0}; - uint16_t data_len; + size_t data_len; #pragma pack(pop) yh_cmd response_cmd = 0; @@ -3126,7 +3510,7 @@ yh_rc yh_util_import_template(yh_session *session, uint16_t *object_id, }; uint8_t buf[1]; } data = {0}; - uint16_t data_len; + size_t data_len; union { struct { uint16_t object_id; @@ -3587,7 +3971,7 @@ yh_rc yh_util_import_otp_aead_key(yh_session *session, uint16_t *key_id, } response = {0}; #pragma pack(pop) - uint16_t data_len = sizeof(data); + size_t data_len = sizeof(data); size_t response_len = sizeof(response); yh_cmd response_cmd = 0; @@ -3655,7 +4039,7 @@ yh_rc yh_util_generate_otp_aead_key(yh_session *session, uint16_t *key_id, }; uint8_t buf[1]; } data = {0}; - uint16_t data_len = sizeof(data); + size_t data_len = sizeof(data); union { struct { uint16_t key_id; @@ -3864,7 +4248,7 @@ yh_rc yh_util_wrap_data(yh_session *session, uint16_t key_id, const uint8_t *in, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = in_len + 2; + size_t data_len = in_len + 2; yh_cmd response_cmd = 0; @@ -3905,7 +4289,7 @@ yh_rc yh_util_unwrap_data(yh_session *session, uint16_t key_id, uint8_t buf[1]; } data = {0}; #pragma pack(pop) - uint16_t data_len = in_len + 2; + size_t data_len = in_len + 2; yh_cmd response_cmd = 0; diff --git a/lib/yubihsm.h b/lib/yubihsm.h index 0d7001602..792e767a3 100644 --- a/lib/yubihsm.h +++ b/lib/yubihsm.h @@ -88,7 +88,7 @@ /// Length of host challenge for authentication #define YH_HOST_CHAL_LEN 8 /// Maximum length of message buffer -#define YH_MSG_BUF_SIZE 2048 +#define YH_MSG_BUF_SIZE 4096 /// Length of authentication keys #define YH_KEY_LEN 16 /// Device vendor ID @@ -359,6 +359,16 @@ typedef enum { ADD_COMMAND(YHC_DECRYPT_CBC, 0x71), /// Encrypt data using a Symmetric Key with CBC ADD_COMMAND(YHC_ENCRYPT_CBC, 0x72), + /// Import public RSA key as a Public Wrap Key + ADD_COMMAND(YHC_PUT_PUBLIC_WRAPKEY, 0x73), + /// Export (a)symmetric key using a Public Wrap Key + ADD_COMMAND(YHC_GET_RSA_WRAPPED_KEY, 0x74), + /// Import (a)symmetric key after unwrapping in using and RSA wrap key + ADD_COMMAND(YHC_PUT_RSA_WRAPPED_KEY, 0x75), + /// Wrap an object using an RSA Wrap Key + ADD_COMMAND(YHC_EXPORT_RSA_WRAPPED, 0x76), + /// Import an object after unwrapping in using and RSA Wrap Key + ADD_COMMAND(YHC_IMPORT_RSA_WRAPPED, 0x77), /// The response byte returned from the device if the command resulted in an /// error YHC_ERROR = 0x7f, @@ -392,6 +402,9 @@ typedef enum { YH_OTP_AEAD_KEY = 0x07, /// Symmetric Key is a secret key used for encryption and decryption. YH_SYMMETRIC_KEY = 0x08, + /// Public Wrap Key is a public key used to wrap Objects during the + /// export process + YH_PUBLIC_WRAP_KEY = 0x09, /// Public Key is the public key of an asymmetric key-pair. The public key /// never exists in device and is mostly here for PKCS#11. YH_PUBLIC_KEY = 0x83, @@ -514,6 +527,8 @@ typedef enum { YH_ALGO_AES_ECB = 53, /// aes-cbc YH_ALGO_AES_CBC = 54, + /// aes-kwp + YH_ALGO_AES_KWP = 55, } yh_algorithm; /** @@ -623,6 +638,7 @@ static const struct { {"delete-hmac-key", 0x2b}, {"delete-opaque", 0x27}, {"delete-otp-aead-key", 0x2d}, + {"delete-public-wrap-key", 0x55}, {"delete-symmetric-key", 0x31}, {"delete-template", 0x2c}, {"delete-wrap-key", 0x2a}, @@ -647,6 +663,7 @@ static const struct { {"put-mac-key", 0x14}, {"put-opaque", 0x01}, {"put-otp-aead-key", 0x23}, + {"put-public-wrap-key", 0x54}, {"put-symmetric-key", 0x2f}, {"put-template", 0x1b}, {"put-wrap-key", 0x0e}, @@ -683,6 +700,7 @@ static const struct { {"aes256-yubico-otp", YH_ALGO_AES256_YUBICO_OTP}, {"aes-cbc", YH_ALGO_AES_CBC}, {"aes-ecb", YH_ALGO_AES_ECB}, + {"aes-kwp", YH_ALGO_AES_KWP}, {"ecbp256", YH_ALGO_EC_BP256}, {"ecbp384", YH_ALGO_EC_BP384}, {"ecbp512", YH_ALGO_EC_BP512}, @@ -736,6 +754,7 @@ static const struct { {"hmac-key", YH_HMAC_KEY}, {"opaque", YH_OPAQUE}, {"otp-aead-key", YH_OTP_AEAD_KEY}, + {"public-wrap-key", YH_PUBLIC_WRAP_KEY}, {"symmetric-key", YH_SYMMETRIC_KEY}, {"template", YH_TEMPLATE}, {"wrap-key", YH_WRAP_KEY}, @@ -1229,6 +1248,24 @@ yh_rc yh_util_get_device_info(yh_connector *connector, uint8_t *major, uint8_t *log_total, uint8_t *log_used, yh_algorithm *algorithms, size_t *n_algorithms); +/** + * Get device version, part number (chip designator) as required ny FIPS + * + * @param connector Connector to the device + * @param part_number Part number (chip designator) + * @param part_number_len Size of part_number + * + * @return #YHR_SUCCESS if successful or of part_number is NULL + * #YHR_DEVICE_INVALID_COMMAND if firmware version does not support the + *command + * #YHR_INVALID_PARAMETER if the connector is NULL. + * #YH_INVALID_DEVICE_INVALID_DATA If returned part_number is less than + *12 bytes + * #YHR_BUFFER_TOO_SMALL if part_number is smaller than 13 bytes + **/ +yh_rc yh_util_get_partnumber(yh_connector *connector, char *part_number, + size_t *part_number_len); + /** * List objects accessible from the session * @@ -1303,6 +1340,23 @@ yh_rc yh_util_get_object_info(yh_session *session, uint16_t id, yh_rc yh_util_get_public_key(yh_session *session, uint16_t id, uint8_t *data, size_t *data_len, yh_algorithm *algorithm); +/** + * Get the value of the public key with the specified Object ID and type + * + * @param session Authenticated session to use + * @param id Object ID of the public key + * @param type Object type of the public key + * @param data Value of the public key + * @param data_len Length of the public key in bytes + * @param algorithm Algorithm of the key + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL. + * #YHR_BUFFER_TOO_SMALL if the actual key length was bigger than + *data_len. See #yh_rc for other possible errors + **/ +yh_rc yh_util_get_public_key_ex(yh_session *session, yh_object_type type, uint16_t id, uint8_t *data, size_t *data_len, yh_algorithm *algorithm); + /** * Close a session * @@ -1800,10 +1854,31 @@ yh_rc yh_util_delete_object(yh_session *session, uint16_t id, * #YHR_INVALID_PARAMETERS if input parameters are NULL. * See #yh_rc for other possible errors **/ + yh_rc yh_util_export_wrapped(yh_session *session, uint16_t wrapping_key_id, yh_object_type target_type, uint16_t target_id, uint8_t *out, size_t *out_len); +/** + * Export an object under wrap from the device with the option to include the + *ED25519 seed + * + * @param session Authenticated session to use + * @param wrapping_key_id Object ID of the Wrap Key to use to wrap the object + * @param target_type Type of the object to be exported. See #yh_object_type + * @param target_id Object ID of the object to be exported + * @param format Curently supported formats: 0=legacy 1=include ED25519 seed + * @param out Wrapped data + * @param out_len Length of wrapped data + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_export_wrapped_ex(yh_session *session, uint16_t wrapping_key_id, + yh_object_type target_type, uint16_t target_id, + uint8_t format, uint8_t *out, size_t *out_len); + /** * Import a wrapped object into the device. The object should have been *previously exported by #yh_util_export_wrapped() @@ -1823,6 +1898,131 @@ yh_rc yh_util_import_wrapped(yh_session *session, uint16_t wrapping_key_id, const uint8_t *in, size_t in_len, yh_object_type *target_type, uint16_t *target_id); +/** + * Export a (a)symmetric key material using an RSA wrap key, meta data or + *properties, like domains and capabilities, are not included. Only asymmetric + *and symmetric key objects are valid targets. + * + * @param session Authenticated session to use + * @param wrapping_key_id Object ID of the Wrap Key to use to wrap the object + * @param target_type Type of the target key object + * @param target_id Object ID of the target key object + * @param aes Algorithm of the ephemeral AES key. Can be #YH_ALGO_AES128, + *#YH_ALGO_AES192 or #YH_ALGO_AES256 + * @param hash Hash algorithm. One of #YH_ALGO_RSA_OAEP_SHA1, + *#YH_ALGO_RSA_OAEP_SHA256, #YH_ALGO_RSA_OAEP_SHA384 or #YH_ALGO_RSA_OAEP_SHA512 + * @param mgf1 MGF1 algorithm. One of #YH_ALGO_MGF1_SHA1, #YH_ALGO_MGF1_SHA256, + *#YH_ALGO_MGF1_SHA384 or #YH_ALGO_MGF1_SHA512 + * @param label Label for the MGF1 algorithm + * @param label_len Label length + * @param out Wrapped key object bytes + * @param out_len Length of the wrapped key + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL or if the + * command is not supported. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_get_rsa_wrapped_key(yh_session *session, uint16_t wrap_key_id, + yh_object_type target_type, + uint16_t target_id, yh_algorithm aes, + yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *oaep_label, + size_t oaep_label_len, uint8_t *out, + size_t *out_len); + +/** + * Export an object using an RSA wrap key. The wrapped object contain all meta + *data and properties, like domains and capabilities + * + * @param session Authenticated session to use + * @param wrapping_key_id Object ID of the Wrap Key to use to wrap the object + * @param target_type Type of the target object + * @param target_id Object ID of the target object + * @param aes Algorithm of the ephemeral AES key. Can be #YH_ALGO_AES128, + *#YH_ALGO_AES192 or #YH_ALGO_AES256 + * @param hash Hash algorithm. One of #YH_ALGO_RSA_OAEP_SHA1, + *#YH_ALGO_RSA_OAEP_SHA256, #YH_ALGO_RSA_OAEP_SHA384 or #YH_ALGO_RSA_OAEP_SHA512 + * @param mgf1 MGF1 algorithm. One of #YH_ALGO_MGF1_SHA1, #YH_ALGO_MGF1_SHA256, + *#YH_ALGO_MGF1_SHA384 or #YH_ALGO_MGF1_SHA512 + * @param label Label for the MGF1 algorithm + * @param label_len Label length + * @param out Wrapped object bytes + * @param out_len Length of the wrapped object + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL or if the + * command is not supported. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_export_rsa_wrapped(yh_session *session, uint16_t wrap_key_id, + yh_object_type target_type, uint16_t target_id, + yh_algorithm aes, yh_algorithm hash, + yh_algorithm mgf1, const uint8_t *oaep_label, + size_t oaep_label_len, uint8_t *out, + size_t *out_len); + +/** + * Import an object using an RSA wrap key. + * + * @param session Authenticated session to use + * @param wrapping_key_id Object ID of the Wrap Key to use to unwrap the object + * @param hash Hash algorithm. One of #YH_ALGO_RSA_OAEP_SHA1, + *#YH_ALGO_RSA_OAEP_SHA256, #YH_ALGO_RSA_OAEP_SHA384 or #YH_ALGO_RSA_OAEP_SHA512 + * @param mgf1 MGF1 algorithm. One of #YH_ALGO_MGF1_SHA1, #YH_ALGO_MGF1_SHA256, + *#YH_ALGO_MGF1_SHA384 or #YH_ALGO_MGF1_SHA512 + * @param label Label for the MGF1 algorithm + * @param label_len Label length + * @param in Wrapped object bytes + * @param in_len Length of the wrapped object + * @param target_type Type of the target object + * @param target_id Object ID of the target object + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL or if the + * command is not supported. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_import_rsa_wrapped(yh_session *session, uint16_t wrapping_key_id, + yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *label, size_t label_len, + const uint8_t *in, size_t in_len, + yh_object_type *target_type, + uint16_t *target_id); + +/** + * Import an (a)symmetric key using an RSA wrap key. + * + * @param session Authenticated session to use + * @param wrapping_key_id Object ID of the Wrap Key to use to unwrap the object + * @param type Type of object to import. One of #YH_SYMMETRIC_KEY or + *#YH_ASYMMETRIC_KEY + * @param target_id Object ID of object to import + * @param algo Key algorithm of object to import + * @param label Label of object to import + * @param domains Domains of object to import + * @param capabilities of object to import + * @param hash Hash algorithm. One of #YH_ALGO_RSA_OAEP_SHA1, + *#YH_ALGO_RSA_OAEP_SHA256, #YH_ALGO_RSA_OAEP_SHA384 or #YH_ALGO_RSA_OAEP_SHA512 + * @param mgf1 MGF1 algorithm. One of #YH_ALGO_MGF1_SHA1, #YH_ALGO_MGF1_SHA256, + *#YH_ALGO_MGF1_SHA384 or #YH_ALGO_MGF1_SHA512 + * @param oaep_label Label for the MGF1 algorithm + * @param oaep_label_len Label length + * @param in Wrapped object bytes + * @param in_len Length of the wrapped object + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL or if the + * command is not supported. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_put_rsa_wrapped_key( + yh_session *session, uint16_t wrapping_key_id, yh_object_type type, + uint16_t *target_id, yh_algorithm algo, const char *label, uint16_t domains, + const yh_capabilities *capabilities, yh_algorithm hash, yh_algorithm mgf1, + const uint8_t *oaep_label, size_t oaep_label_len, const uint8_t *in, + size_t in_len); + /** * Import a Wrap Key into the device * @@ -1835,8 +2035,9 @@ yh_rc yh_util_import_wrapped(yh_session *session, uint16_t wrapping_key_id, * @param capabilities Capabilities of the Wrap Key. See *#yh_string_to_capabilities() * @param algorithm Algorithm of the Wrap Key. Supported algorithms: - *#YH_ALGO_AES128_CCM_WRAP, #YH_ALGO_AES192_CCM_WRAP and - *#YH_ALGO_AES256_CCM_WRAP + *#YH_ALGO_AES128_CCM_WRAP, #YH_ALGO_AES192_CCM_WRAP, + *#YH_ALGO_AES256_CCM_WRAP, #YH_ALGO_RSA_2048, #YH_ALGO_RSA_3072 and + * #YH_ALGO_RSA_4096 * @param delegated_capabilities Delegated capabilities of the Wrap Key. See *#yh_string_to_capabilities() * @param in the Wrap Key to import @@ -1845,8 +2046,9 @@ yh_rc yh_util_import_wrapped(yh_session *session, uint16_t wrapping_key_id, * @return #YHR_SUCCESS if successful. * #YHR_INVALID_PARAMETERS if input parameters are NULL, in_len *is not what expected based on the algorithm and if the algorithms is not one - *of #YH_ALGO_AES128_CCM_WRAP, #YH_ALGO_AES192_CCM_WRAP or - *#YH_ALGO_AES256_CCM_WRAP. + *of #YH_ALGO_AES128_CCM_WRAP, #YH_ALGO_AES192_CCM_WRAP, + *#YH_ALGO_AES256_CCM_WRAP or an RSA key algorithm. #YHR_INVALID_PARAMETERS will + * also be returned if the firmware version does not support RSA wrap keys. * See #yh_rc for other possible errors **/ yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, @@ -1856,6 +2058,37 @@ yh_rc yh_util_import_wrap_key(yh_session *session, uint16_t *key_id, const yh_capabilities *delegated_capabilities, const uint8_t *in, size_t in_len); +/** + * Import a public RSA key as a public wrap Key into the device + * + * @param session Authenticated session to use + * @param key_id Object ID the Wrap Key. 0 if the Object ID should be generated + *by the device + * @param label Label of the Wrap Key. Maximum length is #YH_OBJ_LABEL_LEN + * @param domains Domains where the Wrap Key will be operating within. See + *#yh_string_to_domains() + * @param capabilities Capabilities of the Wrap Key. See + *#yh_string_to_capabilities() + * @param algorithm Algorithm of the Public Wrap Key. Supported algorithms: + *#YH_ALGO_RSA_2048, #YH_ALGO_RSA_3072 and #YH_ALGO_RSA_4096 + * @param delegated_capabilities Delegated capabilities of the Wrap Key. See + *#yh_string_to_capabilities() + * @param in the Public Wrap Key to import in PEM format + * @param in_len Length of the Wrap Key to import + * + * @return #YHR_SUCCESS if successful. + * #YHR_INVALID_PARAMETERS if input parameters are NULL, in_len + *is not what expected based on the algorithm and if the algorithms is not one + *of #YH_ALGO_RSA_2048, #YH_ALGO_RSA_3072 or #YH_ALGO_RSA_4096. + * See #yh_rc for other possible errors + **/ +yh_rc yh_util_import_public_wrap_key(yh_session *session, uint16_t *key_id, + const char *label, uint16_t domains, + const yh_capabilities *capabilities, + yh_algorithm algorithm, + const yh_capabilities *delegated_capabilities, + const uint8_t *in, size_t in_len); + /** * Generate a Wrap Key that can be used for export, import, wrap data and unwrap *data in the device. diff --git a/pkcs11/pkcs11y.h b/pkcs11/pkcs11y.h index 555b2f2fd..e0dc9bf6f 100644 --- a/pkcs11/pkcs11y.h +++ b/pkcs11/pkcs11y.h @@ -76,4 +76,15 @@ extern "C" { #define CKM_YUBICO_AES_CCM_WRAP \ (CKM_VENDOR_DEFINED | YUBICO_BASE_VENDOR | YH_WRAP_KEY) +/* CKM_YUBICO_AES_CCM_WRAP_PARAMS provides the parameters to the + * CKM_YUBICO_AES_CCM_WRAP mechanism. + */ +typedef struct CKM_YUBICO_AES_CCM_WRAP_PARAMS { + CK_ULONG format; // 0 = legacy, 1 = ED keys with seed +} CKM_YUBICO_AES_CCM_WRAP_PARAMS; + +typedef CKM_YUBICO_AES_CCM_WRAP_PARAMS CK_PTR CKM_YUBICO_AES_CCM_WRAP_PARAMS_PTR; + +#define CKM_YUBICO_RSA_WRAP \ + (CKM_VENDOR_DEFINED | YUBICO_BASE_VENDOR | YH_PUBLIC_WRAP_KEY) #endif diff --git a/pkcs11/tests/CMakeLists.txt b/pkcs11/tests/CMakeLists.txt index 6486543e9..3b0d74ae9 100644 --- a/pkcs11/tests/CMakeLists.txt +++ b/pkcs11/tests/CMakeLists.txt @@ -227,6 +227,12 @@ set ( common.c ) +set ( + SOURCE_ASYM_WRAP_TEST + asym_wrap_test.c + common.c +) + set ( SOURCE_PKCS11_INTERFACES_TEST pkcs11_interfaces_test.c @@ -270,12 +276,10 @@ add_test ( if (NOT ${LIBCRYPTO_VERSION} VERSION_LESS 1.1) add_executable (rsa_enc_test ${SOURCE_RSA_ENC_TEST}) - target_link_libraries ( rsa_enc_test ${LIBCRYPTO_LDFLAGS} "-ldl") - add_test ( NAME rsa_enc_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rsa_enc_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT} @@ -283,24 +287,29 @@ if (NOT ${LIBCRYPTO_VERSION} VERSION_LESS 1.1) add_executable (pss_sign_test ${SOURCE_RSA_PSS_SIGN_TEST}) - target_link_libraries ( pss_sign_test ${LIBCRYPTO_LDFLAGS} "-ldl") - add_test ( NAME pss_sign_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/pss_sign_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT} ) + add_executable (asym_wrap_test ${SOURCE_ASYM_WRAP_TEST}) + target_link_libraries ( + asym_wrap_test + ${LIBCRYPTO_LDFLAGS} + "-ldl") + add_test ( + NAME asym_wrap_test + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/asym_wrap_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT} + ) add_executable (pkcs11_interfaces_test ${SOURCE_PKCS11_INTERFACES_TEST}) - target_link_libraries ( pkcs11_interfaces_test "-ldl") - add_test ( NAME pkcs11_interfaces_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/pkcs11_interfaces_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT} diff --git a/pkcs11/tests/asym_wrap_test.c b/pkcs11/tests/asym_wrap_test.c new file mode 100644 index 000000000..5f37ae4b1 --- /dev/null +++ b/pkcs11/tests/asym_wrap_test.c @@ -0,0 +1,653 @@ +/* + * Copyright 2015-2018 Yubico AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef NDEBUG +#undef NDEBUG +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../pkcs11y.h" +#include "../lib/yubihsm.h" +#include "common.h" +#include "../common/util.h" + +const char rsa2048[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEA1mA5mATDvo4dN7gTNyXMr+Sen2vkTaYY2vDY6B59ZuQp7si9\n" + "H4sjjZjXm0/+CCuwmi287mu8zrqDYsi+cVtw+KsVdMp7EmaHUelael0JzUy+pbJs\n" + "4+PT8kZ/ytx2640j5P1H7mt/LJKcJCq7U0N4WUGx5YEyPUPGJxCgYgnXx8CSxCst\n" + "A3leSfVEnPu9kA7hztMREfyrSIyLoDcNY3y0n7yHL/uHlNAIMvOT1+RAqYHm1mxQ\n" + "sKZtDNtRBACRh4j1wKZSjK+0Wt0h15RJ4fp0i+smNR5rU0UpvpjfbeEe14DBxxE5\n" + "v0W64hsIoedJh5GD9vWARa1wF8+pi+KDHny5KwIDAQABAoIBAG4o4k+g2yl3g8IX\n" + "ICCtluIn++72FUpleM5BB2U4Db6qrnWax7yG1k0z5k9UKrjuIoEH0dc+m7Yrl8pS\n" + "V7KOh53w5ESwq8+Hyi+oVysb1iaeMjWZW2U7tLUBzzdiVOW0EGbiVG1K5f30lLHt\n" + "F3ew6w4KuSyzWCqtQgze+VuHrU9iT/jllv/KFxxaIFQeUOL0sR0CeE7gji7QE4C1\n" + "2WjYjGYb7+N+3R+nmWq3NOXBarj4vSQ42UngzO2eH6d04KsCWJzOQ/2ymCzoai+v\n" + "AaiACdrOrZFscJw8E8jyrNHDEBXvqY6j3S4444FshKiXij5gLoZZ22rY2lWcc3s+\n" + "qOPK9MkCgYEA7wVV3ON+nmS+GZKRa+RlQ0Ooh/VlYw2NJBxmPrYVTN/YQAzE2hoA\n" + "OVrXOjfuF4g/YgxvvbKcImSxUvAoemUPO0v/9NA0L1u8ehP/S+RrmvTk4IG2OlGs\n" + "LqAzpUp1t4OONKzulISmJ0LNRblwbA2ORgugVo5/X495XIwJZ5V2cX0CgYEA5Zq2\n" + "scXFMmcl99OgH5DwPtlYLlKjub4lhiyEEWghcCMDriLlbT/oqi3cJlJejsqHhI+u\n" + "pWH8WBfoVc2xNvaqzqzCJtKHxjjrEhwpjFO7e+idxwbKCB6blpBwayClVjd3UURz\n" + "TNBWPPvx0a1cMyufF1g1Nw8DwpjfTeA7WZdsVccCgYAhW0NCUlVHUZPeCcBVqEgh\n" + "fP22C58clbWOxo/WTJ7oXYzWU3HdZieF2ZGTxF5r1k3SJx4pARYdDqRYiL99ZUEc\n" + "61xLFAtUWJ8TAltsgfIqa+bNFg0SUnePAjhy5tNKywc7fq7E90Yg0IfJJTn1OmcS\n" + "i2jS64wHEATFz504YXloGQKBgQCntZCI3YqivFExanTlWbsCTUNp4pcQz2EdVlrJ\n" + "VCRIgmrnwTmPyUSrOYA6xaOn7St7mm/ZAW+O8TeVpP8yxI4TFIFkVhcypNSfj86R\n" + "X3/sjAbjH4Rm1eST38EdnuTlyvHufG8zxmGXffguTdCw18YHCTkllGQMuhkyCv2O\n" + "/Vn2dQKBgQDI1dM1/jCLg6mh51np0ZB8BgTv8mEIOvgdlD21lTpWaCeAWFJZOqWY\n" + "Oo/dNh3YcbW0I6kTpriukDSxXJ095BhcBAG8jGNxw2293A/lKeyQyGKCYOX2Foh/\n" + "dBVLu4zKddTSxwy7p5iu5fTf4m0T/BJIea16hiyWfL01UxfiS+ybRg==\n" + "-----END RSA PRIVATE KEY-----"; + +const char rsa3072[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIG5AIBAAKCAYEAy9VfKYjKmcggcZaopxgy0TxrnLTQWT+EkInakIz1J7FCRpCw\n" + "FaD/d0eIFcfUka5hRHMSPnP/XtZFoV0CpfOy8lJ/8YSPDv9v/E8Z+A5bATiRlkoD\n" + "iJENb5XhBNe3HGuF6bZUVQWPBpmNTna4t4JiMwJ9xXsnF9m0gWlqz7BSuLM5ivWg\n" + "BSOzcSCwJ3hV6nVygDqWebrULWJ87qGA26zFGBNbYZbwAxHEMAoN9Ys0mxa4WidV\n" + "estUt3fFD3Coc9BdeA7kDD844rDQ74t3joagrwqnsXyTx61g7RyrT+gAOfNvEFpx\n" + "N/1Y4FPgpwHBZLxiF2yFw/Zapq1DPTXQJY2tkHyTb5PUKxXEpOjZlOcxKvJq/x50\n" + "AZMdIxo/UZQ6OwGUTtYq1TpWzH8fa/NNKXu3ciGKvGO5HU+dYRZ2Hl8ZpVbA+PR/\n" + "wGdBd8fENFk6HEP6ztHAC0EHD34BRMXtheSvWJDoa7VuZ+RuwibdQmHOea2rRcHT\n" + "/ljEp6tJNHcp2yDtAgMBAAECggGATWgiU2uXRP8zEu/b7FjMM5l2ZHRmCv6MITe4\n" + "wNxG3WP7f0DDHfOeEHYhv+O7XfeTCKOKch0rBaDpoHXp44vAkTWzUMy+ZzuqE28W\n" + "tZT+CmCpKSHCZcJwD8gjQ+uHpktO94o+TGtn/WGiwAFl9IqXMDfp+2zhU7VhTyPx\n" + "ZB3ZzDqDx7mvo0QDiRqYyuRv/DHN4dReAKxqlzGnsBe1D7d0wcfYFB911jSRBI1M\n" + "78qFl7/xEouNcqx055ecRljKH/EoX5CRm6dGBcThem5EeRY+S0xQGg8escKB4Iiu\n" + "UIQ3drKpHxh6fwIg+rXh7DNp6MasVKjBgEAYEpRkPuJn7KIW1mxU7WTadFyYCzJp\n" + "ABtxUxnplxML4O9+7CYA0SUjOzhGzqN/Clymr7LNAiYSY9fYFc5+M0FhslE+JF7J\n" + "5DJC8fqc08yQ9gx5VAtXVor5ezw64Bahldh9y+tjkLv8DQephQhSuzWugzVIwJgp\n" + "xITOgINUdJ3DPhhekQemAo2z03MBAoHBAPojsnGhahNeu8AwhlUu+JhBiLx4kmxo\n" + "SrU5w183NhEf0jePZQ4GKxxcUkjRM49YIGMA1q646rhA+8Qsk1iKvenPXKiV+9hS\n" + "iYv5Mp4Ou1Xx7RDkdvsdE6i3NV7ZAWQiBXTEQlgufqAO465fR+5lICnhoIV5bU8y\n" + "oEzJXq3zu4VXuX7OCU1nCsNMg+5cIla0EUn932ruwIRuyHUsoWTSkxJCl033SeC9\n" + "srZIhh9oE1MLhsHhCBVd9/Z7SJ4EUgHTZQKBwQDQm/APC7puxCR/bLpYFyYOnLt8\n" + "oqZrI6PjWX7tdI35+4ZIHP2ap0bQUm/7Y22Ihy1DTb/xGU8nJ/3ff9Pkhnrg0nTO\n" + "IeOVlz5PDoywMjj0EuBWcES9b7PHMZ1V/hPjWFY/lyhIwUe2VsH8Ff1kyR3uPZHR\n" + "ijbdorPzAXx1eoSCmmcH1kO7hgV9tiKMPUcbKng+yNVjki0nJhp8fe2mwGmo83j9\n" + "ZiiNCXs5h4Blue0gsJMHhGNIEXaak5TZZLTIMukCgcEA0eY2jSeX7Z0nC4UslDqQ\n" + "HKORbCX5KMLzPdO04CdiVUhQjJLlh7khX/EQk04JaBXZR3qiq4c8X1UYb2vAUSJL\n" + "bvG0nTsOVF4eUbjRAtT12o7iEbTFKr8higgC3w5WHoJ19Z/i1EBfvUwBPodxwthU\n" + "/w/4NUjJsxWWchjgPDQ0fRY57/BQ2gTHgU6pvtDNd9guUdqZKhAiuH6F8915qTMS\n" + "etYqRSBnfBFy74c4FQ6ueJdJg1OkBtoNg2W8b+zMLAAJAoHBALY4sL6D0St2fCcu\n" + "s6vFNMIo2IErltEZxcwPXhdP68EEnCyb3k9cdTf9+sGN/Zz372rOHK8fG4wpm9LC\n" + "VzZU3jtKuytgYOtHvO7T18MFa8iQQJRg5zrOuyxxw2zdT0QU4uoTQOYkp164dCSe\n" + "lMSYBWQZNiniYMDDogrQLoZ0KhHni75FxM6maF+CXLVBxb4OIBE/315lzrlWyGYc\n" + "nh4+D028t+Apf5yLPq9nFJpHicI3W4eCdjL6xi6KYchv9pa2GQKBwBEfxlxetAKu\n" + "QnK4xSWJ0VdlI6QInZN3AjuLRonaS4OeiymZJ2nmmk8Gow3UZfQJcUJ/ucEHWbxY\n" + "eYzv2F8s9x5dHY46hhfNFi+fOb5nc2yLPGlFiD8C5Np6SjxkGPaXASRzO4HUWVB2\n" + "U43rrVtnDR+qIPFQd7V6MWEFF6TDgpEJptpqu741kkm/gT0qFdUXHiQ8tSCZMzDX\n" + "hc3roHoelzSkhPdUeKn0PVTz4LHmsw9FnpdaxzV21F98i0jNCWB28w==\n" + "-----END RSA PRIVATE KEY-----"; + +const char rsa4096[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIJKAIBAAKCAgEA5pyOip8CLSY6a15Cnrvrr9d5Xs6JSaryH3YGsfkpqe9P+NtZ\n" + "qQXYwsUPNboEJh8RF3y9l6qkJNwW/WQXPfQ46zkCrBuTDpAkSaTsGvXnb7bEUyvu\n" + "ORVgLKIqv69KrwGbP+dRm02xUJiEJiWGxzREOUgTjf+dgtIvbDBSV7SjI1C3Z7Ww\n" + "z2Lx+P5qq34c2HM1F2DL9tSHAYtOoNgcefjMSbwCDABcNwcXofn7ZHXU0NKXDVkI\n" + "3UV4SWNnoABkqmLYKqlM7T5OyFO88C35M55wWj64/WiHJQRy1RSDHhP8Il1VsAMm\n" + "j2mBg+sDL98OhyHP9PwuOZPZ5fABjsKfm5otZmPummffCA3bD5LldVLV/fAIKix+\n" + "QPeArchSD55iafP4Bz1xG5+GXwCWQ1D9DKYVRhiDGHfaJsEJOuZETteHWfs1iC9t\n" + "Oc53eTcqne1TEKS0+KZcdKUpG9LigLXRIHOq61Gg2nKvIK0l9uvNyd1+7GE7CyR+\n" + "q3UXQqQYW0HPduaFIe8l+WIDugCxdWN9yK1l9/xWnIVhhd4BZlaqqO1SvfApyCj9\n" + "RIyPuCkyw2BncRjJ3g58tmdiVUF9GPl9ycUtjCG52aNJIzFa6/jYVWik9QXkp1hA\n" + "ipjWHL0ow1bPYcRQaKJBSby95mcVdHjS3wsJylsQZydQe4v+N6T8p54NXHUCAwEA\n" + "AQKCAgBqojnHH3+CIQsiWpOzknGI/bnBfP8+cS1EHu85nF0HlwEDsWnkHi/83+II\n" + "ldsVRYhBtAx29RCxepOm14FnxGYNXm895gI52azt4LTMQqihn4FodAfTnW67NMFP\n" + "oV6HTdbb1bqGdYZoHh39BZ0sv55MXmesYWYT99y1yiJJK93Dlq835Wu8eaQp3nq0\n" + "kbwE2kDSbo/hsqQ6so/JECUawVC1R5oqsn/xcfYbj9wOt+2QIdB2+5R9vIHCbZSd\n" + "B5GEt+/8ygwoJ4eGWjIjXR6+H6UUFay0gID9PPFcVf/LCLKtsTrOCKbr6X1Z4nG2\n" + "0Q2GrrvaLEGzngpDqJcPzC7BZKlYrj+mC6+yC5exT8fbd64HCbaG6XLnKW8HZnw6\n" + "iin8ZFXkejyJEtk2oqvxFfMPxkagYMqUSqfLTnLgf/4a0fNrNDSb4el4L/5AxKby\n" + "NSqE5eATa2cSAK/cBTI1iLfTk6vAneP16S9oGAO7IxzT8d7t2mwSb1xWg2+KdAzg\n" + "NnRLIp9XTz09d21LGzW1Kj6wlPe1j+a37sdLLQ1OH6g7NA7k1m3R+RF8tQU8wIMA\n" + "/DBp3tk38N71rbuTdlxYQ+ur+zxiqoQjN1rNPTc6FMQdekDsXg1uNmyQuAUAYnvo\n" + "BIM2E0w8YQ/07ghDf3KJbIVkPV8FZgjY9zkMIP000ODMYY4oAQKCAQEA/FDQ1nxM\n" + "4EZwLw1Vt33W3SV0iLXJsUUS34Rwy+xcAfPy3WBViaqDZE4ju2kZLQhoFA4L+RIw\n" + "cyIZF+ShOkDf1lVJgBq5yDFRjkC39IAFHQf3E7HyG+3iVo29k+jb0HTR19f2arI6\n" + "LCygQPviNbvJJ+Yr8EyZslCqW0NjhQWw/z2ydWrMwmVgg2wMO/64y2LYq2RujQKi\n" + "SzMnZsIGNBVm/eB7geKDv0JB4XOfnWBml7PYUx05Pd8QHxbEhLrXvSCv75nRRSVh\n" + "lCDa+ebwXCHWIzIJPeye+5YOYrbtQ35dNbhAerP2DwyqAjG/LarZT+uGXpy90WtP\n" + "zuX1nDojCwc9cQKCAQEA6fqbyW4apLZwj2y1X0Bf6YvLRCOyPJV7XqKhM3humgFN\n" + "Ry/VXTS3JqrlcquxB1LTaXeS8FoGENMAZX+e4Yw+4MFKqMoI3YkN9EUTa3iJF/0U\n" + "ZhvMikwDPgkddvKSZQqs/jTauqEdDBe5R2hITu95PoThrOg4a2ish1nML8Y+0QYR\n" + "R0bTgt96IIq/Hmhl84NwWaC+rx5U8GcFhKuVx3tN+wys1MK2eXwUGlVt1p1KtpWx\n" + "2F/5Z9AIbfU1ovRH7+ugql0Cupu+KtczOeO5Z6BCk9CFMz32qLDYw+dXBJ+6kUcy\n" + "UImgd+u/tULJ2K7GLkAIeK6Z7SBYH2pA1XxBg2QdRQKCAQAqvg4Cp5/mRkhu0BV7\n" + "NggWAmhRWGpIa2kdEDSDdxDHC+pSciVLYuVLMql+7/jh1hC7hP2mPdyTRG13zLU7\n" + "Rw4kIuKGnwBl12T3ciM3ehBjsJu8bGKVNKEpBG3fBo1mLMP3ipAl1vdf0Fd9aq4R\n" + "aDRVW/qJhJBs0plpSGstd59aPbtjhKoXLFFDMiSIbUgkvCP0NNk9bfrMPmgoUin2\n" + "3MFLtKF3iUXEOpcqeAnMAS6f+ElnGwY9YvI6MgMscPJnCYiEUExRKFn1W/N8bhC9\n" + "qsW5xJooMVNlTzA0rMRYsKldlk7l+mJufji2knLOa6jQjxd+I5NMTJ+CbxZCVt7k\n" + "2V8hAoIBAGc3refjUY+eB/PNggl+DZGqoMXzdVpymxT5a2GYXDpGHsArotVWPwGo\n" + "3EWE5jiT2j2piUHMhOaBHqin7wAS7V4bBwOE9Po9ztEWc+WyK9BQTeJpmwbbV4bT\n" + "YJMrmVdHqV8PE/rGvliqUorkvxlLXVIuLpwnaVRAvfOLsp7UtrthENg/r2kJiwe2\n" + "DW+toGQXdMWlOtln6RKQcAfB5fY1OAZq5geJyhO3n+qqCyVlCCOZz/XjCNQ6Gq3f\n" + "QYUcfGujp6HgHCcUM4UUoD2GbzD+qsAoecpMKHbsZQOvF10r1ZLnNJQA0rB0aILe\n" + "7spO95BJoTMT20WXQijBp85F3WTIEn0CggEBAPhtkW3ezzzwCwAwN8wsJ/qNmY0Z\n" + "QWmflKPmz1SPzIkArtysDEO4/K9zBrP1/DTpOCknzpoxDZjt7iPFyOw6hIyYL4oI\n" + "+kbEBrw7mrOBLZyp0WUia+KXGzM6TA1HQxtZZMd1MbyI7ZA1l8gaidENfl5OeFgC\n" + "m43X4wJEWD2EyGX7Uvc/DsEYSey1ESQtdp6bxmBiqg53BcfL2V7VmXUv1skDJY0i\n" + "qSeb46nVbqBha4bRBPTxeh7a4DAuwn/2J2p+8bzGiHmBLNB31RNJiVtkqzQYLqzi\n" + "Gh0FCwVmpa+sRcA2c0N7gwt6YEWo4QJgUjQ16GxxszS9Y+xjw2jdYUaOdBQ=\n" + "-----END RSA PRIVATE KEY-----"; + +#define BUFSIZE 1024 + +static CK_FUNCTION_LIST_3_0_PTR p11; +static CK_SESSION_HANDLE session; + +static void generate_ec_keys(CK_OBJECT_HANDLE_PTR pubkey, + CK_OBJECT_HANDLE_PTR pvtkey) { + CK_BYTE ec_params[] = {0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x03, 0x01, 0x07}; + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG class_c = CKO_PUBLIC_KEY; + CK_ULONG kt = CKK_EC; + CK_BBOOL exportable_capability = CK_TRUE; + CK_BBOOL sign_capability = CK_TRUE; + char *label = "eckey"; + + CK_ATTRIBUTE privateKeyTemplate[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_LABEL, label, strlen(label)}, + {CKA_EXTRACTABLE, &exportable_capability, + sizeof(exportable_capability)}, + {CKA_SIGN, &sign_capability, + sizeof(sign_capability)}}; + + CK_ATTRIBUTE publicKeyTemplate[] = {{CKA_CLASS, &class_c, sizeof(class_c)}, + {CKA_EC_PARAMS, ec_params, + sizeof(ec_params)}}; + + CK_MECHANISM mech = {CKM_EC_KEY_PAIR_GEN, NULL, 0}; + + assert(p11->C_GenerateKeyPair(session, &mech, publicKeyTemplate, 2, + privateKeyTemplate, 5, pubkey, + pvtkey) == CKR_OK); + fprintf(stdout, "Generated EC key. Handle 0x%06lx\n", *pvtkey); +} + +static void generate_aes_key(CK_OBJECT_HANDLE_PTR key) { + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_KEY_TYPE type = CKK_AES; + CK_ULONG key_len = 32; + CK_BBOOL exportable_capability = CK_TRUE; + CK_BBOOL encrypt_capability = CK_TRUE; + CK_BBOOL decrypt_capability = CK_TRUE; + CK_ATTRIBUTE templ[] = + {{CKA_CLASS, &class, sizeof(class)}, + {CKA_KEY_TYPE, &type, sizeof(type)}, + {CKA_VALUE_LEN, &key_len, sizeof(CK_ULONG)}, + {CKA_EXTRACTABLE, &exportable_capability, sizeof(exportable_capability)}, + {CKA_ENCRYPT, &encrypt_capability, sizeof(encrypt_capability)}, + {CKA_DECRYPT, &decrypt_capability, sizeof(decrypt_capability)}}; + CK_MECHANISM mech = {CKM_AES_KEY_GEN, NULL, 0}; + + assert(p11->C_GenerateKey(session, &mech, templ, 6, key) == CKR_OK); + fprintf(stdout, "Generated AES key. Handle 0x%06lx\n", *key); +} + +static void import_rsa_wrapkey(const char *key, int keylen, + CK_OBJECT_HANDLE_PTR keyid) { + CK_BYTE e[] = {0x01, 0x00, 0x01}; + CK_BYTE *p, *q, *dp, *dq, *qinv; + int len = keylen / 16; + p = malloc(len); + q = malloc(len); + dp = malloc(len); + dq = malloc(len); + qinv = malloc(len); + + BIO *bio = BIO_new_mem_buf((void *) key, strlen(key)); + RSA *rsak = PEM_read_bio_RSAPrivateKey(bio, 0, 0, 0); + + const BIGNUM *bp, *bq, *biqmp, *bdmp1, *bdmq1; + RSA_get0_factors(rsak, &bp, &bq); + RSA_get0_crt_params(rsak, &bdmp1, &bdmq1, &biqmp); + BN_bn2binpad(bp, p, len); + BN_bn2binpad(bq, q, len); + BN_bn2binpad(bdmp1, dp, len); + BN_bn2binpad(bdmq1, dq, len); + BN_bn2binpad(biqmp, qinv, len); + + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG kt = CKK_RSA; + CK_BYTE id[] = {0, 0}; + CK_BBOOL wrap_capability = CK_TRUE; + CK_BBOOL sign_capability = CK_TRUE; + char *label = "rsa_wrap"; + CK_ATTRIBUTE keyTemplate[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)}, + {CKA_LABEL, label, strlen(label)}, + {CKA_UNWRAP, &wrap_capability, + sizeof(wrap_capability)}, + {CKA_SIGN, &sign_capability, + sizeof(sign_capability)}, + {CKA_PUBLIC_EXPONENT, e, sizeof(e)}, + {CKA_PRIME_1, p, len}, + {CKA_PRIME_2, q, len}, + {CKA_EXPONENT_1, dp, len}, + {CKA_EXPONENT_2, dq, len}, + {CKA_COEFFICIENT, qinv, len}}; + assert(p11->C_CreateObject(session, keyTemplate, 12, keyid) == CKR_OK); + fprintf(stdout, "Imorted RSA wrap key. Size %d. Handle 0x%06lx\n", keylen, + *keyid); + + free(p); + free(q); + free(dp); + free(dq); + free(qinv); +} + +static CK_OBJECT_HANDLE import_rsa_pub_wrapkey(uint8_t *pubkey, + size_t pubkey_len) { + + CK_OBJECT_HANDLE pubkey_handle; + + CK_ULONG class_k = CKO_PUBLIC_KEY; + CK_BBOOL wrap_capability = CK_TRUE; + char *label = "pub_rsa_wrap"; + CK_ULONG kt = CKK_RSA; + + CK_ATTRIBUTE template[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_LABEL, label, strlen(label)}, + {CKA_WRAP, &wrap_capability, + sizeof(wrap_capability)}, + {CKA_VALUE, pubkey, pubkey_len}}; + assert(p11->C_CreateObject(session, template, 5, &pubkey_handle) == CKR_OK); + return pubkey_handle; +} + +static void generate_rsa_wrapkey(int keylen, CK_OBJECT_HANDLE_PTR keyid) { + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG class_c = CKO_PUBLIC_KEY; + CK_ULONG kt = CKK_RSA; + CK_BYTE id[] = {0, 0}; + char *label = "rsa_wrap"; + CK_BBOOL wrap_capability = CK_TRUE; + CK_BBOOL sign_capability = CK_TRUE; + CK_ULONG key_len = keylen; + CK_OBJECT_HANDLE pub_keyid; + + CK_MECHANISM mech = {CKM_RSA_PKCS_KEY_PAIR_GEN, NULL, 0}; + + CK_ATTRIBUTE privateKeyTemplate[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)}, + {CKA_LABEL, label, strlen(label)}, + {CKA_UNWRAP, &wrap_capability, + sizeof(wrap_capability)}, + {CKA_SIGN, &sign_capability, + sizeof(sign_capability)}}; + + CK_ATTRIBUTE publicKeyTemplate[] = {{CKA_CLASS, &class_c, sizeof(class_c)}, + {CKA_MODULUS_BITS, &key_len, + sizeof(key_len)}}; + + assert(p11->C_GenerateKeyPair(session, &mech, publicKeyTemplate, 2, + privateKeyTemplate, 6, &pub_keyid, + keyid) == CKR_OK); + fprintf(stdout, "Generated RSA wrap key. Size %lu. Handle 0x%06lx\n", key_len, + *keyid); +} + +static void get_pub_wrapkey(CK_OBJECT_HANDLE rsa_wrapkeyid, uint8_t *pubkey, + size_t *pubkey_len) { + CK_ATTRIBUTE template[] = { + {CKA_MODULUS, pubkey, *pubkey_len}, + }; + assert(p11->C_GetAttributeValue(session, rsa_wrapkeyid, template, 1) == + CKR_OK); + *pubkey_len = template[0].ulValueLen; +} + +static void get_wrapped_data(CK_OBJECT_HANDLE wrapping_keyid, + CK_OBJECT_HANDLE keyid, uint8_t *wrapped_obj, + size_t *wrapped_obj_len, bool only_key) { + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA256, CKG_MGF1_SHA256, 0, NULL, 0}; + CK_RSA_AES_KEY_WRAP_PARAMS params = {256, &oaep_params}; + CK_MECHANISM mech = {0, ¶ms, sizeof(params)}; + + if (only_key) { + mech.mechanism = CKM_RSA_AES_KEY_WRAP; + } else { + mech.mechanism = CKM_YUBICO_RSA_WRAP; + } + assert(p11->C_WrapKey(session, &mech, wrapping_keyid, keyid, wrapped_obj, + wrapped_obj_len) == CKR_OK); +} + +static CK_OBJECT_HANDLE import_wrapped_data(CK_OBJECT_HANDLE wrapping_keyid, + uint8_t *wrapped_obj, + size_t wrapped_obj_len, + bool only_key, CK_ULONG key_type) { + + CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA256, CKG_MGF1_SHA256, 0, NULL, 0}; + CK_RSA_AES_KEY_WRAP_PARAMS params = {256, &oaep_params}; + CK_MECHANISM mech = {0, ¶ms, sizeof(params)}; + if (only_key) { + mech.mechanism = CKM_RSA_AES_KEY_WRAP; + } else { + mech.mechanism = CKM_YUBICO_RSA_WRAP; + } + + CK_OBJECT_HANDLE imported_keyhandle; + CK_ULONG kt = key_type; + CK_ULONG class_k = kt == CKK_EC ? CKO_PRIVATE_KEY : CKO_SECRET_KEY; + + if (kt == CKK_EC) { + CK_BYTE ec_params[] = {0x06, 0x08, 0x2a, 0x86, 0x48, + 0xce, 0x3d, 0x03, 0x01, 0x07}; + CK_BBOOL sign_capability = CK_TRUE; + CK_ATTRIBUTE template[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_SIGN, &sign_capability, + sizeof(sign_capability)}, + {CKA_EC_PARAMS, ec_params, sizeof(ec_params)}}; + + assert(p11->C_UnwrapKey(session, &mech, wrapping_keyid, wrapped_obj, + wrapped_obj_len, template, 4, + &imported_keyhandle) == CKR_OK); + } else { + CK_ULONG keylen = 32; + CK_BBOOL encrypt_capability = CK_TRUE; + CK_BBOOL decrypt_capability = CK_TRUE; + CK_ATTRIBUTE template[] = {{CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_DECRYPT, &decrypt_capability, + sizeof(decrypt_capability)}, + {CKA_ENCRYPT, &encrypt_capability, + sizeof(encrypt_capability)}, + {CKA_VALUE_LEN, &keylen, sizeof(keylen)}}; + + assert(p11->C_UnwrapKey(session, &mech, wrapping_keyid, wrapped_obj, + wrapped_obj_len, template, 5, + &imported_keyhandle) == CKR_OK); + } + return imported_keyhandle; +} + +static void find_rsa_wrapkey(CK_OBJECT_HANDLE keyid, size_t key_size) { + CK_BBOOL wrap_capability = CK_TRUE; + char *label = "rsa_wrap"; + CK_ATTRIBUTE template[] = {{CKA_LABEL, label, strlen(label)}, + {CKA_UNWRAP, &wrap_capability, + sizeof(wrap_capability)}}; + + CK_OBJECT_HANDLE objects[10] = {0}; + CK_ULONG n_objects = 0; + assert(p11->C_FindObjectsInit(session, template, 2) == CKR_OK); + assert(p11->C_FindObjects(session, objects, 10, &n_objects) == CKR_OK); + assert(p11->C_FindObjectsFinal(session) == CKR_OK); + assert(n_objects == 1); + assert(objects[0] == keyid); + + CK_ULONG mod_bits = 0; + CK_ATTRIBUTE value_template[] = { + {CKA_MODULUS_BITS, &mod_bits, sizeof(CK_ULONG)}}; + assert(p11->C_GetAttributeValue(session, objects[0], value_template, 1) == + CKR_OK); + assert(mod_bits == key_size); +} + +static void find_pub_rsa_wrapkey(CK_OBJECT_HANDLE keyid, size_t key_size) { + CK_BBOOL wrap_capability = CK_TRUE; + char *label = "pub_rsa_wrap"; + CK_ATTRIBUTE template[] = {{CKA_LABEL, label, strlen(label)}, + {CKA_WRAP, &wrap_capability, + sizeof(wrap_capability)}}; + + CK_OBJECT_HANDLE objects[10] = {0}; + CK_ULONG n_objects = 0; + assert(p11->C_FindObjectsInit(session, template, 2) == CKR_OK); + assert(p11->C_FindObjects(session, objects, 10, &n_objects) == CKR_OK); + assert(p11->C_FindObjectsFinal(session) == CKR_OK); + assert(n_objects == 1); + assert(objects[0] == keyid); + + CK_ULONG mod_bits = 0; + CK_ULONG key_type = 0; + CK_ATTRIBUTE value_template[] = {{CKA_MODULUS_BITS, &mod_bits, + sizeof(CK_ULONG)}, + {CKA_KEY_TYPE, &key_type, sizeof(CK_ULONG)}}; + assert(p11->C_GetAttributeValue(session, objects[0], value_template, 2) == + CKR_OK); + assert(mod_bits == key_size); + assert(key_type == CKK_RSA); +} + +static CK_OBJECT_HANDLE get_public_key_handle(CK_OBJECT_HANDLE privkey) { + CK_OBJECT_HANDLE found_obj[10] = {0}; + CK_ULONG n_found_obj = 0; + CK_ULONG class_pub = CKO_PUBLIC_KEY; + uint16_t ckaid = 0; + + CK_ATTRIBUTE idTemplate[] = { + {CKA_ID, &ckaid, sizeof(ckaid)} + }; + CK_ATTRIBUTE idClassTemplate[] = { + {CKA_ID, &ckaid, sizeof(ckaid)}, + {CKA_CLASS, &class_pub, sizeof(class_pub)} + }; + + assert(p11->C_GetAttributeValue(session, privkey, idTemplate, 1) == CKR_OK); + assert(p11->C_FindObjectsInit(session, idClassTemplate, 2) == CKR_OK); + assert(p11->C_FindObjects(session, found_obj, 10, &n_found_obj) == CKR_OK); + assert(n_found_obj == 1); + assert(p11->C_FindObjectsFinal(session) == CKR_OK); + return found_obj[0]; +} + +static void do_ecdsa_sign(CK_OBJECT_HANDLE eckey) { + CK_MECHANISM mech = {CKM_ECDSA_SHA1, NULL, 0}; + CK_BYTE sig[64] = {0}; + CK_ULONG sig_len = sizeof(sig); + + CK_BYTE data[16] = {0}; + CK_ULONG data_len = sizeof(data); + assert((RAND_bytes(data, data_len) > 0)); + assert(p11->C_SignInit(session, &mech, eckey) == CKR_OK); + assert(p11->C_Sign(session, data, sizeof(data), sig, &sig_len) == CKR_OK); + CK_OBJECT_HANDLE eckey_pub = get_public_key_handle(eckey); + assert(p11->C_VerifyInit(session, &mech, eckey_pub) == CKR_OK); + assert(p11->C_Verify(session, data, sizeof(data), sig, sig_len) == CKR_OK); +} + +static void do_aesecb_encryption(CK_OBJECT_HANDLE aeskey) { + CK_MECHANISM mech = {CKM_AES_ECB, NULL, 0}; + CK_BYTE enc[16] = {0}; + CK_ULONG enc_len = sizeof(enc); + CK_BYTE dec[16] = {0}; + CK_ULONG dec_len = sizeof(dec); + + CK_BYTE data[16] = {0}; + CK_ULONG data_len = sizeof(data); + assert((RAND_bytes(data, data_len) > 0)); + + assert(p11->C_EncryptInit(session, &mech, aeskey) == CKR_OK); + assert(p11->C_Encrypt(session, data, sizeof(data), enc, &enc_len) == CKR_OK); + assert(p11->C_DecryptInit(session, &mech, aeskey) == CKR_OK); + assert(p11->C_Decrypt(session, enc, enc_len, dec, &dec_len) == CKR_OK); + assert(dec_len == 16); + assert(memcmp(data, dec, dec_len) == 0); +} + +static void test_asym_wrapkey(CK_OBJECT_HANDLE_PTR eckey, + CK_OBJECT_HANDLE_PTR aeskey, + CK_OBJECT_HANDLE wrapkey, size_t keysize) { + CK_OBJECT_HANDLE pub_wrapkey, imported_eckey, imported_aeskey; + + find_rsa_wrapkey(wrapkey, keysize); + + // Get public key of RSA wrap key: C_GetAttributeValue(CKA_MODULUS) + uint8_t pubkey[2048] = {0}; + size_t pubkey_len = sizeof(pubkey); + get_pub_wrapkey(wrapkey, pubkey, &pubkey_len); + fprintf(stdout, "Got public key for RSA wrap key 0x%06lx. OK!\n", wrapkey); + + // Import the public key of the RSA wrap key as a public wrap key + // C_CreateObject(RSA Public Key) + pub_wrapkey = import_rsa_pub_wrapkey(pubkey, pubkey_len); + fprintf(stdout, + "Imported RSA public wrap key of size %zu. ID 0x%06lx OK!\n", + keysize, pub_wrapkey); + find_pub_rsa_wrapkey(pub_wrapkey, keysize); + + // Wrap EC key material then import it again as an RSA wrapped key + // C_WrapKey, C_UnwrapKey + uint8_t wrapped_key[2048] = {0}; + size_t wrapped_key_len = sizeof(wrapped_key); + get_wrapped_data(pub_wrapkey, *eckey, wrapped_key, &wrapped_key_len, true); + fprintf(stdout, "Got wrapped EC key material. %zu bytes. OK!\n", wrapped_key_len); + imported_eckey = + import_wrapped_data(wrapkey, wrapped_key, wrapped_key_len, true, CKK_EC); + fprintf(stdout, "Imported unwrapped EC key material. 0x%06lx. OK!\n", + imported_eckey); + do_ecdsa_sign(imported_eckey); + fprintf(stdout, "Signed using imported EC key. OK!\n"); + + // Wrap EC key object then import it again as an RSA wrapped object + // C_WrapKey, C_UnwrapKey + memset(wrapped_key, 0, sizeof(wrapped_key)); + wrapped_key_len = sizeof(wrapped_key); + get_wrapped_data(pub_wrapkey, *eckey, wrapped_key, &wrapped_key_len, false); + fprintf(stdout, "Got wrapped EC key object. %zu bytes. OK!\n", wrapped_key_len); + destroy_object(p11, session, *eckey); + fprintf(stdout, "Removed EC key object. OK!\n"); + *eckey = + import_wrapped_data(wrapkey, wrapped_key, wrapped_key_len, false, CKK_EC); + fprintf(stdout, "Imported unwrapped EC key object. OK!\n"); + do_ecdsa_sign(*eckey); + fprintf(stdout, "Signed using imported EC object. OK!\n"); + + // Wrap AES key material then import it again as an RSA wrapped key + // C_WrapKey, C_UnwrapKey + memset(wrapped_key, 0, sizeof(wrapped_key)); + wrapped_key_len = sizeof(wrapped_key); + get_wrapped_data(pub_wrapkey, *aeskey, wrapped_key, &wrapped_key_len, true); + fprintf(stdout, "Got wrapped AES key material. %zu bytes. OK!\n", wrapped_key_len); + imported_aeskey = + import_wrapped_data(wrapkey, wrapped_key, wrapped_key_len, true, CKK_AES); + fprintf(stdout, "Imported unwrapped AES key material. 0x%06lx. OK!\n", + imported_aeskey); + do_aesecb_encryption(imported_aeskey); + fprintf(stdout, "Encrypted using imported AES key. OK!\n"); + + // Wrap AES key object then import it again as an RSA wrapped object + // C_WrapKey, C_UnwrapKey + memset(wrapped_key, 0, sizeof(wrapped_key)); + wrapped_key_len = sizeof(wrapped_key); + get_wrapped_data(pub_wrapkey, *aeskey, wrapped_key, &wrapped_key_len, false); + fprintf(stdout, "Got wrapped AES key object. %zu bytes. OK!\n", wrapped_key_len); + destroy_object(p11, session, *aeskey); + fprintf(stdout, "Removed AES key object. OK!\n"); + *aeskey = + import_wrapped_data(wrapkey, wrapped_key, wrapped_key_len, false, CKK_AES); + fprintf(stdout, "Imported unwrapped AES key object. OK!\n"); + do_aesecb_encryption(*aeskey); + fprintf(stdout, "Signed using imported AES object. OK!\n"); + + // Delete test keys + destroy_object(p11, session, imported_eckey); + destroy_object(p11, session, imported_aeskey); + destroy_object(p11, session, pub_wrapkey); + +} + +static bool is_asymwrap_supported(void) { + CK_SESSION_INFO info; + CK_RV r; + + if ((r = p11->C_GetSessionInfo(session, &info)) != CKR_OK) { + fprintf(stderr, "C_GetSessionInfo (r = %lu)\n", r); + return CKR_FUNCTION_FAILED; + } + + CK_MECHANISM_TYPE m[128]; + CK_ULONG n = sizeof(m) / sizeof(m[0]); + if ((r = p11->C_GetMechanismList(info.slotID, m, &n)) != CKR_OK) { + fprintf(stderr, "C_GetMechanismList (r = %lu)\n", r); + return CKR_FUNCTION_FAILED; + } + + for (CK_ULONG i = 0; i < n; i++) { + if (m[i] == CKM_YUBICO_RSA_WRAP) { + return true; + } + } + return false; +} + +int main(int argc, char **argv) { + + if (argc != 2) { + fprintf(stderr, "usage: /path/to/yubihsm_pkcs11/module\n"); + exit(EXIT_FAILURE); + } + + void *handle = open_module(argv[1]); + p11 = get_function_list(handle); + session = open_session(p11); + print_session_state(p11, session); + + if (!is_asymwrap_supported()) { + goto clean; + } + + const char *keys[] = {rsa2048, rsa3072, rsa4096}; + size_t keysizes[] = {2048, 3072, 4096}; + +// Generate EC key to wrap + CK_OBJECT_HANDLE ec_pubkey, ec_privkey, aes_key; + generate_ec_keys(&ec_pubkey, &ec_privkey); + generate_aes_key(&aes_key); + + for (int i = 0; i < 3; i++) { + CK_OBJECT_HANDLE wrapkey; + + generate_rsa_wrapkey(keysizes[i], &wrapkey); + assert(wrapkey != 0); + test_asym_wrapkey(&ec_privkey, &aes_key, wrapkey, keysizes[i]); + destroy_object(p11, session, wrapkey); + + wrapkey = 0; + import_rsa_wrapkey(keys[i], keysizes[i], &wrapkey); + assert(wrapkey != 0); + test_asym_wrapkey(&ec_privkey, &aes_key, wrapkey, keysizes[i]); + destroy_object(p11, session, wrapkey); + } + destroy_object(p11, session, ec_privkey); + printf("OK!\n"); + +clean: + close_session(p11, session); + close_module(handle); + return (EXIT_SUCCESS); +} diff --git a/pkcs11/util_pkcs11.c b/pkcs11/util_pkcs11.c index 613224d88..da6c609db 100644 --- a/pkcs11/util_pkcs11.c +++ b/pkcs11/util_pkcs11.c @@ -168,13 +168,21 @@ static CK_ULONG encode_length(CK_BYTE_PTR buffer, CK_ULONG length) { } } -static void add_mech(CK_MECHANISM_TYPE *buf, CK_ULONG_PTR count, - CK_MECHANISM_TYPE item) { - for (CK_ULONG i = 0; i < *count; i++) { +static bool find_mech(CK_MECHANISM_TYPE_PTR buf, CK_ULONG count, + CK_MECHANISM_TYPE item) { + for (CK_ULONG i = 0; i < count; i++) { if (buf[i] == item) { - return; + return true; } } + return false; +} + +static void add_mech(CK_MECHANISM_TYPE_PTR buf, CK_ULONG_PTR count, + CK_MECHANISM_TYPE item) { + if (find_mech(buf, *count, item)) { + return; + } buf[*count] = item; *count = *count + 1; } @@ -204,9 +212,8 @@ CK_RV get_mechanism_list(yubihsm_pkcs11_slot *slot, } } - CK_MECHANISM_TYPE buffer[128] = { - 0}; // NOTE: this is a bit hardcoded, but much more - // than what we might add below. + // NOTE: this is a bit hardcoded, but much more than what we might add below. + CK_MECHANISM_TYPE buffer[128] = {0}; CK_ULONG items = 0; for (size_t i = 0; i < slot->n_algorithms; i++) { @@ -322,6 +329,18 @@ CK_RV get_mechanism_list(yubihsm_pkcs11_slot *slot, case YH_ALGO_RSA_OAEP_SHA384: case YH_ALGO_RSA_OAEP_SHA512: add_mech(buffer, &items, CKM_RSA_PKCS_OAEP); + if(find_mech(buffer, items, CKM_AES_KEY_WRAP_KWP)) { + add_mech(buffer, &items, CKM_YUBICO_RSA_WRAP); + add_mech(buffer, &items, CKM_RSA_AES_KEY_WRAP); + } + break; + + case YH_ALGO_AES_KWP: + add_mech(buffer, &items, CKM_AES_KEY_WRAP_KWP); + if(find_mech(buffer, items, CKM_RSA_PKCS_OAEP)) { + add_mech(buffer, &items, CKM_YUBICO_RSA_WRAP); + add_mech(buffer, &items, CKM_RSA_AES_KEY_WRAP); + } break; case YH_ALGO_AES128_CCM_WRAP: @@ -529,6 +548,14 @@ CK_RV get_mechanism_info(yubihsm_pkcs11_slot *slot, CK_MECHANISM_TYPE type, pInfo->flags = CKF_HW | CKF_DECRYPT | CKF_ENCRYPT; break; + case CKM_YUBICO_RSA_WRAP: + case CKM_RSA_AES_KEY_WRAP: + find_minmax_rsa_key_length_in_bits(slot->algorithms, slot->n_algorithms, + &pInfo->ulMinKeySize, + &pInfo->ulMaxKeySize); + pInfo->flags = CKF_HW | CKF_WRAP | CKF_UNWRAP; + break; + case CKM_RSA_PKCS_KEY_PAIR_GEN: find_minmax_rsa_key_length_in_bits(slot->algorithms, slot->n_algorithms, &pInfo->ulMinKeySize, @@ -628,6 +655,12 @@ CK_RV get_mechanism_info(yubihsm_pkcs11_slot *slot, CK_MECHANISM_TYPE type, pInfo->flags = CKF_DIGEST; break; + case CKM_AES_KEY_WRAP_KWP: + pInfo->ulMaxKeySize = 256; + pInfo->ulMinKeySize = 128; + pInfo->flags = CKF_HW; + break; + case CKM_YUBICO_AES_CCM_WRAP: pInfo->ulMaxKeySize = 256; pInfo->ulMinKeySize = 128; @@ -839,9 +872,22 @@ yubihsm_pkcs11_object_desc *get_object_desc(yubihsm_pkcs11_slot *slot, return _get_object_desc(slot, id, type, sequence); } -static bool check_domains(uint16_t subset_domains, uint16_t domains) { +static bool check_domains(uint16_t subset_domains, yubihsm_pkcs11_slot *slot) { + uint16_t authkey_domains = slot->authkey_domains; + if (authkey_domains == 0) { + yh_object_descriptor authkey = {0}; + if (yh_util_get_object_info(slot->device_session, + slot->device_session->authkey_id, + YH_AUTHENTICATION_KEY, + &authkey) != YHR_SUCCESS) { + DBG_ERR("Unable to check authentication key domains"); + return false; + } + authkey_domains = authkey.domains; + } + for (uint16_t i = 0; i < YH_MAX_DOMAINS; i++) { - if ((subset_domains & (1 << i)) && !(domains & (1 << i))) { + if ((subset_domains & (1 << i)) && !(authkey_domains & (1 << i))) { return false; } } @@ -853,7 +899,7 @@ CK_RV write_meta_object(yubihsm_pkcs11_slot *slot, yh_capabilities *target_capabilities, uint16_t target_domains, bool replace) { - if (!check_domains(target_domains, slot->authkey_domains)) { + if (!check_domains(target_domains, slot)) { DBG_ERR( "Current user's domain access does not match target_object domains."); return CKR_FUNCTION_REJECTED; @@ -1082,9 +1128,8 @@ static void get_capability_attribute(yh_object_descriptor *object, static CK_RV add_mech_type(CK_BYTE_PTR value, CK_ULONG max, CK_ULONG_PTR length, CK_MECHANISM_TYPE mech) { - for (CK_ULONG i = 0; i < *length; i += sizeof(CK_MECHANISM_TYPE)) { - if (*(CK_MECHANISM_TYPE_PTR)(value + i) == mech) - return CKR_OK; + if(find_mech((CK_MECHANISM_TYPE_PTR)value, *length / sizeof(CK_MECHANISM_TYPE), mech)) { + return CKR_OK; } if (*length + sizeof(CK_MECHANISM_TYPE) > max) return CKR_BUFFER_TOO_SMALL; @@ -1371,7 +1416,8 @@ static CK_RV get_attribute_secret_key(CK_ATTRIBUTE_TYPE type, break; case CKA_VALUE_LEN: - if (object->type == YH_WRAP_KEY || object->type == YH_SYMMETRIC_KEY) { + if (object->type == YH_WRAP_KEY || object->type == YH_SYMMETRIC_KEY || + object->type == YH_HMAC_KEY) { size_t key_length = 0; yh_rc yrc = yh_get_key_bitlength(object->algorithm, &key_length); if (yrc != YHR_SUCCESS) { @@ -1542,6 +1588,8 @@ static CK_RV get_attribute_private_key(CK_ATTRIBUTE_TYPE type, } *length = sizeof(CK_KEY_TYPE); + } else if (object->type == YH_WRAP_KEY && yh_is_rsa(object->algorithm)) { + *((CK_KEY_TYPE *) value) = CKK_RSA; } else { return CKR_FUNCTION_FAILED; } @@ -1622,11 +1670,18 @@ static CK_RV get_attribute_private_key(CK_ATTRIBUTE_TYPE type, } break; + case CKA_WRAP: // applicable for RSA wrap keys + get_capability_attribute(object, "export-wrapped", true, value, + length, NULL); + break; + case CKA_UNWRAP: // applicable for RSA wrap keys + get_capability_attribute(object, "import-wrapped", true, value, + length, NULL); + break; + case CKA_SIGN_RECOVER: case CKA_VERIFY: case CKA_VERIFY_RECOVER: - case CKA_WRAP: - case CKA_UNWRAP: case CKA_WRAP_WITH_TRUSTED: case CKA_ALWAYS_AUTHENTICATE: *((CK_BBOOL *) value) = CK_FALSE; @@ -1711,7 +1766,7 @@ static CK_RV get_attribute_private_key(CK_ATTRIBUTE_TYPE type, p += resplen; *length = p - (uint8_t *) value; } else if (yh_is_ed(object->algorithm)) { - uint8_t resp[2048]; + uint8_t resp[2048] = {0}; size_t resplen = sizeof(resp); yh_rc yrc = @@ -1750,8 +1805,9 @@ static CK_RV get_attribute_private_key(CK_ATTRIBUTE_TYPE type, uint8_t resp[2048] = {0}; size_t resp_len = sizeof(resp); - yh_rc yrc = yh_util_get_public_key(session->slot->device_session, - object->id, resp, &resp_len, NULL); + yh_rc yrc = + yh_util_get_public_key_ex(session->slot->device_session, object->type, + object->id, resp, &resp_len, NULL); if (yrc != YHR_SUCCESS) { return yrc_to_rv(yrc); } @@ -1787,7 +1843,7 @@ static CK_RV get_attribute_private_key(CK_ATTRIBUTE_TYPE type, return CKR_OK; } -static CK_RV load_public_key(yh_session *session, uint16_t id, EVP_PKEY **key) { +static CK_RV load_public_key(yh_session *session, uint16_t id, uint8_t type, EVP_PKEY **key) { uint8_t data[1024] = {0}; size_t data_len = sizeof(data) - 1; @@ -1800,7 +1856,8 @@ static CK_RV load_public_key(yh_session *session, uint16_t id, EVP_PKEY **key) { EC_POINT *ec_point = NULL; yh_algorithm algo; - yh_rc yrc = yh_util_get_public_key(session, id, data + 1, &data_len, &algo); + yh_rc yrc = + yh_util_get_public_key_ex(session, type, id, data + 1, &data_len, &algo); if (yrc != YHR_SUCCESS) { return yrc_to_rv(yrc); } @@ -2030,6 +2087,9 @@ static CK_RV get_attribute_public_key(CK_ATTRIBUTE_TYPE type, *((CK_KEY_TYPE *) value) = CKK_VENDOR_DEFINED; // TODO: argh } *length = sizeof(CK_KEY_TYPE); + } else if (object->type == YH_PUBLIC_WRAP_KEY && yh_is_rsa(object->algorithm)) { + *((CK_KEY_TYPE *) value) = CKK_RSA; + *length = sizeof(CK_KEY_TYPE); } else { return CKR_FUNCTION_FAILED; } @@ -2129,7 +2189,7 @@ static CK_RV get_attribute_public_key(CK_ATTRIBUTE_TYPE type, p += resplen; *length = p - (uint8_t *) value; } else if (yh_is_ed(object->algorithm)) { - uint8_t resp[2048]; + uint8_t resp[2048] = {0}; size_t resplen = sizeof(resp); yh_rc yrc = @@ -2168,8 +2228,9 @@ static CK_RV get_attribute_public_key(CK_ATTRIBUTE_TYPE type, uint8_t resp[2048] = {0}; size_t resp_len = sizeof(resp); - yh_rc yrc = yh_util_get_public_key(session->slot->device_session, - object->id, resp, &resp_len, NULL); + yh_rc yrc = + yh_util_get_public_key_ex(session->slot->device_session, object->type, + object->id, resp, &resp_len, NULL); if (yrc != YHR_SUCCESS) { return yrc_to_rv(yrc); } @@ -2197,7 +2258,7 @@ static CK_RV get_attribute_public_key(CK_ATTRIBUTE_TYPE type, EVP_PKEY *pkey = NULL; CK_RV rv = - load_public_key(session->slot->device_session, object->id, &pkey); + load_public_key(session->slot->device_session, object->id, object->type, &pkey); if (rv != CKR_OK) { EVP_PKEY_free(pkey); return rv; @@ -2229,6 +2290,14 @@ static CK_RV get_attribute(CK_ATTRIBUTE_TYPE type, yh_object_descriptor *object, session); case YH_WRAP_KEY: + if(yh_is_rsa(object->algorithm)) { + return get_attribute_private_key(type, object, meta_object, value, length, + session); + } else { + return get_attribute_secret_key(type, object, meta_object, value, length); + } + break; + case YH_HMAC_KEY: case YH_SYMMETRIC_KEY: return get_attribute_secret_key(type, object, meta_object, value, length); @@ -2237,6 +2306,7 @@ static CK_RV get_attribute(CK_ATTRIBUTE_TYPE type, yh_object_descriptor *object, return get_attribute_private_key(type, object, meta_object, value, length, session); case YH_PUBLIC_KEY: + case YH_PUBLIC_WRAP_KEY: return get_attribute_public_key(type, object, meta_object, value, length, session); @@ -2339,12 +2409,9 @@ CK_RV check_sign_mechanism(yubihsm_pkcs11_slot *slot, return rv; } - for (CK_ULONG i = 0; i < count; i++) { - if (pMechanism->mechanism == mechanisms[i]) { - return CKR_OK; - } + if (find_mech(mechanisms, count, pMechanism->mechanism)) { + return CKR_OK; } - return CKR_MECHANISM_INVALID; } @@ -2367,12 +2434,9 @@ CK_RV check_decrypt_mechanism(yubihsm_pkcs11_slot *slot, return rv; } - for (CK_ULONG i = 0; i < count; i++) { - if (pMechanism->mechanism == mechanisms[i]) { - return CKR_OK; - } + if (find_mech(mechanisms, count, pMechanism->mechanism)) { + return CKR_OK; } - return CKR_MECHANISM_INVALID; } @@ -2397,7 +2461,9 @@ CK_RV check_wrap_mechanism(yubihsm_pkcs11_slot *slot, CK_MECHANISM_TYPE mechanisms[128] = {0}; CK_ULONG count = 128; - if (pMechanism->mechanism != CKM_YUBICO_AES_CCM_WRAP) { + if (pMechanism->mechanism != CKM_YUBICO_AES_CCM_WRAP && + pMechanism->mechanism != CKM_YUBICO_RSA_WRAP && + pMechanism->mechanism != CKM_RSA_AES_KEY_WRAP) { return CKR_MECHANISM_INVALID; } @@ -2406,12 +2472,9 @@ CK_RV check_wrap_mechanism(yubihsm_pkcs11_slot *slot, return rv; } - for (CK_ULONG i = 0; i < count; i++) { - if (pMechanism->mechanism == mechanisms[i]) { - return CKR_OK; - } + if (find_mech(mechanisms, count, pMechanism->mechanism)) { + return CKR_OK; } - return CKR_MECHANISM_INVALID; } @@ -3368,7 +3431,7 @@ CK_RV perform_verify(yh_session *session, yubihsm_pkcs11_op_info *op_info, unsigned int md_len = sizeof(md_data); EVP_PKEY_CTX *ctx = NULL; - rv = load_public_key(session, op_info->op.verify.key_id, &key); + rv = load_public_key(session, op_info->op.verify.key_id, YH_ASYMMETRIC_KEY, &key); if (rv != CKR_OK) { goto pv_failure; } @@ -3626,7 +3689,7 @@ CK_RV perform_rsa_encrypt(yh_session *session, yubihsm_pkcs11_op_info *op_info, EVP_PKEY *public_key = NULL; EVP_PKEY_CTX *ctx = NULL; - CK_RV rv = load_public_key(session, op_info->op.encrypt.key_id, &public_key); + CK_RV rv = load_public_key(session, op_info->op.encrypt.key_id, YH_ASYMMETRIC_KEY, &public_key); if (rv != CKR_OK) { DBG_ERR("Failed to load public key"); goto rsa_enc_cleanup; @@ -4213,6 +4276,14 @@ CK_RV parse_rsa_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, } break; + case CKA_ENCRYPT: + if ((rv = set_template_attribute(&template->encrypt, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_ENCRYPT inconsistent in template"); + return rv; + } + break; + case CKA_TOKEN: case CKA_PRIVATE: case CKA_SENSITIVE: @@ -4225,28 +4296,40 @@ CK_RV parse_rsa_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, break; case CKA_WRAP: + if ((rv = set_template_attribute(&template->wrap, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_WRAP inconsistent in template"); + return rv; + } + break; + + case CKA_UNWRAP: + if ((rv = set_template_attribute(&template->unwrap, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_UNWRAP inconsistent in template"); + return rv; + } + break; + case CKA_DERIVE: - case CKA_ENCRYPT: + if ((rv = set_template_attribute(&template->derive, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_DERIVE inconsistent in template"); + return rv; + } + break; + case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_MODIFIABLE: case CKA_COPYABLE: case CKA_ALWAYS_AUTHENTICATE: + case CKA_SIGN_RECOVER: if ((rv = check_bool_attribute(pTemplate[i].pValue, false)) != CKR_OK) { return rv; } break; - case CKA_SIGN_RECOVER: - case CKA_UNWRAP: { - CK_BBOOL b_val = *(CK_BBOOL *) pTemplate[i].pValue; - if (b_val != CK_FALSE) { - DBG_ERR("Boolean false check failed for attribute 0x%lx. This will " - "be ignored", - pTemplate[i].type); - } - } break; - case CKA_MODULUS: case CKA_PRIVATE_EXPONENT: case CKA_EXPONENT_1: @@ -4905,7 +4988,31 @@ CK_RV parse_rsa_generate_template(CK_ATTRIBUTE_PTR pPublicKeyTemplate, break; case CKA_WRAP: + if ((rv = check_bool_attribute(pPrivateKeyTemplate[i].pValue, false)) != + CKR_OK) { + DBG_ERR("Boolean false check failed for attribute CKA_WRAP"); + return rv; + } + break; + + case CKA_UNWRAP: // pkcs11-tool sets this on private keys + if ((rv = set_template_attribute(&template->unwrap, + pPrivateKeyTemplate[i].pValue)) != + CKR_OK) { + DBG_ERR("CKA_UNWRAP inconsistent in template"); + return rv; + } + break; + case CKA_DERIVE: + if ((rv = set_template_attribute(&template->derive, + pPrivateKeyTemplate[i].pValue)) != + CKR_OK) { + DBG_ERR("CKA_DERIVE inconsistent in template"); + return rv; + } + break; + case CKA_ENCRYPT: case CKA_SIGN_RECOVER: case CKA_VERIFY: @@ -4920,7 +5027,6 @@ CK_RV parse_rsa_generate_template(CK_ATTRIBUTE_PTR pPublicKeyTemplate, } break; - case CKA_UNWRAP: // pkcs11-tool sets this on private keys case CKA_SUBJECT: break; @@ -5485,6 +5591,223 @@ CK_RV parse_wrap_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, } } +CK_RV set_object_type(uint8_t *type, uint8_t expected_type) { + if (*type == 0) { + *type = expected_type; + return CKR_OK; + } + if (*type != expected_type) { + DBG_ERR("Mismatch in attribute values"); + return CKR_TEMPLATE_INCONSISTENT; + } + return CKR_OK; +} + +CK_RV +parse_rsa_wrappedkey_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + yubihsm_pkcs11_object_template *template, + pkcs11_meta_object *pkcs11meta, CK_BYTE* type) { + CK_RV rv; + CK_ULONG ckk_type = 0; + uint8_t *ecparams = NULL; + uint16_t ecparams_len = 0; + for (CK_ULONG i = 0; i < ulCount; i++) { + switch (pTemplate[i].type) { + case CKA_CLASS: { + uint32_t value = *((CK_ULONG_PTR) (pTemplate[i].pValue)); + switch (value) { + case CKO_SECRET_KEY: + rv = set_object_type(type, YH_SYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + break; + case CKO_PRIVATE_KEY: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + break; + default: + DBG_ERR("Unsupported wrapped key class"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } break; + case CKA_KEY_TYPE: { + ckk_type = *((CK_ULONG_PTR) (pTemplate[i].pValue)); + switch (ckk_type) { + case CKK_RSA: + case CKK_EC: + case CKK_EC_EDWARDS: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + break; + case CKK_AES: + rv = set_object_type(type, YH_SYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + break; + default: + DBG_ERR("Unsupported wrapped key type 0x%lx", ckk_type); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } break; + case CKA_ID: + rv = + parse_meta_id_template(template, pkcs11meta, true, + pTemplate[i].pValue, pTemplate[i].ulValueLen); + if (rv != CKR_OK) { + return rv; + } + break; + + case CKA_LABEL: + rv = parse_meta_label_template(template, pkcs11meta, true, + pTemplate[i].pValue, + pTemplate[i].ulValueLen); + if (rv != CKR_OK) { + return rv; + } + break; + + case CKA_MODULUS_BITS: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + switch (*((CK_ULONG_PTR) pTemplate[i].pValue)) { + case 2048: + template->algorithm = YH_ALGO_RSA_2048; + break; + + case 3072: + template->algorithm = YH_ALGO_RSA_3072; + break; + + case 4096: + template->algorithm = YH_ALGO_RSA_4096; + break; + + default: + DBG_ERR("CKA_MODULUS_BITS wrong length in PublicKeyTemplate (%lu)", + *((CK_ULONG_PTR) pTemplate[i].pValue)); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_EC_PARAMS: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + if (ecparams == NULL) { + ecparams = (CK_BYTE_PTR) pTemplate[i].pValue; + ecparams_len = pTemplate[i].ulValueLen; + } else { + DBG_ERR("CKA_PUBLIC_EXPONENT inconsistent in PublicKeyTemplate"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_VALUE_LEN: + rv = set_object_type(type, YH_SYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + CK_ULONG keylen = *((CK_ULONG_PTR) pTemplate[i].pValue); + switch (keylen) { + case 16: + template->algorithm = YH_ALGO_AES128; + break; + case 24: + template->algorithm = YH_ALGO_AES192; + break; + case 32: + template->algorithm = YH_ALGO_AES256; + break; + default: + DBG_ERR("Invalid key length %lu", keylen); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_DECRYPT: + if ((rv = set_template_attribute(&template->encrypt, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_DECRYPT inconsistent in pTemplate"); + return rv; + } + break; + case CKA_ENCRYPT: + if ((rv = set_template_attribute(&template->decrypt, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_DECRYPT inconsistent in pTemplate"); + return rv; + } + break; + + case CKA_SIGN: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + if ((rv = set_template_attribute(&template->sign, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_SIGN inconsistent in pTemplate"); + return rv; + } + break; + + case CKA_DERIVE: + rv = set_object_type(type, YH_ASYMMETRIC_KEY); + if (rv != CKR_OK) { + return rv; + } + if ((rv = set_template_attribute(&template->derive, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_DERIVE inconsistent in PrivateKeyTemplate"); + return rv; + } + break; + + case CKA_EXTRACTABLE: + if ((rv = set_template_attribute(&template->exportable, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_EXTRACTABLE inconsistent in PrivateKeyTemplate"); + return rv; + } + break; + + default: + DBG_ERR("unknown attribute 0x%lx", pTemplate[i].type); + break; + } + } + + if (ecparams != NULL) { + uint16_t key_len; + if (ckk_type == CKK_EC) { + rv = + parse_ecparams(ecparams, ecparams_len, &template->algorithm, &key_len); + } else if (ckk_type == CKK_EC_EDWARDS) { + rv = + parse_edparams(ecparams, ecparams_len, &template->algorithm, &key_len); + } else { + DBG_ERR("Found EC parameters for non EC keys"); + return CKR_TEMPLATE_INCONSISTENT; + } + if (rv != CKR_OK) { + DBG_ERR("Failed to parse CKA_ECPARAMS"); + return rv; + } + } + + return CKR_OK; +} + CK_RV parse_aes_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, yubihsm_pkcs11_object_template *template, bool generate) { @@ -5595,7 +5918,6 @@ CK_RV populate_template(int type, void *object, CK_ATTRIBUTE_PTR pTemplate, DBG_INFO("Getting attribute 0x%lx", pTemplate[i].type); CK_ULONG len = sizeof(tmp); CK_RV attribute_rc; - if (type == ECDH_KEY_TYPE) { ecdh_session_key *key = object; attribute_rc = diff --git a/pkcs11/util_pkcs11.h b/pkcs11/util_pkcs11.h index 8deb3fb2c..be4090383 100644 --- a/pkcs11/util_pkcs11.h +++ b/pkcs11/util_pkcs11.h @@ -123,6 +123,7 @@ bool create_session(yubihsm_pkcs11_slot *slot, CK_FLAGS flags, void release_session(yubihsm_pkcs11_context *ctx, yubihsm_pkcs11_session *session); +CK_RV set_object_type(uint8_t *type, uint8_t expected_type); CK_RV set_template_attribute(yubihsm_pkcs11_attribute *attribute, CK_BBOOL *value); CK_RV parse_rsa_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, @@ -137,6 +138,11 @@ CK_RV parse_hmac_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_RV parse_wrap_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, yubihsm_pkcs11_object_template *template, yh_algorithm algorithm, bool generate); +CK_RV parse_rsa_wrappedkey_template(CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount, + yubihsm_pkcs11_object_template *template, + pkcs11_meta_object *pkcs11meta, + CK_BYTE *type); CK_RV parse_aes_template(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, yubihsm_pkcs11_object_template *template, bool generate); diff --git a/pkcs11/yubihsm_pkcs11.c b/pkcs11/yubihsm_pkcs11.c index ec1ca87a5..29dfa59bf 100644 --- a/pkcs11/yubihsm_pkcs11.c +++ b/pkcs11/yubihsm_pkcs11.c @@ -462,14 +462,14 @@ static CK_RV C_GetInfo_Ex(CK_INFO_PTR pInfo, CK_VERSION cryptokiVersion) { memcpy((char *) pInfo->libraryDescription, YUBIHSM_PKCS11_LIBDESC, strlen(YUBIHSM_PKCS11_LIBDESC)); - CK_VERSION libraryVersion = {VERSION_MAJOR, (VERSION_MINOR * 10) + VERSION_PATCH}; + CK_VERSION libraryVersion = {VERSION_MAJOR, + (VERSION_MINOR * 10) + VERSION_PATCH}; pInfo->libraryVersion = libraryVersion; return CKR_OK; } - CK_DEFINE_FUNCTION(CK_RV, C_GetInfo)(CK_INFO_PTR pInfo) { DIN; @@ -707,16 +707,17 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetTokenInfo) memcpy((char *) pInfo->model, s, l); memset(pInfo->serialNumber, ' ', sizeof(pInfo->serialNumber)); - l = snprintf((char *) pInfo->serialNumber, sizeof(pInfo->serialNumber), "%08u", serial); + l = snprintf((char *) pInfo->serialNumber, sizeof(pInfo->serialNumber), + "%08u", serial); pInfo->serialNumber[l] = ' '; pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; pInfo->ulMaxSessionCount = - CK_EFFECTIVELY_INFINITE; // maximum number of sessions that can be opened - // with the token at one time by a single - // application + CK_EFFECTIVELY_INFINITE; // maximum number of sessions that can be opened + // with the token at one time by a single + // application pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; // number of sessions that this application // currently has open with the token @@ -1221,6 +1222,40 @@ CK_DEFINE_FUNCTION(CK_RV, C_Logout)(CK_SESSION_HANDLE hSession) { return rv; } +static yh_rc set_wrapkey_capabilities(yubihsm_pkcs11_object_template *template, + yh_capabilities *capabilities) { + yh_rc rc; + if (template->wrap == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("export-wrapped", capabilities); + if (rc != YHR_SUCCESS) { + return rc; + } + } + + if (template->unwrap == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("import-wrapped", capabilities); + if (rc != YHR_SUCCESS) { + return rc; + } + } + + if (template->encrypt == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("wrap-data", capabilities); + if (rc != YHR_SUCCESS) { + return rc; + } + } + + if (template->decrypt == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("unwrap-data", capabilities); + if (rc != YHR_SUCCESS) { + return rc; + } + } + + return YHR_SUCCESS; +} + CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { @@ -1340,34 +1375,61 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) DBG_INFO("parsed RSA key, algorithm: %d, objlen: %d", template.algorithm, template.objlen); - if (template.sign == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("sign-pkcs,sign-pss", &capabilities); + uint8_t p[512], q[512]; + + set_component(p, template.obj.rsa.p, template.objlen); + set_component(q, template.obj.rsa.q, template.objlen); + + BN_free(template.obj.rsa.p); + BN_free(template.obj.rsa.q); + + if (template.unwrap) { + type = YH_WRAP_KEY; + + rc = set_wrapkey_capabilities(&template, &capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); goto c_co_out; } - } - if (template.decrypt == ATTRIBUTE_TRUE) { - rc = - yh_string_to_capabilities("decrypt-pkcs,decrypt-oaep", &capabilities); + rc = yh_string_to_capabilities("all", &delegated_capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); goto c_co_out; } - } - uint8_t p[512], q[512]; + uint8_t key[1024] = {0}; + memcpy(key, p, template.objlen); + memcpy(key + template.objlen, q, template.objlen); - set_component(p, template.obj.rsa.p, template.objlen); - set_component(q, template.obj.rsa.q, template.objlen); + rc = yh_util_import_wrap_key(session->slot->device_session, &template.id, + template.label, 0xffff, &capabilities, + template.algorithm, &delegated_capabilities, + key, template.objlen * 2); - BN_free(template.obj.rsa.p); - BN_free(template.obj.rsa.q); + } else { - rc = yh_util_import_rsa_key(session->slot->device_session, &template.id, - template.label, 0xffff, &capabilities, - template.algorithm, p, q); + if (template.sign == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("sign-pkcs,sign-pss", &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_co_out; + } + } + + if (template.decrypt == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("decrypt-pkcs,decrypt-oaep", + &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_co_out; + } + } + + rc = yh_util_import_rsa_key(session->slot->device_session, &template.id, + template.label, 0xffff, &capabilities, + template.algorithm, p, q); + } if (rc != YHR_SUCCESS) { DBG_ERR("Failed importing RSA key to device: %s", yh_strerror(rc)); rv = yrc_to_rv(rc); @@ -1429,10 +1491,9 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) } rc = yh_util_import_ed_key(session->slot->device_session, &template.id, - template.label, 0xffff, &capabilities, - template.algorithm, - template.obj.buf); - if(rc != YHR_SUCCESS) { + template.label, 0xffff, &capabilities, + template.algorithm, template.obj.buf); + if (rc != YHR_SUCCESS) { DBG_ERR("Failed importing ED key to device"); rv = yrc_to_rv(rc); goto c_co_out; @@ -1490,7 +1551,6 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) key_type.d == CKK_YUBICO_AES192_CCM_WRAP || key_type.d == CKK_YUBICO_AES256_CCM_WRAP) { yh_algorithm algo = key_type.d & 0xff; - type = YH_WRAP_KEY; rv = parse_wrap_template(pTemplate, ulCount, &template, algo, false); if (rv != CKR_OK) { goto c_co_out; @@ -1498,36 +1558,10 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) DBG_INFO("parsed WRAP key, objlen: %d", template.objlen); - if (template.wrap == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("export-wrapped", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_co_out; - } - } - - if (template.unwrap == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("import-wrapped", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_co_out; - } - } - - if (template.encrypt == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("wrap-data", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_co_out; - } - } - - if (template.decrypt == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("unwrap-data", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_co_out; - } + rc = set_wrapkey_capabilities(&template, &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_co_out; } rc = yh_string_to_capabilities("all", &delegated_capabilities); @@ -1536,10 +1570,11 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) goto c_co_out; } + type = YH_WRAP_KEY; rc = yh_util_import_wrap_key(session->slot->device_session, &template.id, - template.label, 0xffff, &capabilities, algo, - &delegated_capabilities, template.obj.buf, - template.objlen); + template.label, 0xffff, &capabilities, algo, + &delegated_capabilities, template.obj.buf, + template.objlen); if (rc != YHR_SUCCESS) { DBG_ERR("Failed writing WRAP key to device: %s", yh_strerror(rc)); rv = yrc_to_rv(rc); @@ -1665,7 +1700,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) meta_object.target_id = template.id; } } else if (class.d == CKO_PUBLIC_KEY) { - bool pubkey_found = false; + // Read the value of the public key for (CK_ULONG i = 0; i < ulCount; i++) { switch (pTemplate[i].type) { @@ -1680,84 +1715,155 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) goto c_co_out; } break; + case CKA_WRAP: + if ((rv = set_template_attribute(&template.wrap, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_WRAP inconsistent in template"); + return rv; + } + break; + case CKA_ENCRYPT: + if ((rv = set_template_attribute(&template.encrypt, + pTemplate[i].pValue)) != CKR_OK) { + DBG_ERR("CKA_ENCRYPT inconsistent in template"); + return rv; + } + break; } } - // Get a list of all asym objects in the YubiHSM - yh_object_descriptor asym_keys[YH_MAX_ITEMS_COUNT] = {0}; - size_t asym_keys_len = sizeof(asym_keys); - rc = yh_util_list_objects(session->slot->device_session, 0, - YH_ASYMMETRIC_KEY, 0, &capabilities, 0, NULL, - asym_keys, &asym_keys_len); - if (rc != YHR_SUCCESS) { - DBG_ERR("Failed to get object list"); - rv = yrc_to_rv(rc); - goto c_co_out; - } + if (template.wrap && key_type.d == CKK_RSA) { + switch (template.objlen) { + case 256: + template.algorithm = YH_ALGO_RSA_2048; + break; + case 384: + template.algorithm = YH_ALGO_RSA_3072; + break; + case 512: + template.algorithm = YH_ALGO_RSA_4096; + break; + default: + DBG_ERR("Unsupported key length"); + rv = CKR_DATA_INVALID; + goto c_co_out; + } + + if (template.algorithm == 0) { + DBG_ERR("Missing CKA_KEY_TYPE in attribute template"); + rv = CKR_TEMPLATE_INCOMPLETE; + goto c_co_out; + } + + rc = set_wrapkey_capabilities(&template, &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_co_out; + } - // Check which asym public key matches the one in the request - for (size_t i = 0; i < asym_keys_len; i++) { - uint8_t pubkey[2048] = {0}; - size_t pubkey_len = sizeof(pubkey); - rc = yh_util_get_public_key(session->slot->device_session, - asym_keys[i].id, pubkey, &pubkey_len, NULL); + rc = yh_string_to_capabilities("all", &delegated_capabilities); if (rc != YHR_SUCCESS) { - DBG_ERR("Failed to get public key of object 0x%x", asym_keys[i].id); rv = yrc_to_rv(rc); goto c_co_out; } - if (match_byte_array(pubkey, pubkey_len, template.obj.buf, - template.objlen)) { - template.id = asym_keys[i].id; - pubkey_found = true; + rc = + yh_util_import_public_wrap_key(session->slot->device_session, &template.id, + template.label, 0xffff, &capabilities, + template.algorithm, &delegated_capabilities, + template.obj.buf, template.objlen); + if (rc != YHR_SUCCESS) { + DBG_ERR("Failed writing Public Wrap key to device: %s", + yh_strerror(rc)); + rv = yrc_to_rv(rc); + goto c_co_out; + } + type = YH_PUBLIC_WRAP_KEY; + + } else { // Treat it as asymmetric public key. List all asymmetric keys and + // check whether this public key matches any of them. If not, + // import operation fails + + bool pubkey_found = false; + // Get a list of all asym objects in the YubiHSM + yh_object_descriptor asym_keys[YH_MAX_ITEMS_COUNT] = {0}; + size_t asym_keys_len = sizeof(asym_keys); + rc = yh_util_list_objects(session->slot->device_session, 0, + YH_ASYMMETRIC_KEY, 0, &capabilities, 0, NULL, + asym_keys, &asym_keys_len); + if (rc != YHR_SUCCESS) { + DBG_ERR("Failed to get object list"); + rv = yrc_to_rv(rc); + goto c_co_out; + } - // If there's need, update or create meta_object - yubihsm_pkcs11_object_desc *asym_key_desc = - _get_object_desc(session->slot, asym_keys[i].id, YH_ASYMMETRIC_KEY, - asym_keys[i].sequence); - if (asym_key_desc == NULL) { - continue; + // Check which asym public key matches the one in the request + for (size_t i = 0; i < asym_keys_len; i++) { + uint8_t pubkey[2048] = {0}; + size_t pubkey_len = sizeof(pubkey); + rc = yh_util_get_public_key(session->slot->device_session, + asym_keys[i].id, pubkey, &pubkey_len, NULL); + if (rc != YHR_SUCCESS) { + DBG_ERR("Failed to get public key of object 0x%x", asym_keys[i].id); + rv = yrc_to_rv(rc); + goto c_co_out; } - if (meta_object.cka_id.len > 0 || meta_object.cka_label.len > 0) { - yubihsm_pkcs11_object_desc *pMeta_object = - find_meta_object_by_target(session->slot, asym_keys[i].id, - YH_ASYMMETRIC_KEY, asym_keys[i].sequence, - asym_key_desc->object.domains); - - if (pMeta_object != NULL) { // meta object already exists. Update it. - if (meta_object.cka_id.len > 0) { - pMeta_object->meta_object.cka_id_pubkey.len = - meta_object.cka_id.len; - memcpy(pMeta_object->meta_object.cka_id_pubkey.value, - meta_object.cka_id.value, meta_object.cka_id.len); - } - if (meta_object.cka_label.len > 0) { - pMeta_object->meta_object.cka_label_pubkey.len = - meta_object.cka_label.len; - memcpy(pMeta_object->meta_object.cka_label_pubkey.value, - meta_object.cka_label.value, meta_object.cka_label.len); - } - rv = write_meta_object(session->slot, &pMeta_object->meta_object, - &capabilities, asym_key_desc->object.domains, - true); - if (rv != CKR_OK) { - goto c_co_out; + + if (match_byte_array(pubkey, pubkey_len, template.obj.buf, + template.objlen)) { + template.id = asym_keys[i].id; + pubkey_found = true; + + // If there's need, update or create meta_object + yubihsm_pkcs11_object_desc *asym_key_desc = + _get_object_desc(session->slot, asym_keys[i].id, YH_ASYMMETRIC_KEY, + asym_keys[i].sequence); + if (asym_key_desc == NULL) { + continue; + } + if (meta_object.cka_id.len > 0 || meta_object.cka_label.len > 0) { + yubihsm_pkcs11_object_desc *pMeta_object = + find_meta_object_by_target(session->slot, asym_keys[i].id, + YH_ASYMMETRIC_KEY, + asym_keys[i].sequence, + asym_key_desc->object.domains); + + if (pMeta_object != + NULL) { // meta object already exists. Update it. + if (meta_object.cka_id.len > 0) { + pMeta_object->meta_object.cka_id_pubkey.len = + meta_object.cka_id.len; + memcpy(pMeta_object->meta_object.cka_id_pubkey.value, + meta_object.cka_id.value, meta_object.cka_id.len); + } + if (meta_object.cka_label.len > 0) { + pMeta_object->meta_object.cka_label_pubkey.len = + meta_object.cka_label.len; + memcpy(pMeta_object->meta_object.cka_label_pubkey.value, + meta_object.cka_label.value, meta_object.cka_label.len); + } + rv = write_meta_object(session->slot, &pMeta_object->meta_object, + &capabilities, + asym_key_desc->object.domains, true); + if (rv != CKR_OK) { + goto c_co_out; + } + } else { // meta object does not exist. Create it + meta_object.target_id = asym_keys[i].id; + // No need to write this meta object now becase we will do it + // later } - } else { // meta object does not exist. Create it - meta_object.target_id = asym_keys[i].id; - // No need to write this meta object now becase we will do it later } + break; } - break; } - } - if (pubkey_found == false) { - rv = CKR_ATTRIBUTE_VALUE_INVALID; - goto c_co_out; + if (pubkey_found == false) { + rv = CKR_ATTRIBUTE_VALUE_INVALID; + goto c_co_out; + } + type = YH_ASYMMETRIC_KEY; } - type = YH_ASYMMETRIC_KEY; } else { rv = CKR_TEMPLATE_INCONSISTENT; goto c_co_out; @@ -1786,7 +1892,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject) } } - if (class.d == CKO_PUBLIC_KEY) { + if (class.d == CKO_PUBLIC_KEY && type != YH_PUBLIC_WRAP_KEY) { *phObject = object->sequence << 24 | (object->type | 0x80) << 16 | object->id; } else { @@ -1856,7 +1962,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_DestroyObject) DBG_INFO("No ECDH session key with ID %08lx was found", hObject); } } else { - if (((uint8_t)(hObject >> 16)) == YH_PUBLIC_KEY) { + if (((uint8_t) (hObject >> 16)) == YH_PUBLIC_KEY) { DBG_INFO("Trying to delete public key, returning success with noop"); goto c_do_out; } @@ -2220,18 +2326,6 @@ static bool should_include_sessionkeys(bool is_secret_key, bool extractable_set, return true; } -static CK_RV set_object_type(uint8_t *type, uint8_t expected_type) { - if (*type == 0) { - *type = expected_type; - return CKR_OK; - } - if (*type != expected_type) { - DBG_ERR("Mismatch in attribute values"); - return CKR_TEMPLATE_INCONSISTENT; - } - return CKR_OK; -} - CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { @@ -2286,6 +2380,8 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) yh_algorithm algorithm = 0; bool unknown = false; bool secret_key = false; + bool rsa_key = false; + bool wrap_key = false; bool extractable_set = false; size_t template_value_len = 0; uint8_t template_id[CKA_ATTRIBUTE_VALUE_SIZE] = {0}; @@ -2303,14 +2399,14 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) break; case CKA_CLASS: { - uint32_t value = *((CK_ULONG_PTR)(pTemplate[i].pValue)); + uint32_t value = *((CK_ULONG_PTR) (pTemplate[i].pValue)); uint8_t class_type = 0; switch (value) { case CKO_CERTIFICATE: DBG_INFO("filtering for certificates"); algorithm = YH_ALGO_OPAQUE_X509_CERTIFICATE; // TODO: handle other certs? - type = YH_OPAQUE; + class_type = YH_OPAQUE; break; case CKO_DATA: @@ -2388,10 +2484,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) case CKA_WRAP: if (*((CK_BBOOL *) pTemplate[i].pValue) == CK_TRUE) { - rv = set_object_type(&type, YH_WRAP_KEY); - if (rv != CKR_OK) { - goto c_foi_out; - } + wrap_key = true; rc = yh_string_to_capabilities("export-wrapped", &capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); @@ -2402,10 +2495,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) case CKA_UNWRAP: if (*((CK_BBOOL *) pTemplate[i].pValue) == CK_TRUE) { - rv = set_object_type(&type, YH_WRAP_KEY); - if (rv != CKR_OK) { - goto c_foi_out; - } + wrap_key = true; rc = yh_string_to_capabilities("import-wrapped", &capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); @@ -2434,7 +2524,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) break; case CKA_KEY_TYPE: { - uint32_t value = *((CK_ULONG_PTR)(pTemplate[i].pValue)); + uint32_t value = *((CK_ULONG_PTR) (pTemplate[i].pValue)); uint8_t key_type = 0; switch (value) { case CKK_YUBICO_AES128_CCM_WRAP: @@ -2452,6 +2542,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) key_type = YH_SYMMETRIC_KEY; break; case CKK_RSA: + rsa_key = true; case CKK_EC: key_type = YH_ASYMMETRIC_KEY; break; @@ -2487,6 +2578,14 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) } } + if(wrap_key && rsa_key) { + if (pub) { + type = YH_PUBLIC_WRAP_KEY; + } else { + type = YH_WRAP_KEY; + } + } + if (unknown == false) { uint16_t found_objects = 0; if (secret_key == true) { @@ -2529,39 +2628,38 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit) "Value in template not an X509Certificate. Cannot perform search."); rv = CKR_ATTRIBUTE_VALUE_INVALID; goto c_foi_out; - } else { - yh_object_descriptor tmp_objects[YH_MAX_ITEMS_COUNT] = {0}; - size_t tmp_n_objects = sizeof(tmp_objects); - rc = yh_util_list_objects(session->slot->device_session, 0, YH_OPAQUE, - domains, &capabilities, - YH_ALGO_OPAQUE_X509_CERTIFICATE, label, - tmp_objects, &tmp_n_objects); + } + yh_object_descriptor tmp_objects[YH_MAX_ITEMS_COUNT] = {0}; + size_t tmp_n_objects = sizeof(tmp_objects); + rc = yh_util_list_objects(session->slot->device_session, 0, YH_OPAQUE, + domains, &capabilities, + YH_ALGO_OPAQUE_X509_CERTIFICATE, label, + tmp_objects, &tmp_n_objects); + if (rc != YHR_SUCCESS) { + DBG_ERR("Failed to get object list"); + rv = yrc_to_rv(rc); + goto c_foi_out; + } + + for (size_t i = 0; i < tmp_n_objects; i++) { + uint8_t cert[2048] = {0}; + size_t cert_len = sizeof(cert); + rc = yh_util_get_opaque(session->slot->device_session, + tmp_objects[i].id, cert, &cert_len); if (rc != YHR_SUCCESS) { - DBG_ERR("Failed to get object list"); + DBG_ERR("Failed to get opaque object 0x%x", tmp_objects[i].id); rv = yrc_to_rv(rc); goto c_foi_out; } - for (size_t i = 0; i < tmp_n_objects; i++) { - uint8_t cert[2048] = {0}; - size_t cert_len = sizeof(cert); - rc = yh_util_get_opaque(session->slot->device_session, - tmp_objects[i].id, cert, &cert_len); - if (rc != YHR_SUCCESS) { - DBG_ERR("Failed to get opaque object 0x%x", tmp_objects[i].id); - rv = yrc_to_rv(rc); - goto c_foi_out; - } - - if (match_byte_array(template_value, template_value_len, cert, - cert_len)) { - session->operation.op.find.objects[0].id = tmp_objects[i].id; - session->operation.op.find.objects[0].type = tmp_objects[i].type; - session->operation.op.find.objects[0].sequence = - tmp_objects[i].sequence; - found_objects = 1; - break; - } + if (match_byte_array(template_value, template_value_len, cert, + cert_len)) { + session->operation.op.find.objects[0].id = tmp_objects[i].id; + session->operation.op.find.objects[0].type = tmp_objects[i].type; + session->operation.op.find.objects[0].sequence = + tmp_objects[i].sequence; + found_objects = 1; + break; } } } else { @@ -2705,6 +2803,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjects) case YH_ASYMMETRIC_KEY: case YH_OPAQUE: case YH_WRAP_KEY: + case YH_PUBLIC_WRAP_KEY: case YH_HMAC_KEY: case YH_PUBLIC_KEY: case YH_SYMMETRIC_KEY: @@ -2814,7 +2913,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_EncryptInit) rv = check_decrypt_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Encryption mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Encryption mechanism 0x%lx not supported", pMechanism->mechanism); goto c_ei_out; } @@ -3135,6 +3234,110 @@ CK_DEFINE_FUNCTION(CK_RV, C_EncryptFinal) return rv; } +typedef struct rsa_pkcs_oaep_params { + yh_algorithm mgf_algo; + yh_algorithm oaep_algo; + uint8_t oaep_label[64]; + u_int oaep_label_len; +} rsa_pkcs_oaep_params_t; + +static CK_RV parse_rsa_pkcs_oaep_params(CK_RSA_PKCS_OAEP_PARAMS *p, + rsa_pkcs_oaep_params_t *r) { + const EVP_MD *md = NULL; + switch (p->mgf) { + case CKG_MGF1_SHA1: + r->mgf_algo = YH_ALGO_MGF1_SHA1; + break; + case CKG_MGF1_SHA256: + r->mgf_algo = YH_ALGO_MGF1_SHA256; + break; + case CKG_MGF1_SHA384: + r->mgf_algo = YH_ALGO_MGF1_SHA384; + break; + case CKG_MGF1_SHA512: + r->mgf_algo = YH_ALGO_MGF1_SHA512; + break; + default: + DBG_ERR("Invalid mgf parameter (%lx)", p->mgf); + return CKR_MECHANISM_PARAM_INVALID; + } + switch (p->hashAlg) { + case CKM_SHA_1: + r->oaep_algo = YH_ALGO_RSA_OAEP_SHA1; + md = EVP_sha1(); + break; + case CKM_SHA256: + r->oaep_algo = YH_ALGO_RSA_OAEP_SHA256; + md = EVP_sha256(); + break; + case CKM_SHA384: + r->oaep_algo = YH_ALGO_RSA_OAEP_SHA384; + md = EVP_sha384(); + break; + case CKM_SHA512: + r->oaep_algo = YH_ALGO_RSA_OAEP_SHA512; + md = EVP_sha512(); + break; + default: + DBG_ERR("Invalid hashAlg parameter (%lx)", p->hashAlg); + return CKR_MECHANISM_PARAM_INVALID; + } + switch (p->source) { + case 0: + if (p->ulSourceDataLen) { + DBG_ERR("Invalid ulSourceDataLen (%lu) parameter for source == 0", + p->ulSourceDataLen); + return CKR_MECHANISM_PARAM_INVALID; + } + case CKZ_DATA_SPECIFIED: + if (p->ulSourceDataLen && p->pSourceData == NULL) { + DBG_ERR( + "Invalid pSourceData parameter (NULL) for ulSourceDataLen != 0"); + return CKR_MECHANISM_PARAM_INVALID; + } + break; + default: + DBG_ERR("Invalid source parameter (%lx)", p->source); + return CKR_MECHANISM_PARAM_INVALID; + } + EVP_MD_CTX *mdctx = EVP_MD_CTX_create(); + if (mdctx == NULL) { + DBG_ERR("Failed to digest source"); + return CKR_FUNCTION_FAILED; + } + r->oaep_label_len = sizeof(r->oaep_label); + EVP_DigestInit_ex(mdctx, md, NULL); + EVP_DigestUpdate(mdctx, p->pSourceData, p->ulSourceDataLen); + EVP_DigestFinal_ex(mdctx, r->oaep_label, &r->oaep_label_len); + EVP_MD_CTX_destroy(mdctx); + return CKR_OK; +} + +typedef struct rsa_aes_key_wrap_params { + yh_algorithm aes_algo; + rsa_pkcs_oaep_params_t oaep_params; +} rsa_aes_key_wrap_params_t; + +static CK_RV parse_rsa_aes_key_wrap_params(CK_RSA_AES_KEY_WRAP_PARAMS *p, + rsa_aes_key_wrap_params_t *r) { + + switch (p->ulAESKeyBits) { + case 128: + r->aes_algo = YH_ALGO_AES128; + break; + case 192: + r->aes_algo = YH_ALGO_AES192; + break; + case 256: + r->aes_algo = YH_ALGO_AES256; + break; + default: + DBG_ERR("Invalid ulAESKeyBits parameter"); + return CKR_MECHANISM_PARAM_INVALID; + } + return parse_rsa_pkcs_oaep_params(p->pOAEPParams, &r->oaep_params); +} + CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit) (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { @@ -3186,7 +3389,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit) rv = check_decrypt_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Decryption mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Decryption mechanism 0x%lx not supported", pMechanism->mechanism); goto c_di_out; } session->operation.mechanism.mechanism = pMechanism->mechanism; @@ -3226,67 +3429,17 @@ CK_DEFINE_FUNCTION(CK_RV, C_DecryptInit) goto c_di_out; } - switch (params->mgf) { - case CKG_MGF1_SHA1: - session->operation.mechanism.oaep.mgf1Algo = YH_ALGO_MGF1_SHA1; - break; - case CKG_MGF1_SHA256: - session->operation.mechanism.oaep.mgf1Algo = YH_ALGO_MGF1_SHA256; - break; - case CKG_MGF1_SHA384: - session->operation.mechanism.oaep.mgf1Algo = YH_ALGO_MGF1_SHA384; - break; - case CKG_MGF1_SHA512: - session->operation.mechanism.oaep.mgf1Algo = YH_ALGO_MGF1_SHA512; - break; - default: - DBG_ERR("Unknown value in parameter mgf"); - rv = CKR_MECHANISM_PARAM_INVALID; - goto c_di_out; - }; - - const EVP_MD *md = NULL; - - switch (params->hashAlg) { - case CKM_SHA_1: - md = EVP_sha1(); - break; - case CKM_SHA256: - md = EVP_sha256(); - break; - case CKM_SHA384: - md = EVP_sha384(); - break; - case CKM_SHA512: - md = EVP_sha512(); - break; - default: - DBG_ERR("Unknown value in parameter hashAlg"); - rv = CKR_MECHANISM_PARAM_INVALID; - goto c_di_out; - } - mdctx = EVP_MD_CTX_create(); - if (mdctx == NULL) { - rv = CKR_HOST_MEMORY; + rsa_pkcs_oaep_params_t oaep_params; + rv = parse_rsa_pkcs_oaep_params(params, &oaep_params); + if (rv != CKR_OK) { goto c_di_out; } - if (EVP_DigestInit_ex(mdctx, md, NULL) == 0) { - rv = CKR_MECHANISM_PARAM_INVALID; - goto c_di_out; - } + session->operation.mechanism.oaep.mgf1Algo = oaep_params.mgf_algo; + session->operation.mechanism.oaep.label_len = oaep_params.oaep_label_len; + memcpy(session->operation.mechanism.oaep.label, oaep_params.oaep_label, + oaep_params.oaep_label_len); - if (EVP_DigestUpdate(mdctx, params->pSourceData, - params->ulSourceDataLen) != 1) { - rv = CKR_FUNCTION_FAILED; - goto c_di_out; - } - if (EVP_DigestFinal_ex(mdctx, session->operation.mechanism.oaep.label, - &session->operation.mechanism.oaep.label_len) != - 1) { - rv = CKR_FUNCTION_FAILED; - goto c_di_out; - } } else if (pMechanism->mechanism != CKM_RSA_PKCS) { DBG_ERR("Mechanism %lu not supported", pMechanism->mechanism); rv = CKR_MECHANISM_INVALID; @@ -4054,7 +4207,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_SignInit) rv = check_sign_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Signing mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Signing mechanism 0x%lx not supported", pMechanism->mechanism); goto c_si_out; } session->operation.mechanism.mechanism = @@ -4483,7 +4636,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_VerifyInit) rv = check_sign_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Verification mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Verification mechanism 0x%lx not supported", pMechanism->mechanism); goto c_vi_out; } session->operation.mechanism.mechanism = @@ -5046,36 +5199,10 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKey) DBG_INFO("parsed WRAP key, objlen: %d", template.objlen); - if (template.wrap == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("export-wrapped", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_gk_out; - } - } - - if (template.unwrap == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("import-wrapped", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_gk_out; - } - } - - if (template.encrypt == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("wrap-data", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_gk_out; - } - } - - if (template.decrypt == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("unwrap-data", &capabilities); - if (rc != YHR_SUCCESS) { - rv = yrc_to_rv(rc); - goto c_gk_out; - } + rc = set_wrapkey_capabilities(&template, &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_gk_out; } rc = yh_string_to_capabilities("all", &delegated_capabilities); @@ -5251,27 +5378,47 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair) // TODO(adma): check more return values if (yh_is_rsa(template.algorithm)) { - - if (template.sign == ATTRIBUTE_TRUE) { - rc = yh_string_to_capabilities("sign-pkcs,sign-pss", &capabilities); + if (template.unwrap) { // This is a wrap key + rc = set_wrapkey_capabilities(&template, &capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); goto c_gkp_out; } - } - if (template.decrypt == ATTRIBUTE_TRUE) { - rc = - yh_string_to_capabilities("decrypt-pkcs,decrypt-oaep", &capabilities); + yh_capabilities delegated_capabilities = {{0}}; + rc = yh_string_to_capabilities("all", &delegated_capabilities); if (rc != YHR_SUCCESS) { rv = yrc_to_rv(rc); goto c_gkp_out; } - } - rc = yh_util_generate_rsa_key(session->slot->device_session, &template.id, + rc = + yh_util_generate_wrap_key(session->slot->device_session, &template.id, template.label, 0xffff, &capabilities, - template.algorithm); + template.algorithm, &delegated_capabilities); + + } else { + if (template.sign == ATTRIBUTE_TRUE) { + rc = yh_string_to_capabilities("sign-pkcs,sign-pss", &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_gkp_out; + } + } + + if (template.decrypt == ATTRIBUTE_TRUE) { + rc = + yh_string_to_capabilities("decrypt-pkcs,decrypt-oaep", &capabilities); + if (rc != YHR_SUCCESS) { + rv = yrc_to_rv(rc); + goto c_gkp_out; + } + } + + rc = yh_util_generate_rsa_key(session->slot->device_session, &template.id, + template.label, 0xffff, &capabilities, + template.algorithm); + } if (rc != YHR_SUCCESS) { DBG_ERR("Failed generating RSA key on device: %s", yh_strerror(rc)); rv = yrc_to_rv(rc); @@ -5324,8 +5471,14 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair) } } - yubihsm_pkcs11_object_desc *object_desc = - _get_object_desc(session->slot, template.id, YH_ASYMMETRIC_KEY, 0xffff); + yubihsm_pkcs11_object_desc *object_desc = NULL; + if (template.unwrap) { + object_desc = + _get_object_desc(session->slot, template.id, YH_WRAP_KEY, 0xffff); + } else { + object_desc = + _get_object_desc(session->slot, template.id, YH_ASYMMETRIC_KEY, 0xffff); + } if (object_desc == NULL) { rv = CKR_OBJECT_HANDLE_INVALID; goto c_gkp_out; @@ -5405,9 +5558,10 @@ CK_DEFINE_FUNCTION(CK_RV, C_WrapKey) // NOTE: pWrappedKey is NULL so we just return the length we need if (pWrappedKey == NULL) { - *pulWrappedKeyLen = - sizeof(yh_object_descriptor) + object->object.len + YH_CCM_WRAP_OVERHEAD; - DBG_INFO("Calculated that wrapping will need %lu bytes", *pulWrappedKeyLen); + *pulWrappedKeyLen = YH_MSG_BUF_SIZE; + // CKM_YUBICO_AES_CCM_WRAP len = sizeof(yh_object_descriptor) + + // object->object.len + YH_CCM_WRAP_OVERHEAD; + DBG_INFO("Wrapping will need maximum of %lu bytes", *pulWrappedKeyLen); rv = CKR_OK; goto c_wk_out; } @@ -5420,7 +5574,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_WrapKey) rv = check_wrap_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Wrapping mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Wrapping mechanism 0x%lx not supported", pMechanism->mechanism); goto c_wk_out; } @@ -5446,26 +5600,66 @@ CK_DEFINE_FUNCTION(CK_RV, C_WrapKey) goto c_wk_out; } - uint8_t buf[2048] = {0}; - size_t len = sizeof(buf); + size_t len = *pulWrappedKeyLen; + + yh_rc yrc = YHR_SUCCESS; + if (pMechanism->mechanism == CKM_YUBICO_AES_CCM_WRAP) { + if (pMechanism->pParameter && + pMechanism->ulParameterLen != sizeof(CKM_YUBICO_AES_CCM_WRAP_PARAMS)) { + DBG_ERR("Wrong mechanism parameter length"); + rv = CKR_MECHANISM_PARAM_INVALID; + goto c_wk_out; + } + + CKM_YUBICO_AES_CCM_WRAP_PARAMS *params = pMechanism->pParameter; + CK_ULONG format = 0; // None = Do not include seed + if (params != NULL) { + format = params->format; + } + yrc = + yh_util_export_wrapped_ex(session->slot->device_session, key->object.id, + object->object.type, object->object.id, format, + pWrappedKey, &len); + } else { // CKM_RSA_AES_KEY_WRAP or CKM_YUBICO_RSA_WRAP + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_RSA_AES_KEY_WRAP_PARAMS)) { + DBG_ERR("Wrong mechanism parameter length"); + rv = CKR_MECHANISM_PARAM_INVALID; + goto c_wk_out; + } - yh_rc yrc = - yh_util_export_wrapped(session->slot->device_session, key->object.id, - object->object.type, object->object.id, buf, &len); + rsa_aes_key_wrap_params_t params = {0}; + rv = parse_rsa_aes_key_wrap_params(pMechanism->pParameter, ¶ms); + if (rv != CKR_OK) { + goto c_wk_out; + } + + if (pMechanism->mechanism == CKM_RSA_AES_KEY_WRAP) { + yrc = yh_util_get_rsa_wrapped_key(session->slot->device_session, + key->object.id, object->object.type, + object->object.id, params.aes_algo, + params.oaep_params.oaep_algo, + params.oaep_params.mgf_algo, + params.oaep_params.oaep_label, + params.oaep_params.oaep_label_len, + pWrappedKey, &len); + } else { // CKM_YUBICO_RSA_WRAP + yrc = yh_util_export_rsa_wrapped(session->slot->device_session, + key->object.id, object->object.type, + object->object.id, params.aes_algo, + params.oaep_params.oaep_algo, + params.oaep_params.mgf_algo, + params.oaep_params.oaep_label, + params.oaep_params.oaep_label_len, + pWrappedKey, &len); + } + } if (yrc != YHR_SUCCESS) { DBG_ERR("Wrapping failed: %s", yh_strerror(yrc)); rv = yrc_to_rv(yrc); goto c_wk_out; } - if (len > *pulWrappedKeyLen) { - DBG_ERR("buffer too small, needed %lu, got %lu", (unsigned long) len, - *pulWrappedKeyLen); - rv = CKR_BUFFER_TOO_SMALL; - goto c_wk_out; - } - - memcpy(pWrappedKey, buf, len); *pulWrappedKeyLen = len; DOUT; @@ -5485,10 +5679,6 @@ CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey) DIN; - // NOTE: since the wrap is opaque we just ignore the template.. - UNUSED(pTemplate); - UNUSED(ulAttributeCount); - if (g_yh_initialized == false) { DBG_ERR("libyubihsm is not initialized or already finalized"); return CKR_CRYPTOKI_NOT_INITIALIZED; @@ -5521,7 +5711,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey) rv = check_wrap_mechanism(session->slot, pMechanism); if (rv != CKR_OK) { - DBG_ERR("Wrapping mechanism %lu not supported", pMechanism->mechanism); + DBG_ERR("Wrapping mechanism 0x%lx not supported", pMechanism->mechanism); goto c_uk_out; } @@ -5542,9 +5732,145 @@ CK_DEFINE_FUNCTION(CK_RV, C_UnwrapKey) uint16_t target_id = 0; yh_object_type target_type = 0; - yh_rc yrc = yh_util_import_wrapped(session->slot->device_session, - key->object.id, pWrappedKey, - ulWrappedKeyLen, &target_type, &target_id); + yh_rc yrc = YHR_SUCCESS; + if (pMechanism->mechanism == CKM_YUBICO_AES_CCM_WRAP) { + yrc = yh_util_import_wrapped(session->slot->device_session, key->object.id, + pWrappedKey, ulWrappedKeyLen, &target_type, + &target_id); + } else { // CKM_RSA_AES_KEY_WRAP or CKM_YUBICO_RSA_WRAP + + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_RSA_AES_KEY_WRAP_PARAMS)) { + DBG_ERR("Wrong mechanism parameter length"); + rv = CKR_MECHANISM_PARAM_INVALID; + goto c_uk_out; + } + + rsa_aes_key_wrap_params_t params = {0}; + rv = parse_rsa_aes_key_wrap_params(pMechanism->pParameter, ¶ms); + if (rv != CKR_OK) { + goto c_uk_out; + } + + if (pMechanism->mechanism == CKM_YUBICO_RSA_WRAP) { + yrc = + yh_util_import_rsa_wrapped(session->slot->device_session, + key->object.id, params.oaep_params.oaep_algo, + params.oaep_params.mgf_algo, + params.oaep_params.oaep_label, + params.oaep_params.oaep_label_len, + pWrappedKey, ulWrappedKeyLen, &target_type, + &target_id); + } else { // CKM_RSA_AES_KEY_WRAP + + pkcs11_meta_object pkcs11meta; + yubihsm_pkcs11_object_template object_template = {0}; + rv = parse_rsa_wrappedkey_template(pTemplate, ulAttributeCount, + &object_template, &pkcs11meta, + (CK_BYTE *) &target_type); + if (rv != CKR_OK) { + DBG_ERR("Failed to parse wrapped key template"); + goto c_uk_out; + } + + yh_capabilities capabilities = {{0}}; + if (object_template.exportable == ATTRIBUTE_TRUE) { + yrc = yh_string_to_capabilities("exportable-under-wrap", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } + if (object_template.sign == ATTRIBUTE_TRUE) { + if (yh_is_rsa(object_template.algorithm)) { + yrc = yh_string_to_capabilities("sign-pkcs,sign-pss", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } else if (yh_is_ec(object_template.algorithm)) { + yrc = yh_string_to_capabilities("sign-ecdsa", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } else if (yh_is_ed(object_template.algorithm)) { + yrc = yh_string_to_capabilities("sign-eddsa", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } else { + DBG_ERR( + "Key type unsupported for unwrap or for signing capabilities"); + rv = CKR_TEMPLATE_INCONSISTENT; + goto c_uk_out; + } + } + if (object_template.decrypt == ATTRIBUTE_TRUE) { + if (yh_is_rsa(object_template.algorithm)) { + yrc = yh_string_to_capabilities("decrypt-pkcs,decrypt-oaep", + &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } else if (yh_is_aes(object_template.algorithm)) { + if (object_template.decrypt == ATTRIBUTE_TRUE) { + yrc = yh_string_to_capabilities("decrypt-ecb,decrypt-cbc", + &capabilities); + if (yrc != YHR_SUCCESS) { + rv = CKR_FUNCTION_FAILED; + goto c_uk_out; + } + } + } else { + DBG_ERR( + "Key type unsupported for unwrap or for decryptions capabilities"); + rv = CKR_TEMPLATE_INCONSISTENT; + goto c_uk_out; + } + } + if (object_template.encrypt == ATTRIBUTE_TRUE) { + if (!yh_is_aes(object_template.algorithm)) { + DBG_ERR( + "Key type unsupported for unwrap or for encryption capabilities"); + rv = CKR_TEMPLATE_INCONSISTENT; + goto c_uk_out; + } + yrc = + yh_string_to_capabilities("encrypt-ecb,encrypt-cbc", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = CKR_FUNCTION_FAILED; + goto c_uk_out; + } + } + if (object_template.derive == ATTRIBUTE_TRUE) { + if (!yh_is_ec(object_template.algorithm)) { + DBG_ERR("Key type unsupported for unwrap or for ECDH derivation " + "capabilities"); + rv = CKR_TEMPLATE_INCONSISTENT; + goto c_uk_out; + } + yrc = yh_string_to_capabilities("derive-ecdh", &capabilities); + if (yrc != YHR_SUCCESS) { + rv = yrc_to_rv(yrc); + goto c_uk_out; + } + } + + yrc = + yh_util_put_rsa_wrapped_key(session->slot->device_session, + key->object.id, target_type, &target_id, + object_template.algorithm, + object_template.label, 0xffff, + &capabilities, params.oaep_params.oaep_algo, + params.oaep_params.mgf_algo, + params.oaep_params.oaep_label, + params.oaep_params.oaep_label_len, + pWrappedKey, ulWrappedKeyLen); + } + } if (yrc != YHR_SUCCESS) { DBG_ERR("Unwrapping failed: %s", yh_strerror(yrc)); rv = yrc_to_rv(yrc); diff --git a/resources/tests/bash/test_wrapkey.sh b/resources/tests/bash/test_wrapkey.sh index a379c7445..8fa3d3bf8 100755 --- a/resources/tests/bash/test_wrapkey.sh +++ b/resources/tests/bash/test_wrapkey.sh @@ -11,16 +11,33 @@ then rm -rf yubihsm-shell_test_dir fi mkdir yubihsm-shell_test_dir; cd yubihsm-shell_test_dir + +test () { + set +e + $1 > output.txt 2>&1 + ret=$? + if [ $ret -ne 0 ]; then + echo $1 + cat output.txt + rm output.txt + exit 1 + else + echo "$2 ... OK!" + rm output.txt + fi + set -e +} + set -e -#set -x + echo "**********************************" echo " aes128-ccm-wrap" echo "**********************************" echo "=== Generate on YubiHSM" -$BIN -p password -a generate-wrap-key -i 0 -l "wrapkey" -d "1" -c "export-wrapped,import-wrapped" --delegated "export-wrapped,import-wrapped" -A "aes128-ccm-wrap" -cat resp.txt -keyid=$(tail -1 resp.txt | awk '{print $4}') +keyid=0x0005 +$BIN -p password -a generate-wrap-key -i $keyid -l "wrapkey" -d "1" -c "export-wrapped,import-wrapped" --delegated "export-wrapped,import-wrapped" -A "aes128-ccm-wrap" +echo keyid: $keyid info=$($BIN -p password -a get-object-info -i $keyid -t wrap-key) echo $info | grep "id: $keyid" echo $info | grep "type: wrap-key" @@ -33,6 +50,24 @@ echo $info | grep "delegated_capabilities: export-wrapped:import-wrapped" echo "=== Delete key" $BIN -p password -a delete-object -i $keyid -t wrap-key +echo "**********************************" +echo " aes128-ccm-wrap" +echo "**********************************" +test "$BIN -p password -a generate-asymmetric-key -i 100 -l ecKey -d 5,8,13 -c exportable-under-wrap -A ecp224" " Generate EC Key to wrap" +test "$BIN -p password -a generate-wrap-key -i 200 -l wrapkey -d 5,8,13 -c all --delegated all -A rsa2048" " Generate RSA wrap key" +test "$BIN -p password -a get-public-key -i 200 -t wrap-key --out public_wrapkey.pem" " Export rsa public wrap key" +test "$BIN -p password -a put-public-wrapkey -i 200 --delegated all -c all --in public_wrapkey.pem" " Import RSA public wrap key" +test "$BIN -p password -a get-rsa-wrapped --wrap-id 200 -i 100 -t asymmetric-key --out rsawrapped.object" " Export wrapped EC object" +test "$BIN -p password -a get-rsa-wrapped-key --wrap-id 200 -i 100 -t asymmetric-key --out rsawrapped.key" " Export wrapped EC key" +test "$BIN -p password -a delete-object -i 100 -t asymmetric-key" " Delete original EC key" +test "$BIN -p password -a put-rsa-wrapped --wrap-id 200 --in rsawrapped.object" " Import wrapped EC object" +test "$BIN -p password -a put-rsa-wrapped-key --wrap-id 200 -i 300 -t asymmetric-key -A ecp224 --in rsawrapped.key" " Import wrapped EC key" +test "$BIN -p password -a delete-object -i 100 -t asymmetric-key" " Delete EC key" +test "$BIN -p password -a delete-object -i 300 -t asymmetric-key" " Delete EC key" +test "$BIN -p password -a delete-object -i 200 -t wrap-key" " Delete RSA wrap key" +test "$BIN -p password -a delete-object -i 200 -t public-wrap-key" " Delete public RSA wrap key" + + cd .. rm -rf yubihsm-shell_test_dir diff --git a/src/cmdline.ggo b/src/cmdline.ggo index 350d53033..63e41ccb1 100644 --- a/src/cmdline.ggo +++ b/src/cmdline.ggo @@ -43,6 +43,8 @@ option "action" a "Action to perform" values="benchmark", "get-storage-info", "get-template", "get-wrapped", + "get-rsa-wrapped", + "get-rsa-wrapped-key", "get-device-pubkey", "list-objects", "put-asymmetric-key", @@ -54,7 +56,11 @@ option "action" a "Action to perform" values="benchmark", "put-symmetric-key", "put-template", "put-wrap-key", + "put-rsa-wrapkey", + "put-public-wrapkey", "put-wrapped", + "put-rsa-wrapped", + "put-rsa-wrapped-key", "randomize-otp-aead", "reset", "set-log-index", @@ -78,11 +84,14 @@ option "ykhsmauth-reader" r "Only use a matching YubiKey reader name" string opt option "delegated" - "Delegated capabilities" string optional default="0" option "new-password" - "New authentication password" string optional option "algorithm" A "Operation algorithm" string optional default="any" +option "oaep" - "OAEP algorithm. Used primarily with asymmetric wrap" string optional default="rsa-oaep-sha256" +option "mgf1" - "MGF1 algorithm. Used primarily with asymmetric wrap" string optional default="mgf1-sha256" option "nonce" - "OTP nonce" int optional option "iv" - "An initialization vector as a hexadecimal string" string optional option "count" - "Number of bytes to request" int optional default="256" option "duration" - "Blink duration in seconds" int optional default="10" option "wrap-id" - "Wrap key ID" int optional +option "include-seed" - "Include seed when exporting an ED25519 key under wrap" flag off option "template-id" - "Template ID" int optional option "attestation-id" - "Attestation ID" int optional option "log-index" - "Log index" int optional diff --git a/src/commands.c b/src/commands.c index 108a2ddff..8cb6b0868 100644 --- a/src/commands.c +++ b/src/commands.c @@ -711,7 +711,7 @@ int yh_com_echo(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, UNUSED(fmt); uint8_t data[YH_MSG_BUF_SIZE] = {0}; - uint16_t data_len = 0; + size_t data_len = 0; uint8_t response[YH_MSG_BUF_SIZE] = {0}; size_t response_len = 0; @@ -1022,7 +1022,8 @@ int yh_com_get_storage(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, // argc = 3 // arg 0: e:session // arg 1: w:key_id -// arg 2: f:filename +// arg 2: t:key_type +// arg 3: f:filename int yh_com_get_pubkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt) { @@ -1034,8 +1035,8 @@ int yh_com_get_pubkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, yh_algorithm algo = 0; EVP_PKEY *public_key = NULL; - yh_rc yrc = yh_util_get_public_key(argv[0].e, argv[1].w, response, - &response_len, &algo); + yh_rc yrc = yh_util_get_public_key_ex(argv[0].e, argv[2].t, argv[1].w, + response, &response_len, &algo); if (yrc != YHR_SUCCESS) { fprintf(stderr, "Failed to get public key: %s\n", yh_strerror(yrc)); return -1; @@ -1367,7 +1368,8 @@ int yh_com_get_object_info(yubihsm_context *ctx, Argument *argv, // arg 1: w:keyid // arg 2: t:type // arg 3: w:id -// arg 4: f:file +// arg 4: b:include_seed +// arg 5: f:file int yh_com_get_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt) { uint8_t response[YH_MSG_BUF_SIZE] = {0}; @@ -1375,8 +1377,11 @@ int yh_com_get_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, UNUSED(in_fmt); - yh_rc yrc = yh_util_export_wrapped(argv[0].e, argv[1].w, argv[2].b, argv[3].w, - response, &response_len); + uint8_t format = argv[4].b ? 1 : 0; + + yh_rc yrc = + yh_util_export_wrapped_ex(argv[0].e, argv[1].w, argv[2].b, argv[3].w, + format, response, &response_len); if (yrc != YHR_SUCCESS) { fprintf(stderr, "Failed to get wrapped object: %s\n", yh_strerror(yrc)); @@ -1390,6 +1395,96 @@ int yh_com_get_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, return -1; } +// NOTE: Get an RSA wrapped key or object +// argc = 7 +// arg 0: e:session +// arg 1: w:keyid +// arg 2: t:type +// arg 3: w:id +// arg 4: a:aes +// arg 5: a:oaep +// arg 6: a:mgf1 +// arg 7: f:file +static int do_rsa_wrap(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt, bool key_wrap) { + UNUSED(in_fmt); + + int hash = 0; + yh_algorithm aes = argv[4].a; + yh_algorithm oaep = argv[5].a; + yh_algorithm mgf1 = argv[6].a; + yh_rc yrc; + + if (aes == 0) { + aes = YH_ALGO_AES256; + } + + switch (oaep) { + case YH_ALGO_RSA_OAEP_SHA1: + hash = _SHA1; + break; + + case YH_ALGO_RSA_OAEP_SHA256: + hash = _SHA256; + break; + + case YH_ALGO_RSA_OAEP_SHA384: + hash = _SHA384; + break; + + case YH_ALGO_RSA_OAEP_SHA512: + hash = _SHA512; + break; + + default: + fprintf(stderr, "Unrecognized OAEP algorithm\n"); + return -1; + } + + uint8_t label[64] = {0}; + size_t label_len = sizeof(label); + + if (hash_bytes(NULL, 0, hash, label, &label_len) == false) { + fprintf(stderr, "Unable to hash data\n"); + return -1; + } + + uint8_t response[YH_MSG_BUF_SIZE] = {0}; + size_t response_len = sizeof(response); + + if (key_wrap) { + yrc = yh_util_get_rsa_wrapped_key(argv[0].e, argv[1].w, argv[2].b, + argv[3].w, aes, oaep, mgf1, label, + label_len, response, &response_len); + } else { + yrc = yh_util_export_rsa_wrapped(argv[0].e, argv[1].w, argv[2].b, argv[3].w, + aes, oaep, mgf1, label, label_len, + response, &response_len); + } + + if (yrc != YHR_SUCCESS) { + fprintf(stderr, "Failed to encrypt data with OAEP: %s\n", yh_strerror(yrc)); + return yrc; + } + + if (!write_file(response, response_len, ctx->out, fmt_to_fmt(fmt))) { + fprintf(stderr, "Failed to write wrapped object to file"); + return YHR_GENERIC_ERROR; + } + + return YHR_SUCCESS; +} + +int yh_com_get_rsa_wrapped(yubihsm_context *ctx, Argument *argv, + cmd_format in_fmt, cmd_format fmt) { + return do_rsa_wrap(ctx, argv, in_fmt, fmt, false); +} + +int yh_com_get_rsa_wrapped_key(yubihsm_context *ctx, Argument *argv, + cmd_format in_fmt, cmd_format fmt) { + return do_rsa_wrap(ctx, argv, in_fmt, fmt, true); +} + // NOTE(adma): Get a template object // argc = 2 // arg 0: e:session, @@ -1990,7 +2085,7 @@ int yh_com_put_symmetric(yubihsm_context *ctx, Argument *argv, // arg 2: s:label // arg 3: w:domains // arg 4: c:capabilities -// arg 5: i:key +// arg 5: x:key int yh_com_put_asymmetric(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt) { @@ -2284,6 +2379,7 @@ int yh_com_put_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, UNUSED(ctx); UNUSED(in_fmt); UNUSED(fmt); + yh_algorithm algo = 0; if (argv[6].len == 16) { @@ -2310,6 +2406,140 @@ int yh_com_put_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, return 0; } +// NOTE: Store an RSA wrapping key +// argc = 6 +// arg 0: e:session +// arg 1: w:key_id +// arg 2: s:label +// arg 3: w:domains +// arg 4: c:capabilities +// arg 5: c:delegated_capabilities +// arg 6: x:key +int yh_com_put_rsa_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt) { + UNUSED(ctx); + UNUSED(in_fmt); + UNUSED(fmt); + + uint8_t key[512] = {0}; + size_t key_material_len = sizeof(key); + yh_algorithm algo = 0; + + bool ret = read_private_key(argv[6].x, argv[6].len, &algo, key, + &key_material_len, false); + if (ret == false) { + fprintf(stderr, "Unable to read wrap key\n"); + return -1; + } + + yh_rc yrc = yh_util_import_wrap_key(argv[0].e, &argv[1].w, argv[2].s, + argv[3].w, &argv[4].c, algo, &argv[5].c, + key, key_material_len); + if (yrc != YHR_SUCCESS) { + fprintf(stderr, "Failed to store wrapkey: %s\n", yh_strerror(yrc)); + return -1; + } + + fprintf(stderr, "Stored Wrap key 0x%04x\n", argv[1].w); + + return 0; +} + +static bool read_rsa_pubkey(const uint8_t *buf, size_t len, + uint8_t *bytes, size_t *bytes_len) { + BIO *bio; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return false; + + (void) BIO_write(bio, buf, len); + + RSA *rsa = NULL; + EVP_PKEY *pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free_all(bio); + + if (pubkey == NULL || EVP_PKEY_base_id(pubkey) != EVP_PKEY_RSA || + (rsa = EVP_PKEY_get1_RSA(pubkey)) == NULL) { + fprintf(stderr, "Failed to parse RSA public key\n"); + EVP_PKEY_free(pubkey); + return false; + } + + bool ret = false; + const BIGNUM *n = NULL; + RSA_get0_key(rsa, &n, NULL, NULL); + if (n == NULL) { + goto fail; + } + + size_t nn = BN_num_bytes(n); + if (*bytes_len < nn) { + fprintf(stderr, "%s: insufficient dst buffer space\n", __func__); + goto fail; + } + + *bytes_len = (size_t) BN_bn2bin(n, bytes); + + ret = true; +fail: + RSA_free(rsa); + EVP_PKEY_free(pubkey); + return ret; + +} + +// NOTE: Store a public wrap key +// argc = 6 +// arg 0: e:session +// arg 1: w:key_id +// arg 2: s:label +// arg 3: w:domains +// arg 4: c:capabilities +// arg 5: c:delegated_capabilities +// arg 6: i:pubkey +int yh_com_put_public_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt) { + UNUSED(ctx); + UNUSED(in_fmt); + UNUSED(fmt); + + uint8_t pubkey[512]; + size_t pubkey_len = sizeof(pubkey); + yh_algorithm algo = 0; + + if (!read_rsa_pubkey(argv[6].x, argv[6].len, pubkey, &pubkey_len)) { + fprintf(stderr, "Failed to read public key\n"); + return -1; + } + + switch (pubkey_len) { + case 256: + algo = YH_ALGO_RSA_2048; + break; + case 384: + algo = YH_ALGO_RSA_3072; + break; + case 512: + algo = YH_ALGO_RSA_4096; + break; + default: + fprintf(stderr, "Invalid public key length (%zu)\n", pubkey_len); + return -1; + } + + yh_rc yrc = yh_util_import_public_wrap_key(argv[0].e, &argv[1].w, argv[2].s, + argv[3].w, &argv[4].c, algo, + &argv[5].c, pubkey, pubkey_len); + if (yrc != YHR_SUCCESS) { + fprintf(stderr, "Failed to store public wrap key: %s\n", yh_strerror(yrc)); + return -1; + } + + fprintf(stderr, "Stored public wrap key 0x%04x\n", argv[1].w); + + return 0; +} + // NOTE: Store a wrapped object // argc = 3 // arg 0: e:session @@ -2339,6 +2569,146 @@ int yh_com_put_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, return 0; } +// NOTE: Store an asymetrically wrapped object +// argc = 3 +// arg 0: e:session +// arg 1: w:key_id +// arg 2: a:oaep +// arg 3: a:mgf1 +// arg 4: i:data +int yh_com_put_rsa_wrapped(yubihsm_context *ctx, Argument *argv, + cmd_format in_fmt, cmd_format fmt) { + UNUSED(ctx); + UNUSED(in_fmt); + UNUSED(fmt); + + yh_object_type object_type = 0; + uint16_t object_id = 0; + + yh_algorithm mgf1 = argv[3].a; + yh_algorithm oaep = argv[2].a; + int hash = 0; + + switch (oaep) { + case YH_ALGO_RSA_OAEP_SHA1: + hash = _SHA1; + break; + + case YH_ALGO_RSA_OAEP_SHA256: + hash = _SHA256; + break; + + case YH_ALGO_RSA_OAEP_SHA384: + hash = _SHA384; + break; + + case YH_ALGO_RSA_OAEP_SHA512: + hash = _SHA512; + break; + + default: + fprintf(stderr, "Unrecognized OAEP algorithm\n"); + return -1; + } + + uint8_t label[64] = {0}; + size_t label_len = sizeof(label); + + if (hash_bytes(NULL, 0, hash, label, &label_len) == false) { + fprintf(stderr, "Unable to hash data.\n"); + return -1; + } + + yh_rc yrc = yh_util_import_rsa_wrapped(argv[0].e, argv[1].w, oaep, mgf1, + label, label_len, argv[4].x, + argv[4].len, &object_type, &object_id); + if (yrc != YHR_SUCCESS) { + fprintf(stderr, "Failed to store wrapped object: %s\n", yh_strerror(yrc)); + return -1; + } + + const char *type = ""; + yh_type_to_string(object_type, &type); + + fprintf(stderr, "Object imported as 0x%04x of type %s\n", object_id, type); + + return 0; +} + +// NOTE: Store an asymetrically wrapped key object +// argc = 3 +// arg 0: e:session +// arg 1: w:wrapkey_id +// arg 2: t:type +// arg 3: w:key_id +// arg 4: a:key_algorithm +// arg 5: s:label +// arg 6: w:domains +// arg 7: c:capabilities +// arg 8: a:oaep +// arg 9: a:mgf1 +// arg 10: i:data +int yh_com_put_rsa_wrapped_key(yubihsm_context *ctx, Argument *argv, + cmd_format in_fmt, cmd_format fmt) { + UNUSED(ctx); + UNUSED(in_fmt); + UNUSED(fmt); + + yh_object_type object_type = argv[2].t; + uint16_t object_id = argv[3].w; + + yh_algorithm mgf1 = argv[9].a; + yh_algorithm oaep = argv[8].a; + int hash = 0; + + switch (oaep) { + case YH_ALGO_RSA_OAEP_SHA1: + hash = _SHA1; + break; + + case YH_ALGO_RSA_OAEP_SHA256: + hash = _SHA256; + break; + + case YH_ALGO_RSA_OAEP_SHA384: + hash = _SHA384; + break; + + case YH_ALGO_RSA_OAEP_SHA512: + hash = _SHA512; + break; + + default: + fprintf(stderr, "Unrecognized OAEP algorithm\n"); + return -1; + } + + uint8_t label[64] = {0}; + size_t label_len = sizeof(label); + + if (hash_bytes(NULL, 0, hash, label, &label_len) == false) { + fprintf(stderr, "Unable to hash data\n"); + return -1; + } + + yh_rc yrc = + yh_util_put_rsa_wrapped_key(argv[0].e, argv[1].w, object_type, &object_id, + argv[4].a, argv[5].s, argv[6].w, &argv[7].c, + oaep, mgf1, label, label_len, argv[10].x, + argv[10].len); + if (yrc != YHR_SUCCESS) { + fprintf(stderr, "Failed to store wrapped object: %s\n", yh_strerror(yrc)); + return -1; + } + + const char *type = ""; + yh_type_to_string(object_type, &type); + + fprintf(stderr, "Object imported as 0x%04x of type %s\n", object_id, type); + + return 0; +} + // NOTE(adma): Store a template object // argc = 7 // arg 0: e:session @@ -2604,6 +2974,7 @@ int yh_com_get_device_info(yubihsm_context *ctx, Argument *argv, yh_algorithm algorithms[YH_MAX_ALGORITHM_COUNT] = {0}; size_t n_algorithms = sizeof(algorithms); + yh_rc yrc = yh_util_get_device_info(ctx->connector, &major, &minor, &patch, &serial, &log_total, &log_used, algorithms, &n_algorithms); @@ -2627,6 +2998,14 @@ int yh_com_get_device_info(yubihsm_context *ctx, Argument *argv, } fprintf(ctx->out, "\n"); + char part_number[256] = {0}; + size_t part_number_len = sizeof(part_number); + yrc = + yh_util_get_partnumber(ctx->connector, part_number, &part_number_len); + if (yrc == YHR_SUCCESS && part_number_len > 0) { + fprintf(ctx->out, "Part number:\t\t%s\n", part_number); + } + return 0; } diff --git a/src/commands.h b/src/commands.h index 4f68c56b2..76146d213 100644 --- a/src/commands.h +++ b/src/commands.h @@ -112,6 +112,8 @@ int yh_com_get_object_info(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); int yh_com_get_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); +int yh_com_get_rsa_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); +int yh_com_get_rsa_wrapped_key(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); int yh_com_get_device_info(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); int yh_com_get_template(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, @@ -152,8 +154,16 @@ int yh_com_put_hmac(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); int yh_com_put_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); +int yh_com_put_rsa_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt); +int yh_com_put_public_wrapkey(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt); int yh_com_put_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); +int yh_com_put_rsa_wrapped(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt); +int yh_com_put_rsa_wrapped_key(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, + cmd_format fmt); int yh_com_put_template(yubihsm_context *ctx, Argument *argv, cmd_format in_fmt, cmd_format fmt); int yh_com_put_otp_aead_key(yubihsm_context *ctx, Argument *argv, diff --git a/src/main.c b/src/main.c index 4a4abe0cd..172b80890 100644 --- a/src/main.c +++ b/src/main.c @@ -388,7 +388,7 @@ static void create_command_list(CommandList *c) { fmt_nofmt, fmt_nofmt, "Get storages stats", NULL, NULL}); register_subcommand(*c, (Command){"pubkey", yh_com_get_pubkey, - "e:session,w:key_id,F:file=-", fmt_nofmt, + "e:session,w:key_id,t:key_type=asymmetric-key,F:file=-", fmt_nofmt, fmt_PEM, "Get a public key", NULL, NULL}); register_subcommand(*c, (Command){"objectinfo", yh_com_get_object_info, @@ -396,9 +396,19 @@ static void create_command_list(CommandList *c) { "Get information about an object", NULL, NULL}); register_subcommand(*c, (Command){"wrapped", yh_com_get_wrapped, - "e:session,w:wrapkey_id,t:type,w:id,F:file=-", + "e:session,w:wrapkey_id,t:type,w:id,b:include_seed=0,F:file=-", fmt_nofmt, fmt_base64, "Get an object under wrap", NULL, NULL}); + register_subcommand(*c, + (Command){"rsa_wrapped", yh_com_get_rsa_wrapped, + "e:session,w:wrapkey_id,t:type,w:id,a:aes=aes256,a:hash=rsa-oaep-sha256,mgf1=mgf1-sha256,F:file=-", + fmt_nofmt, fmt_binary, + "Get an object under RSA wrap", NULL, NULL}); + register_subcommand(*c, + (Command){"rsa_wrapped_key", yh_com_get_rsa_wrapped_key, + "e:session,w:wrapkey_id,t:type,w:id,a:aes=aes256,a:hash=rsa-oaep-sha256,mgf1=mgf1-sha256,F:file=-", + fmt_nofmt, fmt_binary, + "Get a (a)symmetric key under RSA wrap", NULL, NULL}); register_subcommand(*c, (Command){"deviceinfo", yh_com_get_device_info, NULL, fmt_nofmt, fmt_nofmt, "Extract the version number, serial number " @@ -493,10 +503,30 @@ static void create_command_list(CommandList *c) { "capabilities,c:delegated_capabilities,i:key", fmt_hex, fmt_nofmt, "Store a wrapping key", NULL, NULL}); + register_subcommand(*c, + (Command){"rsa_wrapkey", yh_com_put_rsa_wrapkey, + "e:session,w:key_id,s:label,d:domains,c:" + "capabilities,c:delegated_capabilities,i:key=-", + fmt_PEM, fmt_nofmt, "Store an RSA wrapping key", + NULL, NULL}); + register_subcommand(*c, + (Command){"pub_wrapkey", yh_com_put_public_wrapkey, + "e:session,w:key_id,s:label,d:domains,c:" + "capabilities,c:delegated_capabilities,i:key=-", + fmt_PEM, fmt_nofmt, "Store an RSA wrapping key", + NULL, NULL}); register_subcommand(*c, (Command){"wrapped", yh_com_put_wrapped, "e:session,w:wrapkey_id,i:data=-", fmt_base64, fmt_nofmt, "Store a wrapped object", NULL, NULL}); + register_subcommand(*c, (Command){"rsa_wrapped", yh_com_put_rsa_wrapped, + "e:session,w:wrapkey_id,a:hash=rsa-oaep-sha256,mgf1=mgf1-sha256,i:data=-", + fmt_binary, fmt_nofmt, + "Store a wrapped object", NULL, NULL}); + register_subcommand(*c, (Command){"rsa_wrapped_key", yh_com_put_rsa_wrapped_key, + "e:session,w:wrapkey_id,t:type,w:key_id,a:algorithm,s:label,d:domains,c:capabilities,a:hash=rsa-oaep-sha256,mgf1=mgf1-sha256,i:data=-", + fmt_binary, fmt_nofmt, + "Store a wrapped object", NULL, NULL}); register_subcommand(*c, (Command){"template", yh_com_put_template, "e:session,w:object_id,s:label,d:domains,c:" "capabilities,a:algorithm,i:data=-", @@ -2049,7 +2079,7 @@ int main(int argc, char *argv[]) { } } - Argument arg[7]; + Argument arg[11]; if (requires_session == true) { uint8_t *buf = 0; @@ -2362,8 +2392,14 @@ int main(int argc, char *argv[]) { case action_arg_getMINUS_publicMINUS_key: { arg[1].w = args_info.object_id_arg; - arg[2].s = args_info.out_arg; - arg[2].len = strlen(args_info.out_arg); + if(args_info.object_type_given) { + yrc = yh_string_to_type(args_info.object_type_arg, &arg[2].t); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse type: "); + } else { + arg[2].t = YH_ASYMMETRIC_KEY; + } + arg[3].s = args_info.out_arg; + arg[3].len = strlen(args_info.out_arg); comrc = yh_com_get_pubkey(&g_ctx, arg, fmt_nofmt, @@ -2412,8 +2448,10 @@ int main(int argc, char *argv[]) { arg[3].w = args_info.object_id_arg; - arg[4].s = args_info.out_arg; - arg[4].len = strlen(args_info.out_arg); + arg[4].b = args_info.include_seed_given; + + arg[5].s = args_info.out_arg; + arg[5].len = strlen(args_info.out_arg); comrc = yh_com_get_wrapped(&g_ctx, arg, fmt_nofmt, @@ -2421,6 +2459,80 @@ int main(int argc, char *argv[]) { COM_SUCCEED_OR_DIE(comrc, "Unable to get wrapped object"); } break; + case action_arg_getMINUS_rsaMINUS_wrapped: { + if (args_info.object_type_given == 0) { + fprintf(stderr, "Missing argument object-type\n"); + rc = EXIT_FAILURE; + break; + } + + if (args_info.wrap_id_given == 0) { + fprintf(stderr, "Missing argument wrap-id\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.wrap_id_arg; + yrc = yh_string_to_type(args_info.object_type_arg, &arg[2].t); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse type: "); + + arg[3].w = args_info.object_id_arg; + + yrc = yh_string_to_algo(args_info.algorithm_arg, &arg[4].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse algorithm: "); + + yrc = yh_string_to_algo(args_info.oaep_arg, &arg[5].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse OAEP algorithm: "); + + yrc = yh_string_to_algo(args_info.mgf1_arg, &arg[6].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse MGF1 algorithm: "); + + arg[7].s = args_info.out_arg; + arg[7].len = strlen(args_info.out_arg); + + comrc = + yh_com_get_rsa_wrapped(&g_ctx, arg, fmt_nofmt, + g_out_fmt == fmt_nofmt ? fmt_binary : g_out_fmt); + COM_SUCCEED_OR_DIE(comrc, "Unable to get wrapped object"); + } break; + + case action_arg_getMINUS_rsaMINUS_wrappedMINUS_key: { + if (args_info.object_type_given == 0) { + fprintf(stderr, "Missing argument object-type\n"); + rc = EXIT_FAILURE; + break; + } + + if (args_info.wrap_id_given == 0) { + fprintf(stderr, "Missing argument wrap-id\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.wrap_id_arg; + yrc = yh_string_to_type(args_info.object_type_arg, &arg[2].t); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse type: "); + + arg[3].w = args_info.object_id_arg; + + yrc = yh_string_to_algo(args_info.algorithm_arg, &arg[4].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse algorithm: "); + + yrc = yh_string_to_algo(args_info.oaep_arg, &arg[5].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse OAEP algorithm: "); + + yrc = yh_string_to_algo(args_info.mgf1_arg, &arg[6].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse MGF1 algorithm: "); + + arg[7].s = args_info.out_arg; + arg[7].len = strlen(args_info.out_arg); + + comrc = + yh_com_get_rsa_wrapped_key(&g_ctx, arg, fmt_nofmt, + g_out_fmt == fmt_nofmt ? fmt_binary : g_out_fmt); + COM_SUCCEED_OR_DIE(comrc, "Unable to get wrapped object"); + } break; + case action_arg_getMINUS_deviceMINUS_info: comrc = yh_com_get_device_info(&g_ctx, arg, fmt_nofmt, fmt_nofmt); COM_SUCCEED_OR_DIE(comrc, "Unable to get device info"); @@ -2633,6 +2745,82 @@ int main(int argc, char *argv[]) { COM_SUCCEED_OR_DIE(comrc, "Unable to put wrapkey"); } break; + case action_arg_putMINUS_rsaMINUS_wrapkey: { + + if (args_info.delegated_given == 0) { + fprintf(stderr, "Missing delegated capabilities\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.object_id_arg; + + arg[2].s = args_info.label_arg; + arg[2].len = strlen(args_info.label_arg); + + yrc = yh_string_to_domains(args_info.domains_arg, &arg[3].w); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse domains: "); + + memset(&arg[4].c, 0, sizeof(yh_capabilities)); + yrc = + yh_string_to_capabilities(args_info.capabilities_arg, &arg[4].c); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse capabilities: "); + + memset(&arg[5].c, 0, sizeof(yh_capabilities)); + yrc = yh_string_to_capabilities(args_info.delegated_arg, &arg[5].c); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse capabilities: "); + + if (get_input_data(args_info.in_arg, &arg[6].x, &arg[6].len, + g_in_fmt == fmt_nofmt ? fmt_PEM : g_in_fmt) == + false) { + fprintf(stderr, "Failed to get input data\n"); + rc = EXIT_FAILURE; + break; + } + + comrc = yh_com_put_rsa_wrapkey(&g_ctx, arg, fmt_nofmt, fmt_nofmt); + free(arg[6].x); + COM_SUCCEED_OR_DIE(comrc, "Unable to put wrapkey"); + } break; + + case action_arg_putMINUS_publicMINUS_wrapkey: { + + if (args_info.delegated_given == 0) { + fprintf(stderr, "Missing delegated capabilities\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.object_id_arg; + + arg[2].s = args_info.label_arg; + arg[2].len = strlen(args_info.label_arg); + + yrc = yh_string_to_domains(args_info.domains_arg, &arg[3].w); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse domains: "); + + memset(&arg[4].c, 0, sizeof(yh_capabilities)); + yrc = + yh_string_to_capabilities(args_info.capabilities_arg, &arg[4].c); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse capabilities: "); + + memset(&arg[5].c, 0, sizeof(yh_capabilities)); + yrc = yh_string_to_capabilities(args_info.delegated_arg, &arg[5].c); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse capabilities: "); + + if (get_input_data(args_info.in_arg, &arg[6].x, &arg[6].len, + g_in_fmt == fmt_nofmt ? fmt_PEM : g_in_fmt) == + false) { + fprintf(stderr, "Failed to get input data\n"); + rc = EXIT_FAILURE; + break; + } + + comrc = yh_com_put_public_wrapkey(&g_ctx, arg, fmt_nofmt, fmt_nofmt); + free(arg[6].x); + COM_SUCCEED_OR_DIE(comrc, "Unable to put wrapkey"); + } break; + case action_arg_putMINUS_symmetricMINUS_key: { if (args_info.algorithm_given == 0) { fprintf(stderr, "Missing argument algorithm\n"); @@ -2688,6 +2876,91 @@ int main(int argc, char *argv[]) { COM_SUCCEED_OR_DIE(comrc, "Unable to store wrapped object"); } break; + case action_arg_putMINUS_rsaMINUS_wrapped: { + if (args_info.wrap_id_given == 0) { + fprintf(stderr, "Missing argument wrap-id\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.wrap_id_arg; + + yrc = yh_string_to_algo(args_info.oaep_arg, &arg[2].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse OAEP algorithm: "); + + yrc = yh_string_to_algo(args_info.mgf1_arg, &arg[3].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse MGF1 algorithm: "); + + if (get_input_data(args_info.in_arg, &arg[4].x, &arg[4].len, + g_in_fmt == fmt_nofmt ? fmt_binary : g_in_fmt) == + false) { + fprintf(stderr, "Failed to get input data\n"); + rc = EXIT_FAILURE; + break; + } + + comrc = yh_com_put_rsa_wrapped(&g_ctx, arg, fmt_nofmt, fmt_nofmt); + free(arg[4].x); + COM_SUCCEED_OR_DIE(comrc, "Unable to store RSA wrapped object"); + } break; + + case action_arg_putMINUS_rsaMINUS_wrappedMINUS_key: { + if (args_info.wrap_id_given == 0) { + fprintf(stderr, "Missing argument wrap-id\n"); + rc = EXIT_FAILURE; + break; + } + + if (args_info.object_type_given == 0) { + fprintf(stderr, "Missing argument type\n"); + rc = EXIT_FAILURE; + break; + } + + if (args_info.algorithm_given == 0) { + fprintf(stderr, "Missing argument key-algorithm\n"); + rc = EXIT_FAILURE; + break; + } + + arg[1].w = args_info.wrap_id_arg; + + yrc = yh_string_to_type(args_info.object_type_arg, &arg[2].t); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse object type: "); + + arg[3].w = args_info.object_id_arg; + + yrc = yh_string_to_algo(args_info.algorithm_arg, &arg[4].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse key algorithm: "); + + arg[5].s = args_info.label_arg; + arg[5].len = strlen(args_info.label_arg); + + yrc = yh_string_to_domains(args_info.domains_arg, &arg[6].w); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse domains: "); + + yrc = yh_string_to_capabilities(args_info.capabilities_arg, &arg[7].c); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse capabilities: "); + + yrc = yh_string_to_algo(args_info.oaep_arg, &arg[8].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse OAEP algorithm: "); + + yrc = yh_string_to_algo(args_info.mgf1_arg, &arg[9].a); + LIB_SUCCEED_OR_DIE(yrc, "Unable to parse MGF1 algorithm: "); + + if (get_input_data(args_info.in_arg, &arg[10].x, &arg[10].len, + g_in_fmt == fmt_nofmt ? fmt_binary : g_in_fmt) == + false) { + fprintf(stderr, "Failed to get input data\n"); + rc = EXIT_FAILURE; + break; + } + + comrc = yh_com_put_rsa_wrapped_key(&g_ctx, arg, fmt_nofmt, fmt_nofmt); + free(arg[10].x); + COM_SUCCEED_OR_DIE(comrc, "Unable to store RSA wrapped object"); + } break; + case action_arg_putMINUS_template: { if (args_info.algorithm_given == 0) { fprintf(stderr, "Missing argument algorithm\n");