From b5924451ad37211cf2ac22395508307e1c78f356 Mon Sep 17 00:00:00 2001 From: Aveen Ismail Date: Mon, 5 Aug 2024 19:09:59 +0200 Subject: [PATCH] YKCS11: Add support for ED25519 and X25519 keys --- common/util.c | 4 + ykcs11/mechanisms.c | 316 +---------- ykcs11/mechanisms.h | 2 - ykcs11/objects.c | 892 +++++++++++++++++++++++++++++++ ykcs11/objects.h | 10 + ykcs11/openssl_utils.c | 38 +- ykcs11/tests/CMakeLists.txt | 14 + ykcs11/tests/ykcs11_edx_test.c | 449 ++++++++++++++++ ykcs11/tests/ykcs11_tests.c | 53 +- ykcs11/tests/ykcs11_tests_util.c | 178 ++++++ ykcs11/tests/ykcs11_tests_util.h | 19 +- ykcs11/token.c | 12 +- ykcs11/ykcs11.c | 36 +- 13 files changed, 1692 insertions(+), 331 deletions(-) create mode 100644 ykcs11/tests/ykcs11_edx_test.c diff --git a/common/util.c b/common/util.c index d9cf63ef..bdbe6073 100644 --- a/common/util.c +++ b/common/util.c @@ -327,6 +327,10 @@ int get_curve_name(int key_algorithm) { return NID_X9_62_prime256v1; } else if(key_algorithm == YKPIV_ALGO_ECCP384) { return NID_secp384r1; + } else if(key_algorithm == YKPIV_ALGO_ED25519) { + return NID_ED25519; + } else if(key_algorithm == YKPIV_ALGO_X25519) { + return NID_X25519; } return 0; } diff --git a/ykcs11/mechanisms.c b/ykcs11/mechanisms.c index 5719f54d..e0abfc67 100644 --- a/ykcs11/mechanisms.c +++ b/ykcs11/mechanisms.c @@ -37,46 +37,15 @@ #include "utils.h" #include "debug.h" -#define PRIME256V1 "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" -#define SECP384R1 "\x06\x05\x2b\x81\x04\x00\x22" - // Supported mechanisms for key pair generation static const CK_MECHANISM_TYPE generation_mechanisms[] = { CKM_RSA_PKCS_KEY_PAIR_GEN, //CKM_ECDSA_KEY_PAIR_GEN, Deperecated - CKM_EC_KEY_PAIR_GEN + CKM_EC_KEY_PAIR_GEN, + CKM_EC_EDWARDS_KEY_PAIR_GEN, + CKM_EC_MONTGOMERY_KEY_PAIR_GEN }; -static CK_BBOOL is_RSA_mechanism(CK_MECHANISM_TYPE m) { - - switch (m) { - case CKM_RSA_PKCS_KEY_PAIR_GEN: - case CKM_RSA_PKCS: -// case CKM_RSA_9796: - case CKM_RSA_X_509: - case CKM_MD5_RSA_PKCS: - case CKM_SHA1_RSA_PKCS: - case CKM_SHA256_RSA_PKCS: - case CKM_SHA384_RSA_PKCS: - case CKM_SHA512_RSA_PKCS: -// case CKM_RIPEMD128_RSA_PKCS: - case CKM_RIPEMD160_RSA_PKCS: -// case CKM_RSA_PKCS_OAEP: -// case CKM_RSA_X9_31_KEY_PAIR_GEN: -// case CKM_RSA_X9_31: -// case CKM_SHA1_RSA_X9_31: - case CKM_RSA_PKCS_PSS: - case CKM_SHA1_RSA_PKCS_PSS: - case CKM_SHA256_RSA_PKCS_PSS: - case CKM_SHA512_RSA_PKCS_PSS: - case CKM_SHA384_RSA_PKCS_PSS: - return CK_TRUE; - - default: - return CK_FALSE; - } -} - static const ykcs11_md_t* EVP_MD_by_mechanism(CK_MECHANISM_TYPE m) { switch (m) { case CKM_SHA_1: @@ -115,6 +84,7 @@ CK_RV sign_mechanism_init(ykcs11_session_t *session, ykcs11_pkey_t *key, CK_MECH case CKM_RSA_PKCS: case CKM_RSA_PKCS_PSS: case CKM_ECDSA: + case CKM_EDDSA: // No hash required for these mechanisms break; @@ -212,7 +182,7 @@ CK_RV sign_mechanism_init(ykcs11_session_t *session, ykcs11_pkey_t *key, CK_MECH default: if(session->op_info.op.sign.rsa) { - DBG("Mechanism %lu requires an ECDSA key", session->op_info.mechanism); + DBG("Mechanism %lu requires an ECDSA or EDDSA key", session->op_info.mechanism); return CKR_KEY_TYPE_INCONSISTENT; } session->op_info.op.sign.padding = 0; @@ -358,8 +328,12 @@ CK_RV verify_mechanism_init(ykcs11_session_t *session, ykcs11_pkey_t *key, CK_ME session->op_info.md_ctx = NULL; session->op_info.mechanism = mech->mechanism; session->op_info.op.verify.pkey_ctx = NULL; + bool is_eddsa = false; switch (session->op_info.mechanism) { + case CKM_EDDSA: + is_eddsa = true; + case CKM_RSA_X_509: case CKM_RSA_PKCS: case CKM_RSA_PKCS_PSS: @@ -451,7 +425,7 @@ CK_RV verify_mechanism_init(ykcs11_session_t *session, ykcs11_pkey_t *key, CK_ME session->op_info.op.verify.padding = 0; } - if(md) { + if(md || is_eddsa) { session->op_info.md_ctx = EVP_MD_CTX_create(); if (session->op_info.md_ctx == NULL) { return CKR_FUNCTION_FAILED; @@ -516,6 +490,15 @@ CK_RV verify_mechanism_final(ykcs11_session_t *session, CK_BYTE_PTR sig, CK_ULON int rc; + if (session->op_info.mechanism == CKM_EDDSA) { + rc = EVP_DigestVerify(session->op_info.md_ctx, sig, sig_len, session->op_info.buf, session->op_info.buf_len); + if(rc <= 0) { + DBG("EVP_PKEY_verify failed"); + return rc < 0 ? CKR_FUNCTION_FAILED : CKR_SIGNATURE_INVALID; + } + return CKR_OK; + } + CK_BYTE der[1024] = {0}; if(!session->op_info.op.verify.padding) { if(sig_len > sizeof(der)) { @@ -575,265 +558,6 @@ CK_RV check_generation_mechanism(CK_MECHANISM_PTR m) { } -CK_RV check_pubkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { - - CK_BBOOL rsa = is_RSA_mechanism(mechanism->mechanism); - - for (CK_ULONG i = 0; i < n; i++) { - switch (templ[i].type) { - case CKA_CLASS: - if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) { - DBG("Bad CKA_CLASS"); - return CKR_TEMPLATE_INCONSISTENT; - } - break; - - case CKA_KEY_TYPE: - if ((rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || - (rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_EC)) { - DBG("Bad CKA_KEY_TYPE"); - return CKR_TEMPLATE_INCONSISTENT; - } - - break; - - case CKA_PUBLIC_EXPONENT: - if (rsa == CK_FALSE) { - DBG("Non-RSA key can't have CKA_PUBLIC_EXPONENT"); - return CKR_TEMPLATE_INCONSISTENT; - } - - if(!do_check_public_exponent(templ[i].pValue, templ[i].ulValueLen)) { - DBG("Unsupported public exponent"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - - break; - - case CKA_MODULUS_BITS: - if (rsa == CK_FALSE) { - DBG("Non-RSA key can't have CKA_MODULUS_BITS"); - return CKR_TEMPLATE_INCONSISTENT; - } - switch(*(CK_ULONG_PTR)templ[i].pValue) { - case 1024: - gen->algorithm = YKPIV_ALGO_RSA1024; - break; - case 2048: - gen->algorithm = YKPIV_ALGO_RSA2048; - break; - case 3072: - gen->algorithm = YKPIV_ALGO_RSA3072; - break; - case 4096: - gen->algorithm = YKPIV_ALGO_RSA4096; - break; - default: - DBG("Unsupported MODULUS_BITS (key length)"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_EC_PARAMS: - if (rsa == CK_TRUE) { - DBG("RSA key can't have CKA_EC_PARAMS"); - return CKR_TEMPLATE_INCONSISTENT; - } - // Support PRIME256V1 and SECP384R1 - if (templ[i].ulValueLen == 10 && memcmp((CK_BYTE_PTR)templ[i].pValue, PRIME256V1, 10) == 0) - gen->algorithm = YKPIV_ALGO_ECCP256; - else if(templ[i].ulValueLen == 7 && memcmp((CK_BYTE_PTR)templ[i].pValue, SECP384R1, 7) == 0) - gen->algorithm = YKPIV_ALGO_ECCP384; - else { - DBG("Bad CKA_EC_PARAMS"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_ID: - if (find_pubk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { - DBG("Bad CKA_ID"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); - break; - - case CKA_COPYABLE: - case CKA_DESTROYABLE: - case CKA_EXTRACTABLE: - case CKA_SENSITIVE: - case CKA_TOKEN: - case CKA_ENCRYPT: - case CKA_DECRYPT: - case CKA_SIGN: - case CKA_SIGN_RECOVER: - case CKA_VERIFY: - case CKA_VERIFY_RECOVER: - case CKA_WRAP: - case CKA_UNWRAP: - case CKA_DERIVE: - case CKA_PRIVATE: - case CKA_LABEL: - case CKA_ISSUER: - case CKA_SUBJECT: - // Ignore these attributes for now - break; - - default: - DBG("Invalid attribute %lx in public key template", templ[i].type); - return CKR_ATTRIBUTE_TYPE_INVALID; - } - } - - return CKR_OK; - -} - -CK_RV check_pvtkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { - - CK_BBOOL rsa = is_RSA_mechanism(mechanism->mechanism); - CK_BYTE b_tmp = 0; - - for (CK_ULONG i = 0; i < n; i++) { - switch (templ[i].type) { - case CKA_CLASS: - if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) { - DBG("Bad CKA_CLASS"); - return CKR_TEMPLATE_INCONSISTENT; - } - break; - - case CKA_KEY_TYPE: - if ((rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || - (rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_EC)) { - DBG("Bad CKA_KEY_TYPE"); - return CKR_TEMPLATE_INCONSISTENT; - } - break; - - case CKA_ID: - if (find_pvtk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { - DBG("Bad CKA_ID"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - // Check if ID was already specified in the public key template - // In that case it has to match - if (gen->key_id != 0 && - gen->key_id != *((CK_BYTE_PTR)templ[i].pValue)) { - DBG("Inconsistent CKA_ID"); - return CKR_TEMPLATE_INCONSISTENT; - } - gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); - break; - - case CKA_TOKEN: - if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { - DBG("CKA_TOKEN must be TRUE or omitted"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_PRIVATE: - if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { - DBG("CKA_PRIVATE must be TRUE or omitted"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_SENSITIVE: - if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { - DBG("CKA_SENSITIVE must be TRUE or omitted"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_EXTRACTABLE: - if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { - DBG("CKA_EXTRACTABLE must be FALSE or omitted"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_ALWAYS_AUTHENTICATE: - if (*((CK_BBOOL *)templ[i].pValue) == CK_TRUE) { - if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT && - gen->pin_policy != YKPIV_PINPOLICY_ALWAYS) { - DBG("Inconsistent PIN policy"); - return CKR_TEMPLATE_INCONSISTENT; - } - gen->pin_policy = YKPIV_PINPOLICY_ALWAYS; - } else if (*((CK_BBOOL *)templ[i].pValue) == CK_FALSE) { - if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT) { - DBG("Inconsistent PIN policy"); - return CKR_TEMPLATE_INCONSISTENT; - } - } else { - DBG("CKA_ALWAYS_AUTHENTICATE must be TRUE, FALSE, or omitted"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - break; - - case CKA_YUBICO_TOUCH_POLICY: - b_tmp = *((CK_BYTE *)templ[i].pValue); - if (b_tmp != YKPIV_TOUCHPOLICY_ALWAYS && - b_tmp != YKPIV_TOUCHPOLICY_CACHED && - b_tmp != YKPIV_TOUCHPOLICY_NEVER && - b_tmp != YKPIV_TOUCHPOLICY_DEFAULT) { - DBG("Invalid value for CKA_YUBICO_TOUCH_POLICY"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - if (gen->touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && - gen->touch_policy != b_tmp) { - DBG("Inconsistent touch policy"); - return CKR_TEMPLATE_INCONSISTENT; - } - gen->touch_policy = b_tmp; - break; - - case CKA_YUBICO_PIN_POLICY: - b_tmp = *((CK_BYTE *)templ[i].pValue); - if (b_tmp != YKPIV_PINPOLICY_ALWAYS && - b_tmp != YKPIV_PINPOLICY_ONCE && - b_tmp != YKPIV_PINPOLICY_NEVER && - b_tmp != YKPIV_PINPOLICY_DEFAULT) { - DBG("Invalid value for CKA_YUBICO_PIN_POLICY"); - return CKR_ATTRIBUTE_VALUE_INVALID; - } - if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT && - gen->pin_policy != b_tmp) { - DBG("Inconsistent PIN policy"); - return CKR_TEMPLATE_INCONSISTENT; - } - gen->pin_policy = b_tmp; - break; - - case CKA_COPYABLE: - case CKA_DESTROYABLE: - case CKA_ENCRYPT: - case CKA_DECRYPT: - case CKA_WRAP: - case CKA_UNWRAP: - case CKA_SIGN: - case CKA_SIGN_RECOVER: - case CKA_VERIFY: - case CKA_VERIFY_RECOVER: - case CKA_DERIVE: - case CKA_LABEL: - case CKA_ISSUER: - case CKA_SUBJECT: - // Ignore these attributes for now - break; - - default: - DBG("Invalid attribute 0x%lx in private key template", templ[i].type); - return CKR_ATTRIBUTE_TYPE_INVALID; - } - } - - return CKR_OK; - -} - CK_RV validate_derive_key_attribute(CK_ATTRIBUTE_TYPE type, void *value) { switch (type) { case CKA_TOKEN: @@ -921,7 +645,7 @@ CK_RV digest_mechanism_init(ykcs11_session_t *session, CK_MECHANISM_PTR mech) { CK_RV digest_mechanism_update(ykcs11_session_t *session, CK_BYTE_PTR in, CK_ULONG in_len) { - if(session->op_info.md_ctx) { + if(session->op_info.md_ctx && session->op_info.mechanism != CKM_EDDSA) { if (EVP_DigestUpdate(session->op_info.md_ctx, in, in_len) <= 0) { DBG("EVP_DigestUpdate failed"); return CKR_FUNCTION_FAILED; diff --git a/ykcs11/mechanisms.h b/ykcs11/mechanisms.h index d5887184..93b054ab 100644 --- a/ykcs11/mechanisms.h +++ b/ykcs11/mechanisms.h @@ -42,8 +42,6 @@ CK_RV verify_mechanism_final(ykcs11_session_t *session, CK_BYTE_PTR sig, CK_ULON CK_RV verify_mechanism_cleanup(ykcs11_session_t *session); CK_RV check_generation_mechanism(CK_MECHANISM_PTR m); -CK_RV check_pubkey_template(gen_info_t *gen_info, CK_MECHANISM_PTR mech, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c -CK_RV check_pvtkey_template(gen_info_t *gen_info, CK_MECHANISM_PTR mech, CK_ATTRIBUTE_PTR templ, CK_ULONG n); // TODO: Move to objects.c CK_RV validate_derive_key_attribute(CK_ATTRIBUTE_TYPE type, void *value); CK_RV digest_mechanism_init(ykcs11_session_t *session, CK_MECHANISM_PTR mech); diff --git a/ykcs11/objects.c b/ykcs11/objects.c index a88f8bba..068f482b 100644 --- a/ykcs11/objects.c +++ b/ykcs11/objects.c @@ -39,6 +39,7 @@ #define PRIME256V1 "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" // TODO: already define in mechanisms.c. Move #define SECP384R1 "\x06\x05\x2b\x81\x04\x00\x22" // TODO: already define in mechanisms.c. Move +#define ED25519OID "\x13\x0c\x65\x64\x77\x61\x72\x64\x73\x32\x35\x35\x31\x39" static CK_RV get_doa(ykcs11_slot_t *s, piv_obj_id_t obj, CK_ATTRIBUTE_PTR template); static CK_RV get_coa(ykcs11_slot_t *s, piv_obj_id_t obj, CK_ATTRIBUTE_PTR template); @@ -1690,6 +1691,443 @@ CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, return CKR_OK; } +CK_RV check_create_x25519_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, + CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_value = CK_FALSE; + + CK_BYTE b_tmp; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) { + DBG("CKA_CLASS muste be CKO_PRIVATE_KEY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKK_EC_MONTGOMERY) { + DBG("CKA_KEY_TYPE muste be CKK_EC_MONTGOMERY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ID: + has_id = CK_TRUE; + if (find_pvtk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("CKA_ID is not a valid PIV object id"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VALUE: + has_value = CK_TRUE; + *value = (CK_BYTE_PTR)templ[i].pValue; + *value_len = templ[i].ulValueLen; + break; + +// case CKA_EC_PARAMS: +// ec_params = (CK_BYTE_PTR)templ[i].pValue; +// ec_params_len = templ[i].ulValueLen; +// break; + + case CKA_TOKEN: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_TOKEN must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_PRIVATE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_PRIVATE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SENSITIVE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_SENSITIVE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_EXTRACTABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_EXTRACTABLE must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_COPYABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_COPYABLE must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_DESTROYABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_DESTROYABLE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_DECRYPT: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_ENCRYPT must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ENCRYPT: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_ENCRYPT must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SIGN: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_SIGN must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SIGN_RECOVER: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_SIGN_RECOVER must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_VERIFY: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_VERIFY must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_VERIFY_RECOVER: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_VERIFY_RECOVER must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_WRAP: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_WRAP must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_UNWRAP: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_UNWRAP must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ALWAYS_AUTHENTICATE: + if (*((CK_BBOOL *)templ[i].pValue) == CK_TRUE) { + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT && + *pin_policy != YKPIV_PINPOLICY_ALWAYS) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *pin_policy = YKPIV_PINPOLICY_ALWAYS; + } else if (*((CK_BBOOL *)templ[i].pValue) == CK_FALSE) { + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + } else { + DBG("CKA_ALWAYS_AUTHENTICATE must be TRUE, FALSE, or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_YUBICO_TOUCH_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_TOUCHPOLICY_ALWAYS && + b_tmp != YKPIV_TOUCHPOLICY_CACHED && + b_tmp != YKPIV_TOUCHPOLICY_NEVER && + b_tmp != YKPIV_TOUCHPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_TOUCH_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (*touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && + *touch_policy != b_tmp) { + DBG("Inconsistent touch policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *touch_policy = b_tmp; + break; + + case CKA_YUBICO_PIN_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_PINPOLICY_ALWAYS && + b_tmp != YKPIV_PINPOLICY_ONCE && + b_tmp != YKPIV_PINPOLICY_NEVER && + b_tmp != YKPIV_PINPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_PIN_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT && + *pin_policy != b_tmp) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *pin_policy = b_tmp; + break; + + case CKA_DERIVE: + case CKA_LABEL: + case CKA_SUBJECT: + // Ignore other attributes + break; + + default: + DBG("Invalid attribute type 0x%lx", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_value == CK_FALSE) { + return CKR_TEMPLATE_INCOMPLETE; + } + + return CKR_OK; +} + +CK_RV check_create_ed_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, + CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy) { + + CK_ULONG i; + CK_BBOOL has_id = CK_FALSE; + CK_BBOOL has_value = CK_FALSE; + CK_BBOOL has_params = CK_FALSE; + + CK_BYTE_PTR ec_params = NULL; + CK_ULONG ec_params_len = 0; + CK_BYTE b_tmp; + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) { + DBG("CKA_CLASS muste be CKO_PRIVATE_KEY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKK_EC_EDWARDS) { + DBG("CKA_KEY_TYPE muste be CKK_EC_EDWARDS"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ID: + has_id = CK_TRUE; + if (find_pvtk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("CKA_ID is not a valid PIV object id"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + *id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_VALUE: + has_value = CK_TRUE; + *value = (CK_BYTE_PTR)templ[i].pValue; + *value_len = templ[i].ulValueLen; + break; + + case CKA_EC_PARAMS: + has_params = CK_TRUE; + ec_params = (CK_BYTE_PTR)templ[i].pValue; + ec_params_len = templ[i].ulValueLen; + break; + + case CKA_TOKEN: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_TOKEN must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_PRIVATE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_PRIVATE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SENSITIVE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_SENSITIVE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_EXTRACTABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_EXTRACTABLE must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_COPYABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_COPYABLE must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_DESTROYABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_DESTROYABLE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ENCRYPT: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_ENCRYPT must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SIGN: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_SIGN must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SIGN_RECOVER: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_SIGN_RECOVER must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_VERIFY: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_VERIFY must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_VERIFY_RECOVER: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_VERIFY_RECOVER must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_WRAP: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_WRAP must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_UNWRAP: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_UNWRAP must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ALWAYS_AUTHENTICATE: + if (*((CK_BBOOL *)templ[i].pValue) == CK_TRUE) { + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT && + *pin_policy != YKPIV_PINPOLICY_ALWAYS) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *pin_policy = YKPIV_PINPOLICY_ALWAYS; + } else if (*((CK_BBOOL *)templ[i].pValue) == CK_FALSE) { + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + } else { + DBG("CKA_ALWAYS_AUTHENTICATE must be TRUE, FALSE, or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_YUBICO_TOUCH_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_TOUCHPOLICY_ALWAYS && + b_tmp != YKPIV_TOUCHPOLICY_CACHED && + b_tmp != YKPIV_TOUCHPOLICY_NEVER && + b_tmp != YKPIV_TOUCHPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_TOUCH_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (*touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && + *touch_policy != b_tmp) { + DBG("Inconsistent touch policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *touch_policy = b_tmp; + break; + + case CKA_YUBICO_PIN_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_PINPOLICY_ALWAYS && + b_tmp != YKPIV_PINPOLICY_ONCE && + b_tmp != YKPIV_PINPOLICY_NEVER && + b_tmp != YKPIV_PINPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_PIN_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (*pin_policy != YKPIV_PINPOLICY_DEFAULT && + *pin_policy != b_tmp) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + *pin_policy = b_tmp; + break; + + case CKA_DERIVE: + case CKA_DECRYPT: + case CKA_LABEL: + case CKA_SUBJECT: + // Ignore other attributes + break; + + default: + DBG("Invalid attribute type 0x%lx", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + if (has_id == CK_FALSE || + has_value == CK_FALSE || + has_params == CK_FALSE) { + return CKR_TEMPLATE_INCOMPLETE; + } + + if (ec_params_len != 14 || memcmp(ec_params, ED25519OID, ec_params_len) != 0) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + return CKR_OK; +} + CK_RV check_create_ec_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, CK_BYTE_PTR *value, CK_ULONG_PTR value_len, CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy) { @@ -2183,3 +2621,457 @@ CK_RV check_create_rsa_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, return CKR_OK; } + +static CK_RV check_rsa_pubkey_template(gen_info_t *gen, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + for (CK_ULONG i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) { + DBG("Bad CKA_CLASS"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_KEY_TYPE *)templ[i].pValue) != CKK_RSA) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + + break; + + case CKA_PUBLIC_EXPONENT: + if(!do_check_public_exponent(templ[i].pValue, templ[i].ulValueLen)) { + DBG("Unsupported public exponent"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + + break; + + case CKA_MODULUS_BITS: + switch(*(CK_ULONG_PTR)templ[i].pValue) { + case 1024: + gen->algorithm = YKPIV_ALGO_RSA1024; + break; + case 2048: + gen->algorithm = YKPIV_ALGO_RSA2048; + break; + case 3072: + gen->algorithm = YKPIV_ALGO_RSA3072; + break; + case 4096: + gen->algorithm = YKPIV_ALGO_RSA4096; + break; + default: + DBG("Unsupported MODULUS_BITS (key length)"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ID: + if (find_pubk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("Bad CKA_ID"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_COPYABLE: + case CKA_DESTROYABLE: + case CKA_EXTRACTABLE: + case CKA_SENSITIVE: + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_WRAP: + case CKA_UNWRAP: + case CKA_DERIVE: + case CKA_PRIVATE: + case CKA_LABEL: + case CKA_ISSUER: + case CKA_SUBJECT: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in public key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; +} + +static CK_RV check_ec_pubkey_template(gen_info_t *gen, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + for (CK_ULONG i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) { + DBG("Bad CKA_CLASS"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_KEY_TYPE *)templ[i].pValue) != CKK_EC) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + + break; + + case CKA_EC_PARAMS: + // Support PRIME256V1 and SECP384R1 + if (templ[i].ulValueLen == 10 && memcmp((CK_BYTE_PTR)templ[i].pValue, PRIME256V1, 10) == 0) + gen->algorithm = YKPIV_ALGO_ECCP256; + else if(templ[i].ulValueLen == 7 && memcmp((CK_BYTE_PTR)templ[i].pValue, SECP384R1, 7) == 0) + gen->algorithm = YKPIV_ALGO_ECCP384; + else { + DBG("Bad CKA_EC_PARAMS"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ID: + if (find_pubk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("Bad CKA_ID"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_COPYABLE: + case CKA_DESTROYABLE: + case CKA_EXTRACTABLE: + case CKA_SENSITIVE: + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_WRAP: + case CKA_UNWRAP: + case CKA_DERIVE: + case CKA_PRIVATE: + case CKA_LABEL: + case CKA_ISSUER: + case CKA_SUBJECT: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in public key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; + +} + +static CK_RV check_ed_pubkey_template(gen_info_t *gen, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + gen->algorithm = YKPIV_ALGO_ED25519; + + for (CK_ULONG i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) { + DBG("Bad CKA_CLASS"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_KEY_TYPE *)templ[i].pValue) != CKK_EC_EDWARDS) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_ID: + if (find_pubk_object(*((CK_BYTE_PTR) templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("Bad CKA_ID"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + gen->key_id = *((CK_BYTE_PTR) templ[i].pValue); + break; + + case CKA_EC_PARAMS: + case CKA_COPYABLE: + case CKA_DESTROYABLE: + case CKA_EXTRACTABLE: + case CKA_SENSITIVE: + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_WRAP: + case CKA_UNWRAP: + case CKA_DERIVE: + case CKA_PRIVATE: + case CKA_LABEL: + case CKA_ISSUER: + case CKA_SUBJECT: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in public key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; +} + +static CK_RV check_x_pubkey_template(gen_info_t *gen, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + gen->algorithm = YKPIV_ALGO_X25519; + + for (CK_ULONG i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) { + DBG("Bad CKA_CLASS"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_KEY_TYPE: + if (*((CK_KEY_TYPE *)templ[i].pValue) != CKK_EC_MONTGOMERY) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_ID: + if (find_pubk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("Bad CKA_ID"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_EC_PARAMS: + case CKA_COPYABLE: + case CKA_DESTROYABLE: + case CKA_EXTRACTABLE: + case CKA_SENSITIVE: + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_WRAP: + case CKA_UNWRAP: + case CKA_DERIVE: + case CKA_PRIVATE: + case CKA_LABEL: + case CKA_ISSUER: + case CKA_SUBJECT: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute %lx in public key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; +} + +CK_RV check_pubkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + switch(mechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + return check_rsa_pubkey_template(gen, templ, n); + case CKM_EC_KEY_PAIR_GEN: + return check_ec_pubkey_template(gen, templ, n); + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + return check_ed_pubkey_template(gen, templ, n); + case CKM_EC_MONTGOMERY_KEY_PAIR_GEN: + return check_x_pubkey_template(gen, templ, n); + default: + DBG("Unsupported public key mechanism %lx ", mechanism->mechanism); + return CKR_MECHANISM_INVALID; + } +} + +CK_RV check_pvtkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + CK_BYTE b_tmp = 0; + + for (CK_ULONG i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) { + DBG("Bad CKA_CLASS"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_KEY_TYPE: + switch (mechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + if (*((CK_KEY_TYPE *) templ[i].pValue) != CKK_RSA) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + case CKM_EC_KEY_PAIR_GEN: + if (*((CK_KEY_TYPE *) templ[i].pValue) != CKK_EC) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + case CKM_EC_EDWARDS_KEY_PAIR_GEN: + if (*((CK_KEY_TYPE *) templ[i].pValue) != CKK_EC_EDWARDS) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + case CKM_EC_MONTGOMERY_KEY_PAIR_GEN: + if (*((CK_KEY_TYPE *) templ[i].pValue) != CKK_EC_MONTGOMERY) { + DBG("Bad CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + default: + DBG("Unsupported CKA_KEY_TYPE"); + return CKR_TEMPLATE_INCONSISTENT; + } + break; + + case CKA_ID: + if (find_pvtk_object(*((CK_BYTE_PTR)templ[i].pValue)) == PIV_INVALID_OBJ) { + DBG("Bad CKA_ID"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + // Check if ID was already specified in the public key template + // In that case it has to match + if (gen->key_id != 0 && + gen->key_id != *((CK_BYTE_PTR)templ[i].pValue)) { + DBG("Inconsistent CKA_ID"); + return CKR_TEMPLATE_INCONSISTENT; + } + gen->key_id = *((CK_BYTE_PTR)templ[i].pValue); + break; + + case CKA_TOKEN: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_TOKEN must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_PRIVATE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_PRIVATE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_SENSITIVE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_TRUE) { + DBG("CKA_SENSITIVE must be TRUE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_EXTRACTABLE: + if (*((CK_BBOOL *)templ[i].pValue) != CK_FALSE) { + DBG("CKA_EXTRACTABLE must be FALSE or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_ALWAYS_AUTHENTICATE: + if (*((CK_BBOOL *)templ[i].pValue) == CK_TRUE) { + if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT && + gen->pin_policy != YKPIV_PINPOLICY_ALWAYS) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + gen->pin_policy = YKPIV_PINPOLICY_ALWAYS; + } else if (*((CK_BBOOL *)templ[i].pValue) == CK_FALSE) { + if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + } else { + DBG("CKA_ALWAYS_AUTHENTICATE must be TRUE, FALSE, or omitted"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + break; + + case CKA_YUBICO_TOUCH_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_TOUCHPOLICY_ALWAYS && + b_tmp != YKPIV_TOUCHPOLICY_CACHED && + b_tmp != YKPIV_TOUCHPOLICY_NEVER && + b_tmp != YKPIV_TOUCHPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_TOUCH_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (gen->touch_policy != YKPIV_TOUCHPOLICY_DEFAULT && + gen->touch_policy != b_tmp) { + DBG("Inconsistent touch policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + gen->touch_policy = b_tmp; + break; + + case CKA_YUBICO_PIN_POLICY: + b_tmp = *((CK_BYTE *)templ[i].pValue); + if (b_tmp != YKPIV_PINPOLICY_ALWAYS && + b_tmp != YKPIV_PINPOLICY_ONCE && + b_tmp != YKPIV_PINPOLICY_NEVER && + b_tmp != YKPIV_PINPOLICY_DEFAULT) { + DBG("Invalid value for CKA_YUBICO_PIN_POLICY"); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + if (gen->pin_policy != YKPIV_PINPOLICY_DEFAULT && + gen->pin_policy != b_tmp) { + DBG("Inconsistent PIN policy"); + return CKR_TEMPLATE_INCONSISTENT; + } + gen->pin_policy = b_tmp; + break; + + case CKA_COPYABLE: + case CKA_DESTROYABLE: + case CKA_ENCRYPT: + case CKA_DECRYPT: + case CKA_WRAP: + case CKA_UNWRAP: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_DERIVE: + case CKA_LABEL: + case CKA_ISSUER: + case CKA_SUBJECT: + // Ignore these attributes for now + break; + + default: + DBG("Invalid attribute 0x%lx in private key template", templ[i].type); + return CKR_ATTRIBUTE_TYPE_INVALID; + } + } + + return CKR_OK; + +} \ No newline at end of file diff --git a/ykcs11/objects.h b/ykcs11/objects.h index 4194240b..315710aa 100644 --- a/ykcs11/objects.h +++ b/ykcs11/objects.h @@ -58,6 +58,12 @@ CK_RV get_data_len(ykcs11_slot_t *s, CK_BYTE sub_id, CK_ULONG_PTR len); CK_RV check_create_cert(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, CK_BYTE_PTR *value, CK_ULONG_PTR cert_len); +CK_RV check_create_x25519_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, + CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy); +CK_RV check_create_ed_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, + CK_BYTE_PTR *value, CK_ULONG_PTR value_len, + CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy); CK_RV check_create_ec_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, CK_BYTE_PTR *value, CK_ULONG_PTR value_len, CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy); @@ -68,4 +74,8 @@ CK_RV check_create_rsa_key(CK_ATTRIBUTE_PTR templ, CK_ULONG n, CK_BYTE_PTR id, CK_BYTE_PTR *dq, CK_ULONG_PTR dq_len, CK_BYTE_PTR *qinv, CK_ULONG_PTR qinv_len, CK_BYTE_PTR touch_policy, CK_BYTE_PTR pin_policy); + +CK_RV check_pubkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n); +CK_RV check_pvtkey_template(gen_info_t *gen, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG n); + #endif diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index ae801bd7..e6208a74 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -259,11 +259,9 @@ CK_RV do_create_rsa_key(CK_BYTE_PTR mod, CK_ULONG mod_len, CK_BYTE_PTR exp, CK_U } CK_RV do_create_public_key(CK_BYTE_PTR in, CK_ULONG in_len, CK_ULONG algorithm, ykcs11_pkey_t **pkey) { - int curve_name = get_curve_name(algorithm); CK_BYTE_PTR eob = in + in_len; unsigned long offs, len; - - if (curve_name == 0) { + if (YKPIV_IS_RSA(algorithm)) { if(in >= eob) return CKR_GENERAL_ERROR; @@ -293,8 +291,7 @@ CK_RV do_create_public_key(CK_BYTE_PTR in, CK_ULONG in_len, CK_ULONG algorithm, in += offs; return do_create_rsa_key(mod, mod_len, in, len, pkey); - } - else { + } else { if(in >= eob) return CKR_GENERAL_ERROR; @@ -306,14 +303,31 @@ CK_RV do_create_public_key(CK_BYTE_PTR in, CK_ULONG in_len, CK_ULONG algorithm, return CKR_GENERAL_ERROR; in += offs; - return do_create_ec_key(in, len, curve_name, pkey); + + if (YKPIV_IS_EC(algorithm)) { + int curve_name = get_curve_name(algorithm); + return do_create_ec_key(in, len, curve_name, pkey); + } else if (YKPIV_IS_25519(algorithm)) { + if (algorithm == YKPIV_ALGO_ED25519) { + *pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, in, len); + } else { + *pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, in, len); + } + if (*pkey == NULL) { + return CKR_HOST_MEMORY; + } + return CKR_OK; + } } + DBG("Unsupported key algorithm"); + return CKR_DATA_INVALID; } CK_RV do_sign_empty_cert(const char *cn, ykcs11_pkey_t *pubkey, ykcs11_pkey_t *pvtkey, ykcs11_x509_t **cert) { *cert = X509_new(); - if (*cert == NULL) + if (*cert == NULL) { return CKR_HOST_MEMORY; + } X509_set_version(*cert, 2); // Version 3 X509_NAME_add_entry_by_txt(X509_get_issuer_name(*cert), "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0); X509_NAME_add_entry_by_txt(X509_get_subject_name(*cert), "CN", MBSTRING_ASC, (const unsigned char*)cn, -1, -1, 0); @@ -515,9 +529,12 @@ CK_KEY_TYPE do_get_key_type(ykcs11_pkey_t *key) { switch (EVP_PKEY_base_id(key)) { case EVP_PKEY_RSA: return CKK_RSA; - case EVP_PKEY_EC: return CKK_EC; + case EVP_PKEY_ED25519: + return CKK_EC_EDWARDS; + case EVP_PKEY_X25519: + return CKK_EC_MONTGOMERY; } } return CKK_VENDOR_DEFINED; // Actually an error @@ -538,6 +555,7 @@ CK_ULONG do_get_signature_size(ykcs11_pkey_t *key) { case EVP_PKEY_RSA: return EVP_PKEY_size(key); case EVP_PKEY_EC: + case EVP_PKEY_ED25519: switch(EVP_PKEY_bits(key)) { case 256: return 64; @@ -571,6 +589,10 @@ CK_BYTE do_get_key_algorithm(ykcs11_pkey_t *key) { case 384: return YKPIV_ALGO_ECCP384; } + case EVP_PKEY_ED25519: + return YKPIV_ALGO_ED25519; + case EVP_PKEY_X25519: + return YKPIV_ALGO_X25519; } } return 0; diff --git a/ykcs11/tests/CMakeLists.txt b/ykcs11/tests/CMakeLists.txt index 726fb214..99d5a9a6 100644 --- a/ykcs11/tests/CMakeLists.txt +++ b/ykcs11/tests/CMakeLists.txt @@ -66,4 +66,18 @@ if(NOT DEFINED SKIP_TESTS) WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/ykcs11/tests/ ) set_property(TEST test_ykcs11_interfaces APPEND PROPERTY ENVIRONMENT "YKPIV_ENV_HWTESTS_CONFIRMED=${HW_TESTS}") + + set(SOURCE_YKCS11_EDX_TESTS + ykcs11_edx_test.c + ykcs11_tests_util.c + ) + add_executable(test_ykcs11_edx ${SOURCE_YKCS11_EDX_TESTS}) + target_link_libraries(test_ykcs11_edx ykcs11_shared ${LIBCRYPTO_LDFLAGS}) + add_test( + NAME test_ykcs11_edx + COMMAND test_ykcs11_edx + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/ykcs11/tests/ + ) + set_property(TEST test_ykcs11_edx APPEND PROPERTY ENVIRONMENT "YKPIV_ENV_HWTESTS_CONFIRMED=${HW_TESTS}") + endif(NOT DEFINED SKIP_TESTS) \ No newline at end of file diff --git a/ykcs11/tests/ykcs11_edx_test.c b/ykcs11/tests/ykcs11_edx_test.c new file mode 100644 index 00000000..4cf4757d --- /dev/null +++ b/ykcs11/tests/ykcs11_edx_test.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2015-2017,2019-2020 Yubico AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "../../common/openssl-compat.h" +#include "../ykcs11.h" +#include "../ykcs11-config.h" + +#include + +#include +#include +#include + +#include "ykcs11_tests_util.h" + +#ifdef _WIN32 +#define dprintf(fd, ...) fprintf(stdout, __VA_ARGS__) +#endif + +CK_FUNCTION_LIST_3_0_PTR funcs; + +#define asrt(c, e, m) _asrt(__FILE__, __LINE__, c, e, m); + +static void _asrt(const char *file, int line, CK_ULONG check, CK_ULONG expected, const char *msg) { + + if (check == expected) + return; + + fprintf(stderr, "%s.%d: <%s> check failed with value %lu (0x%lx), expected %lu (0x%lx)\n", + file, line, msg, check, check, expected, expected); + + exit(EXIT_FAILURE); + +} + +static void get_functions() { + CK_INTERFACE_PTR interface; + asrt(C_GetInterface(NULL,NULL,&interface,0), CKR_OK, "C_GetInterface default"); + funcs = interface->pFunctionList; +} + +#if HW_TESTS +static void init_connection() { + asrt(funcs->C_Initialize(NULL), CKR_OK, "INITIALIZE"); + CK_SLOT_ID pSlotList[16]; + CK_ULONG pulCount = 16; + asrt(funcs->C_GetSlotList(true, pSlotList, &pulCount), CKR_OK, "GETSLOTLIST"); +} + +static bool has_hardware_support() { + const CK_VERSION HW = {5, 70}; + CK_TOKEN_INFO info; + bool ret = false; + + init_connection(); + asrt(funcs->C_GetTokenInfo(0, &info), CKR_OK, "GetTokeninfo"); + + if(info.firmwareVersion.major > HW.major || (info.firmwareVersion.major == HW.major && info.firmwareVersion.minor >= HW.minor)) { + ret = true; + } + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + return ret; +} + +static void test_mechanism_list_and_info() { + dprintf(0, "TEST START: test_mechanism_list_and_info()\n"); + + CK_MECHANISM_TYPE_PTR mechs; + CK_ULONG n_mechs; + CK_MECHANISM_INFO info; + CK_ULONG i; + + init_connection(); + asrt(funcs->C_GetMechanismList(0, NULL, &n_mechs), CKR_OK, "GetMechanismList"); + + mechs = malloc(n_mechs * sizeof(CK_MECHANISM_TYPE)); + asrt(funcs->C_GetMechanismList(0, mechs, &n_mechs), CKR_OK, "GetMechanismList"); + + CK_MECHANISM_INFO mech_info = {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}; + CK_BOOL ed_mech_found = CK_FALSE, ex_mech_found = CK_FALSE; + for (i = 0; i < n_mechs; i++) { + if(mechs[i] == CKM_EC_EDWARDS_KEY_PAIR_GEN) { + ed_mech_found = CK_TRUE; + asrt(funcs->C_GetMechanismInfo(0, CKM_EC_EDWARDS_KEY_PAIR_GEN, &info), CKR_OK, "GET MECH INFO"); + asrt(memcmp(&mech_info, &info, sizeof(CK_MECHANISM_INFO)), 0, "CHECK MECH INFO"); + } + + if(mechs[i] == CKM_EC_MONTGOMERY_KEY_PAIR_GEN) { + ex_mech_found = CK_TRUE; + asrt(funcs->C_GetMechanismInfo(0, CKM_EC_MONTGOMERY_KEY_PAIR_GEN, &info), CKR_OK, "GET MECH INFO"); + asrt(memcmp(&mech_info, &info, sizeof(CK_MECHANISM_INFO)), 0, "CHECK MECH INFO"); + } + } + free(mechs); + + if(!ed_mech_found || !ex_mech_found) { + asrt(CK_TRUE, CK_FALSE, "ED and EC generation mechanism are not supported"); + } + + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_mechanism_list_and_info()\n"); +} + +static void get_attr(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE privkey, CK_ULONG privkey_type) { + CK_ULONG type; + CK_ATTRIBUTE template[] = { + {CKA_KEY_TYPE, &type, sizeof(type)} + }; + asrt(funcs->C_GetAttributeValue(session, privkey, template, 1), CKR_OK, "GET CKA_ID"); + asrt(type, privkey_type, "GET CKA_KEY_TYPE"); +} + +static void find_object(CK_SESSION_HANDLE session, CK_ULONG privkey_type) { + CK_OBJECT_HANDLE objects[10] = {0}; + CK_ULONG objects_len = 0; + + CK_ULONG type = privkey_type; + CK_ATTRIBUTE template[] = { + {CKA_KEY_TYPE, &type, sizeof(type)} + }; + asrt(funcs->C_FindObjectsInit(session, template, 1), CKR_OK, "FIND INIT"); + asrt(funcs->C_FindObjects(session, objects, 10, &objects_len), CKR_OK, "FIND"); + asrt(funcs->C_FindObjectsFinal(session), CKR_OK, "FIND FINAL"); + asrt(objects_len, 1, "NUMBER OF FOUND OBJECTS"); +} + +static void test_generate_ed() { + dprintf(0, "TEST START: test_generate_ed25519()\n"); + CK_OBJECT_HANDLE pvtkey = 0, pubkey = 0; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + generate_ed_key(funcs, session, &pubkey, &pvtkey); + get_attr(session, pvtkey, CKK_EC_EDWARDS); + find_object(session, CKK_EC_EDWARDS); + + destroy_test_objects(funcs, session, &pvtkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_generate_ed25519()\n"); +} + +static void test_generate_ex() { + dprintf(0, "TEST START: test_generate_ex25519()\n"); + CK_OBJECT_HANDLE pvtkey = 0, pubkey = 0; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + generate_ex_key(funcs, session, &pubkey, &pvtkey); + get_attr(session, pvtkey, CKK_EC_MONTGOMERY); + find_object(session, CKK_EC_MONTGOMERY); + + destroy_test_objects(funcs, session, &pvtkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_generate_ex25519()\n"); +} + +static void test_sign_edkey() { + dprintf(0, "TEST START: test_sign_edkey()\n"); + EVP_PKEY *edkey; + CK_OBJECT_HANDLE pvtkey=0, cert=0; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + edkey = import_edkey(funcs, session, &cert, &pvtkey); + if (edkey == NULL) + exit(EXIT_FAILURE); + + + test_ed_sign_simple(funcs, session, pvtkey); + + EVP_PKEY_free(edkey); + destroy_test_objects(funcs, session, &pvtkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_sign_edkey()\n"); +} + +static void test_edkey_attributes() { + dprintf(0, "TEST START: test_edkey_attributes()\n"); + + CK_OBJECT_HANDLE privkey, pubkey; + CK_SESSION_HANDLE session; + + CK_ULONG obj_class; + CK_BBOOL obj_token; + CK_BBOOL obj_private; + CK_ULONG obj_key_type; + + CK_ATTRIBUTE template[] = { + {CKA_CLASS, &obj_class, sizeof(CK_ULONG)}, + {CKA_TOKEN, &obj_token, sizeof(CK_BBOOL)}, + {CKA_PRIVATE, &obj_private, sizeof(CK_BBOOL)}, + {CKA_KEY_TYPE, &obj_key_type, sizeof(CK_ULONG)}, + }; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + generate_ed_key(funcs, session, &pubkey, &privkey); + asrt(funcs->C_GetAttributeValue(session, pubkey, template, 4), CKR_OK, "GET BASIC ATTRIBUTES"); + asrt(obj_class, CKO_PUBLIC_KEY, "CLASS"); + asrt(obj_token, CK_TRUE, "TOKEN"); + asrt(obj_private, CK_FALSE, "PRIVATE"); + asrt(obj_key_type, CKK_EC_EDWARDS, "KEY_TYPE"); + + asrt(funcs->C_GetAttributeValue(session, privkey, template, 4), CKR_OK, "GET BASIC ATTRIBUTES"); + asrt(obj_class, CKO_PRIVATE_KEY, "CLASS"); + asrt(obj_token, CK_TRUE, "TOKEN"); + asrt(obj_private, CK_TRUE, "PRIVATE"); + asrt(obj_key_type, CKK_EC_EDWARDS, "KEY_TYPE"); + + destroy_test_objects(funcs, session, &privkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + + dprintf(0, "TEST END: test_edkey_attributes()\n"); +} + +static void test_xkey_attributes() { + dprintf(0, "TEST START: test_xkey_attributes()\n"); + + CK_OBJECT_HANDLE privkey, pubkey; + CK_SESSION_HANDLE session; + + CK_ULONG obj_class; + CK_BBOOL obj_token; + CK_BBOOL obj_private; + CK_ULONG obj_key_type; + + CK_ATTRIBUTE template[] = { + {CKA_CLASS, &obj_class, sizeof(CK_ULONG)}, + {CKA_TOKEN, &obj_token, sizeof(CK_BBOOL)}, + {CKA_PRIVATE, &obj_private, sizeof(CK_BBOOL)}, + {CKA_KEY_TYPE, &obj_key_type, sizeof(CK_ULONG)}, + }; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + generate_ex_key(funcs, session, &pubkey, &privkey); + + asrt(funcs->C_GetAttributeValue(session, pubkey, template, 4), CKR_OK, "GET BASIC ATTRIBUTES"); + asrt(obj_class, CKO_PUBLIC_KEY, "CLASS"); + asrt(obj_token, CK_TRUE, "TOKEN"); + asrt(obj_private, CK_FALSE, "PRIVATE"); + asrt(obj_key_type, CKK_EC_MONTGOMERY, "KEY_TYPE"); + + asrt(funcs->C_GetAttributeValue(session, privkey, template, 4), CKR_OK, "GET BASIC ATTRIBUTES"); + asrt(obj_class, CKO_PRIVATE_KEY, "CLASS"); + asrt(obj_token, CK_TRUE, "TOKEN"); + asrt(obj_private, CK_TRUE, "PRIVATE"); + asrt(obj_key_type, CKK_EC_MONTGOMERY, "KEY_TYPE"); + + destroy_test_objects(funcs, session, &privkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + + dprintf(0, "TEST END: test_xkey_attributes()\n"); +} + +static void test_find_objects() { + dprintf(0, "TEST START: test_find_objects()\n"); + + CK_BYTE i; + CK_OBJECT_HANDLE privkey, pubkey; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + generate_ed_key(funcs, session, &pubkey, &privkey); + + asrt(funcs->C_Login(session, CKU_USER, "123456", 6), CKR_OK, "LOGIN USER"); + + CK_OBJECT_HANDLE found_obj[10] = {0}; + CK_ULONG n_found_obj = 0; + CK_ULONG object_class; + CK_BYTE ckaid = 0; + CK_BYTE n_privkey_obj = 0; + CK_BYTE n_pubkey_obj = 0; + CK_BYTE n_cert_obj = 0; + CK_BYTE n_data_obj = 0; + + CK_ATTRIBUTE idTemplate[] = { + {CKA_ID, &ckaid, sizeof(ckaid)} + }; + CK_ATTRIBUTE classTemplate[] = { + {CKA_CLASS, &object_class, sizeof(CK_ULONG)} + }; + + asrt(funcs->C_GetAttributeValue(session, privkey, idTemplate, 1), CKR_OK, "GET CKA_ID"); + asrt(funcs->C_FindObjectsInit(session, idTemplate, 1), CKR_OK, "FIND INIT"); + asrt(funcs->C_FindObjects(session, found_obj, 10, &n_found_obj), CKR_OK, "FIND"); + asrt(n_found_obj, 5, "N FOUND OBJS"); + asrt(funcs->C_FindObjectsFinal(session), CKR_OK, "FIND FINAL"); + + for(i=0; iC_GetAttributeValue(session, found_obj[i], classTemplate, 1), CKR_OK, "GET CKA_CLASS"); + if(object_class == CKO_PRIVATE_KEY) { + n_privkey_obj++; + asrt(found_obj[i], privkey, "Wrong private key"); + } else if(object_class == CKO_PUBLIC_KEY) { + n_pubkey_obj++; + asrt(found_obj[i], pubkey, "Wrong public key"); + } else if(object_class == CKO_CERTIFICATE) { + n_cert_obj++; + } else if(object_class == CKO_DATA) { + n_data_obj++; + } + } + asrt(n_privkey_obj, 1, "NO PRIVATE KEY"); + asrt(n_pubkey_obj, 1, "NO PUBLIC KEY"); + asrt(n_data_obj, 1, "NO DATA"); + asrt(n_cert_obj, 2, "NUMBER OF CERTIFICATES"); + + test_find_objects_by_class(funcs, session, CKO_PRIVATE_KEY, ckaid, 1, 86); + test_find_objects_by_class(funcs, session, CKO_PUBLIC_KEY, ckaid, 1, 111); + test_find_objects_by_class(funcs, session, CKO_DATA, ckaid, 1, 0); + test_find_objects_by_class(funcs, session, CKO_CERTIFICATE, ckaid, 2, 37); + test_find_objects_by_class(funcs, session, CKO_CERTIFICATE, ckaid, 2, 62); + + asrt(funcs->C_Logout(session), CKR_OK, "Logout USER"); + + destroy_test_objects(funcs, session, &privkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_find_objects()\n"); +} + +static EVP_PKEY* test_import_edkey() { + dprintf(0, "TEST START: test_import_edkey()\n"); + + EVP_PKEY *edkey; + CK_OBJECT_HANDLE cert = 0, pvtkey = 0; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + edkey = import_edkey(funcs, session, &cert, &pvtkey); + + destroy_test_objects(funcs, session, &cert, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_import_edkey()\n"); + + return edkey; +} + +static void test_import_x25519key() { + dprintf(0, "TEST START: test_import_x25519key()\n"); + + CK_OBJECT_HANDLE cert = 0, pvtkey = 0; + CK_SESSION_HANDLE session; + + init_connection(); + asrt(funcs->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session), CKR_OK, "OpenSession1"); + + import_x25519key(funcs, session, &cert, &pvtkey); + + destroy_test_objects(funcs, session, &pvtkey, 1); + asrt(funcs->C_CloseSession(session), CKR_OK, "CloseSession"); + asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); + dprintf(0, "TEST END: test_import_x25519key()\n"); +} + +#endif + +#if HW_TESTS +static int destruction_confirmed(void) { +#ifdef _WIN32 + return 1; +#else + return system("../../../tools/confirm.sh") == 0; +#endif +} +#endif + +int main(void) { + + get_functions(); + +#if HW_TESTS + +// test_initalize(); + // Require firmware version 5.7 or higher. Skip if earlier. + if (!has_hardware_support()) { + fprintf(stdout, "Firmware version too old to support ED25519 and EX25519 keys. Skipping tests.\n"); + exit(77); + } + + // Require user confirmation to continue, since this test suite will clear + // any data stored on connected keys. + if (!destruction_confirmed()) { + dprintf(0, "\n***\n*** Hardware tests skipped.\n***\n\n"); + exit(77); // exit code 77 == skipped tests + } + + test_mechanism_list_and_info(); + test_generate_ed(); + test_generate_ex(); + test_import_edkey(); + test_import_x25519key(); + test_sign_edkey(); + test_edkey_attributes(); + test_xkey_attributes(); + test_find_objects(); +#else + fprintf(stderr, "HARDWARE TESTS DISABLED!, skipping...\n"); +#endif + + return EXIT_SUCCESS; +} + +//#pragma clang diagnostic pop \ No newline at end of file diff --git a/ykcs11/tests/ykcs11_tests.c b/ykcs11/tests/ykcs11_tests.c index 09efba71..dd6df472 100644 --- a/ykcs11/tests/ykcs11_tests.c +++ b/ykcs11/tests/ykcs11_tests.c @@ -57,6 +57,7 @@ CK_FUNCTION_LIST_3_0_PTR funcs; #define asrt(c, e, m) _asrt(__FILE__, __LINE__, c, e, m); CK_BBOOL is_neo = CK_FALSE; +CK_BBOOL ed25519_supported = CK_FALSE; static void _asrt(const char *file, int line, CK_ULONG check, CK_ULONG expected, const char *msg) { @@ -157,6 +158,10 @@ static int test_token_info() { asrt(info.hardwareVersion.major, HW.major, "HW_MAJ"); asrt(info.hardwareVersion.minor, HW.minor, "HW_MIN"); + + if(info.firmwareVersion.major > 5 || (info.firmwareVersion.major == 5 && info.firmwareVersion.minor >= 70)) { + ed25519_supported = true; + } asrt(strncmp(info.utcTime, TOKEN_TIME, sizeof(info.utcTime)), 0, "TOKEN_TIME"); @@ -198,9 +203,11 @@ static void test_mechanism_list_and_info() { CKM_SHA_1, CKM_SHA256, CKM_SHA384, - CKM_SHA512}; + CKM_SHA512, + CKM_EC_EDWARDS_KEY_PAIR_GEN, + CKM_EC_MONTGOMERY_KEY_PAIR_GEN}; - static const CK_MECHANISM_INFO token_mechanism_infos[] = { // KEEP ALIGNED WITH token_mechanisms + static const CK_MECHANISM_INFO token_mechanism_infos_3[] = { // KEEP ALIGNED WITH token_mechanisms {1024, 4096, CKF_HW | CKF_GENERATE_KEY_PAIR}, {1024, 4096, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY}, {1024, 4096, CKF_HW | CKF_SIGN | CKF_VERIFY}, @@ -225,20 +232,56 @@ static void test_mechanism_list_and_info() { {0, 0, CKF_DIGEST}, {0, 0, CKF_DIGEST}, {0, 0, CKF_DIGEST}, - {0, 0, CKF_DIGEST} + {0, 0, CKF_DIGEST}, + {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS} }; + static const CK_MECHANISM_INFO token_mechanism_infos[] = { // KEEP ALIGNED WITH token_mechanisms + {1024, 2048, CKF_HW | CKF_GENERATE_KEY_PAIR}, + {1024, 2048, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT}, + {1024, 2048, CKF_HW | CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {1024, 2048, CKF_HW | CKF_SIGN | CKF_VERIFY}, + {256, 384, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {256, 384, CKF_HW | CKF_DERIVE | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {0, 0, CKF_DIGEST}, + {0, 0, CKF_DIGEST}, + {0, 0, CKF_DIGEST}, + {0, 0, CKF_DIGEST}, + {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS} + }; + init_connection(); asrt(funcs->C_GetMechanismList(0, NULL, &n_mechs), CKR_OK, "GetMechanismList"); mechs = malloc(n_mechs * sizeof(CK_MECHANISM_TYPE)); asrt(funcs->C_GetMechanismList(0, mechs, &n_mechs), CKR_OK, "GetMechanismList"); - asrt(memcmp(token_mechanisms, mechs, sizeof(token_mechanisms)), 0, "CHECK MECHS"); + asrt(memcmp(token_mechanisms, mechs, n_mechs * sizeof(CK_MECHANISM_TYPE)), 0, "CHECK MECHS"); for (i = 0; i < n_mechs; i++) { asrt(funcs->C_GetMechanismInfo(0, mechs[i], &info), CKR_OK, "GET MECH INFO"); - asrt(memcmp(token_mechanism_infos + i, &info, sizeof(CK_MECHANISM_INFO)), 0, "CHECK MECH INFO"); + if(ed25519_supported) { + asrt(memcmp(token_mechanism_infos_3 + i, &info, sizeof(CK_MECHANISM_INFO)), 0, "CHECK MECH INFO"); + } else { + asrt(memcmp(token_mechanism_infos + i, &info, sizeof(CK_MECHANISM_INFO)), 0, "CHECK MECH INFO"); + } } free(mechs); asrt(funcs->C_Finalize(NULL), CKR_OK, "FINALIZE"); diff --git a/ykcs11/tests/ykcs11_tests_util.c b/ykcs11/tests/ykcs11_tests_util.c index c42c7a8b..0937af22 100644 --- a/ykcs11/tests/ykcs11_tests_util.c +++ b/ykcs11/tests/ykcs11_tests_util.c @@ -297,6 +297,111 @@ void test_digest_func(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, } } +EVP_PKEY* import_edkey(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_cert, + CK_OBJECT_HANDLE_PTR obj_pvtkey) { + + CK_BYTE params[] = {0x13, 0x0c, 0x65, 0x64, 0x77, 0x61, 0x72, 0x64, 0x73, 0x32, 0x35, 0x35, 0x31, 0x39}; + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG class_c = CKO_CERTIFICATE; + CK_ULONG kt = CKK_EC_EDWARDS; + CK_BYTE id = 1; + CK_BYTE value_c[255] = {0}; + CK_CHAR pvt[255] = {0}; + CK_ULONG pvt_len = sizeof(pvt); + + + CK_ATTRIBUTE privateKeyTemplate[] = { + {CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)}, + {CKA_EC_PARAMS, params, sizeof(params)}, + {CKA_VALUE, pvt, pvt_len} + }; + + CK_ATTRIBUTE certTemplate[] = { + {CKA_CLASS, &class_c, sizeof(class_c)}, + {CKA_ID, &id, sizeof(id)}, + {CKA_VALUE, value_c, sizeof(value_c)} + }; + + EVP_PKEY *ed_key = NULL; + EVP_PKEY_CTX *ed_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL); + EVP_PKEY_keygen_init(ed_ctx); + EVP_PKEY_keygen(ed_ctx, &ed_key); + EVP_PKEY_CTX_free(ed_ctx); + asrt(EVP_PKEY_get_raw_private_key(ed_key, pvt, &pvt_len), 1, "EXTRACTING PRIVATE ED25519 KEY"); + privateKeyTemplate[4].ulValueLen = pvt_len; + + X509 *cert = X509_new(); + X509_set_version(cert, 2); // Version 3 + X509_NAME_add_entry_by_txt(X509_get_issuer_name(cert), "CN", MBSTRING_ASC, (unsigned char*)"Test Issuer", -1, -1, 0); + X509_NAME_add_entry_by_txt(X509_get_subject_name(cert), "CN", MBSTRING_ASC, (unsigned char*)"Test Subject", -1, -1, 0); + ASN1_INTEGER_set(X509_get_serialNumber(cert), 0); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), 0); + + if (X509_set_pubkey(cert, ed_key) == 0) { + exit(EXIT_FAILURE); + } + + if (X509_sign(cert, ed_key, NULL) == 0) { + exit(EXIT_FAILURE); + } + + CK_ULONG cert_len; + unsigned char *p = value_c; + if ((cert_len = (CK_ULONG) i2d_X509(cert, &p)) == 0 || cert_len > sizeof(value_c)) + exit(EXIT_FAILURE); + + certTemplate[2].ulValueLen = cert_len; + + asrt(funcs->C_Login(session, CKU_SO, (CK_CHAR_PTR)"010203040506070801020304050607080102030405060708", 48), CKR_OK, "Login SO"); + + asrt(funcs->C_CreateObject(session, certTemplate, 3, obj_cert), CKR_OK, "IMPORT CERT"); + asrt(*obj_cert, 37, "CERTIFICATE HANDLE"); + asrt(funcs->C_CreateObject(session, privateKeyTemplate, 5, obj_pvtkey), CKR_OK, "IMPORT KEY"); + asrt(*obj_pvtkey, 86, "PRIVATE KEY HANDLE"); + + asrt(funcs->C_Logout(session), CKR_OK, "Logout SO"); + X509_free(cert); + + return ed_key; +} + +void import_x25519key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_cert, + CK_OBJECT_HANDLE_PTR obj_pvtkey) { + + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG kt = CKK_EC_MONTGOMERY; + CK_BYTE id = 1; + CK_CHAR pvt[255] = {0}; + CK_ULONG pvt_len = sizeof(pvt); + + + CK_ATTRIBUTE privateKeyTemplate[] = { + {CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)}, + {CKA_VALUE, pvt, pvt_len} + }; + + EVP_PKEY *key = NULL; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); + EVP_PKEY_keygen_init(ctx); + EVP_PKEY_keygen(ctx, &key); + EVP_PKEY_CTX_free(ctx); + asrt(EVP_PKEY_get_raw_private_key(key, pvt, &pvt_len), 1, "EXTRACTING PRIVATE ED25519 KEY"); + privateKeyTemplate[3].ulValueLen = pvt_len; + + asrt(funcs->C_Login(session, CKU_SO, (CK_CHAR_PTR)"010203040506070801020304050607080102030405060708", 48), CKR_OK, "Login SO"); + + asrt(funcs->C_CreateObject(session, privateKeyTemplate, 4, obj_pvtkey), CKR_OK, "IMPORT KEY"); + asrt(*obj_pvtkey, 86, "PRIVATE KEY HANDLE"); + + asrt(funcs->C_Logout(session), CKR_OK, "Logout SO"); + EVP_PKEY_free(key); +} + EC_KEY* import_ec_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_BYTE n_keys, int curve, CK_ULONG key_len, CK_BYTE* ec_params, CK_ULONG ec_params_len, CK_OBJECT_HANDLE_PTR obj_cert, CK_OBJECT_HANDLE_PTR obj_pvtkey) { @@ -554,6 +659,58 @@ void import_rsa_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, i free(qinv); } +void generate_ed_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey) { + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG class_c = CKO_PUBLIC_KEY; + CK_ULONG kt = CKK_EC_EDWARDS; + CK_BYTE id = 1; + + CK_ATTRIBUTE privateKeyTemplate[] = { + {CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)} + }; + + CK_ATTRIBUTE publicKeyTemplate[] = { + {CKA_CLASS, &class_c, sizeof(class_c)}, + {CKA_ID, &id, sizeof(id)} + }; + + CK_MECHANISM mech = {CKM_EC_EDWARDS_KEY_PAIR_GEN, NULL, 0}; + + asrt(funcs->C_Login(session, CKU_SO, (CK_CHAR_PTR)"010203040506070801020304050607080102030405060708", 48), CKR_OK, "Login SO"); + + asrt(funcs->C_GenerateKeyPair(session, &mech, publicKeyTemplate, 2, privateKeyTemplate, 3, obj_pubkey, obj_pvtkey), CKR_OK, "GEN ED25519 KEYPAIR"); + asrt(funcs->C_Logout(session), CKR_OK, "Logout SO"); +} + +void generate_ex_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey) { + CK_ULONG class_k = CKO_PRIVATE_KEY; + CK_ULONG class_c = CKO_PUBLIC_KEY; + CK_ULONG kt = CKK_EC_MONTGOMERY; + CK_BYTE id = 2; + + CK_ATTRIBUTE privateKeyTemplate[] = { + {CKA_CLASS, &class_k, sizeof(class_k)}, + {CKA_KEY_TYPE, &kt, sizeof(kt)}, + {CKA_ID, &id, sizeof(id)} + }; + + CK_ATTRIBUTE publicKeyTemplate[] = { + {CKA_CLASS, &class_c, sizeof(class_c)}, + {CKA_ID, &id, sizeof(id)} + }; + + CK_MECHANISM mech = {CKM_EC_MONTGOMERY_KEY_PAIR_GEN, NULL, 0}; + + asrt(funcs->C_Login(session, CKU_SO, (CK_CHAR_PTR)"010203040506070801020304050607080102030405060708", 48), CKR_OK, "Login SO"); + + asrt(funcs->C_GenerateKeyPair(session, &mech, publicKeyTemplate, 2, privateKeyTemplate, 3, obj_pubkey, obj_pvtkey), CKR_OK, "GEN ED25519 KEYPAIR"); + asrt(funcs->C_Logout(session), CKR_OK, "Logout SO"); +} + void generate_ec_keys(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_BYTE n_keys, CK_BYTE* ec_params, CK_ULONG ec_params_len, CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey) { @@ -782,6 +939,27 @@ void test_ec_sign_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE sessi asrt(funcs->C_Logout(session), CKR_OK, "Logout USER"); } +void test_ed_sign_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE pvtkey) { + + CK_MECHANISM mech = {CKM_EDDSA, NULL, 0}; + + asrt(funcs->C_Login(session, CKU_USER, (CK_CHAR_PTR) "123456", 6), CKR_OK, "Login USER"); + + CK_BYTE data[32] = {0}; + CK_ULONG data_len = sizeof(data); + if (RAND_bytes(data, data_len) <= 0) + exit(EXIT_FAILURE); + + asrt(funcs->C_SignInit(session, &mech, pvtkey), CKR_OK, "SignInit"); + asrt(funcs->C_Login(session, CKU_CONTEXT_SPECIFIC, (CK_CHAR_PTR)"123456", 6), CKR_OK, "Re-Login USER"); + CK_BYTE sig[256] = {0}; + CK_ULONG sig_len = sizeof(sig); + asrt(funcs->C_Sign(session, data, sizeof(data), sig, &sig_len), CKR_OK, "Sign"); + asrt(funcs->C_VerifyInit(session, &mech, get_public_key_handle(funcs, session, pvtkey)), CKR_OK, "VerifyInit"); + asrt(funcs->C_Verify(session, data, sizeof(data), sig, sig_len), CKR_OK, "Verify"); + asrt(funcs->C_Logout(session), CKR_OK, "Logout USER"); +} + void test_ec_ecdh_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_pvtkey, CK_BYTE n_keys, int curve) { diff --git a/ykcs11/tests/ykcs11_tests_util.h b/ykcs11/tests/ykcs11_tests_util.h index d3140d21..c84366ad 100644 --- a/ykcs11/tests/ykcs11_tests_util.h +++ b/ykcs11/tests/ykcs11_tests_util.h @@ -35,9 +35,20 @@ void test_digest_func(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, void destroy_test_objects(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_cert, CK_ULONG n); -EC_KEY* import_ec_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_BYTE n_keys, - int curve, CK_ULONG key_len, CK_BYTE* ec_params, CK_ULONG ec_params_len, - CK_OBJECT_HANDLE_PTR obj_cert, CK_OBJECT_HANDLE_PTR obj_pvtkey); + +EVP_PKEY* import_edkey(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_cert, + CK_OBJECT_HANDLE_PTR obj_pvtkey); + +void import_x25519key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_cert, + CK_OBJECT_HANDLE_PTR obj_pvtkey); + +EC_KEY* import_ec_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_BYTE n_keys, int curve, CK_ULONG key_len, + CK_BYTE* ec_params, CK_ULONG ec_params_len, CK_OBJECT_HANDLE_PTR obj_cert, CK_OBJECT_HANDLE_PTR obj_pvtkey); + +void generate_ed_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey); +void generate_ex_key(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey); void generate_ec_keys(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_BYTE n_keys, CK_BYTE* ec_params, CK_ULONG ec_params_len, CK_OBJECT_HANDLE_PTR obj_pubkey, CK_OBJECT_HANDLE_PTR obj_pvtkey); @@ -59,6 +70,8 @@ void test_ec_sign_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE sessi void test_ec_ecdh_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_pvtkey, CK_BYTE n_keys, int curve); +void test_ed_sign_simple(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj_pvtkey); + void test_ec_sign_thorough(CK_FUNCTION_LIST_3_0_PTR funcs, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR obj_pvtkey, CK_MECHANISM_TYPE mech_type, EC_KEY *eck, CK_ULONG key_len); diff --git a/ykcs11/token.c b/ykcs11/token.c index 58ae76f2..2633906a 100644 --- a/ykcs11/token.c +++ b/ykcs11/token.c @@ -71,7 +71,9 @@ static const token_mechanism token_mechanisms[] = { CKM_SHA_1, {0, 0, CKF_DIGEST}, CKM_SHA256, {0, 0, CKF_DIGEST}, CKM_SHA384, {0, 0, CKF_DIGEST}, - CKM_SHA512, {0, 0, CKF_DIGEST} + CKM_SHA512, {0, 0, CKF_DIGEST}, + CKM_EC_EDWARDS_KEY_PAIR_GEN, {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS}, + CKM_EC_MONTGOMERY_KEY_PAIR_GEN, {255, 255, CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS} }; // The commented out objects below are either not supported (PIV_DATA_OBJ_BITGT) or requires authentication. @@ -398,6 +400,14 @@ CK_RV token_generate_key(ykpiv_state *state, gen_info_t *gen, CK_BYTE key, CK_BY case YKPIV_ALGO_ECCP384: break; + case YKPIV_ALGO_ED25519: + case YKPIV_ALGO_X25519: + if (!is_version_compatible(state, 5, 7, 0)) { + DBG("ED25519 and X25519 key types are only available with YubiKeys with version number 5.7.0 or later"); + return CKR_FUNCTION_NOT_SUPPORTED; + } + break; + default: return CKR_FUNCTION_FAILED; } diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index 17b63eda..14308f5f 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -1532,22 +1532,19 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)( DBG("Importing private key"); // Try to parse the key as EC - rv = check_create_ec_key(pTemplate, ulCount, &id, + if (check_create_ec_key(pTemplate, ulCount, &id, &ec_data, &ec_data_len, - &touch_policy, &pin_policy); - if (rv == CKR_OK) { + &touch_policy, &pin_policy) == CKR_OK) { DBG("Key is ECDSA"); algorithm = ec_data_len <= 32 ? YKPIV_ALGO_ECCP256 : YKPIV_ALGO_ECCP384; - } else { + } else if (check_create_rsa_key(pTemplate, ulCount, &id, + &p, &p_len, + &q, &q_len, + &dp, &dp_len, + &dq, &dq_len, + &qinv, &qinv_len, + &touch_policy, &pin_policy) == CKR_OK) { // Try to parse the key as RSA - rv = check_create_rsa_key(pTemplate, ulCount, &id, - &p, &p_len, - &q, &q_len, - &dp, &dp_len, - &dq, &dq_len, - &qinv, &qinv_len, - &touch_policy, &pin_policy); - if (rv == CKR_OK) { DBG("Key is RSA"); switch (p_len) { case 63: @@ -1567,10 +1564,17 @@ CK_DEFINE_FUNCTION(CK_RV, C_CreateObject)( algorithm = YKPIV_ALGO_RSA4096; break; } - } else { - DBG("Private key template not valid"); - goto create_out; - } + } else if (check_create_ed_key(pTemplate, ulCount, &id, + &ec_data, &ec_data_len, + &touch_policy, &pin_policy) == CKR_OK) { + DBG("Key is ED25519"); + algorithm = YKPIV_ALGO_ED25519; + + } else if (check_create_x25519_key(pTemplate, ulCount, &id, + &ec_data, &ec_data_len, + &touch_policy, &pin_policy) == CKR_OK) { + DBG("Key is X25519"); + algorithm = YKPIV_ALGO_X25519; } DBG("Key id is %u", id);