From 5bef13acece5ef4462e1573cb4930183e18c257b Mon Sep 17 00:00:00 2001 From: Alessio Di Mauro Date: Fri, 7 Aug 2015 17:09:09 +0200 Subject: [PATCH] First stab at key generation. --- ykcs11/debug.h | 6 +- ykcs11/mechanisms.c | 163 ++++++++++++++++++++++++++++++++++++++++- ykcs11/mechanisms.h | 4 + ykcs11/objects.c | 130 ++++++++++++++++---------------- ykcs11/openssl_utils.c | 1 + ykcs11/token_vendors.c | 45 ++++++++++++ ykcs11/token_vendors.h | 6 ++ ykcs11/ykcs11.c | 161 +++++++++++++++++++++++++++++++--------- ykcs11/ykcs11.h | 12 ++- ykcs11/yubico_token.c | 10 +-- 10 files changed, 428 insertions(+), 110 deletions(-) diff --git a/ykcs11/debug.h b/ykcs11/debug.h index 946fc25..a89f49c 100644 --- a/ykcs11/debug.h +++ b/ykcs11/debug.h @@ -1,15 +1,15 @@ #ifndef DEBUG_H #define DEBUG_H +#define YKCS11_DBG 1 // General debug, must be either 1 or 0 +#define YKCS11_DINOUT 0 // Function in/out debug, must be either 1 or 0 + #define D(x) do { \ printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \ printf x; \ printf ("\n"); \ } while (0) -#define YKCS11_DBG 0 // General debug, must be either 1 or 0 -#define YKCS11_DINOUT 0 // Function in/out debug, must be either 1 or 0 - #if YKCS11_DBG #include #define DBG(x) D(x); diff --git a/ykcs11/mechanisms.c b/ykcs11/mechanisms.c index bec71a3..1e94f39 100644 --- a/ykcs11/mechanisms.c +++ b/ykcs11/mechanisms.c @@ -17,6 +17,13 @@ static const CK_MECHANISM_TYPE sign_mechanisms[] = { CKM_ECDSA_SHA1 }; +// 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 +}; + CK_RV check_sign_mechanism(const ykcs11_session_t *s, const CK_MECHANISM_PTR m) { CK_ULONG i; @@ -40,9 +47,9 @@ CK_RV check_sign_mechanism(const ykcs11_session_t *s, const CK_MECHANISM_PTR m) if (token.get_token_mechanism_info(m->mechanism, &info) != CKR_OK) return CKR_MECHANISM_INVALID; - // TODO: also check that parametes make sense if any? + // TODO: also check that parametes make sense if any? And key size is in [min max] - CKR_OK; + return CKR_OK; } @@ -289,3 +296,155 @@ CK_RV apply_sign_mechanism_finalize(op_info_t *op_info) { return CKR_FUNCTION_FAILED; } } + +CK_RV check_generation_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m) { + + CK_ULONG i; + CK_BBOOL supported = CK_FALSE; + token_vendor_t token; + CK_MECHANISM_INFO info; + + // Check if the mechanism is supported by the module + for (i = 0; i < sizeof(generation_mechanisms) / sizeof(CK_MECHANISM_TYPE); i++) { + if (m->mechanism == generation_mechanisms[i]) { + supported = CK_TRUE; + break; + } + } + if (supported == CK_FALSE) + return CKR_MECHANISM_INVALID; + + // Check if the mechanism is supported by the token + token = get_token_vendor(s->slot->token->vid); + + if (token.get_token_mechanism_info(m->mechanism, &info) != CKR_OK) + return CKR_MECHANISM_INVALID; + + // TODO: also check that parametes make sense if any? And key size is in [min max] + + return CKR_OK; + +} + +CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + CK_ULONG i; + CK_BBOOL rsa_mechanism; + + op_info->op.gen.rsa = is_RSA_mechanism(op_info->mechanism.mechanism); + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR) templ[i].pValue) != CKO_PUBLIC_KEY) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_KEY_TYPE: + if ((op_info->op.gen.rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || + (op_info->op.gen.rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_ECDSA)) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_PUBLIC_EXPONENT: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_MECHANISM_PARAM_INVALID; + + // Only support F4 + if (templ[i].ulValueLen != 3 || memcmp((CK_BYTE_PTR)templ[i].pValue, "\x01\x00\x01", 3) != 0) + return CKR_MECHANISM_PARAM_INVALID; + + break; + + case CKA_MODULUS_BITS: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_MECHANISM_PARAM_INVALID; + + if (*((CK_ULONG_PTR)templ[i].pValue) != 1024 && + *((CK_ULONG_PTR) templ[i].pValue) != 2048) // TODO: make define? + return CKR_MECHANISM_PARAM_INVALID; + + op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); // TODO: check length? + break; + + case CKA_ID: + // TODO: get pvt key with attributed id and store it's id into op_info + + case CKA_TOKEN: + case CKA_ENCRYPT: + case CKA_VERIFY: + case CKA_WRAP: + // Ignore these attributes for now + break; + + default: + return CKR_MECHANISM_PARAM_INVALID; + } + } + + return CKR_OK; + +} + +CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n) { + + CK_ULONG i; + CK_BBOOL rsa_mechanism; + + op_info->op.gen.rsa = is_RSA_mechanism(op_info->mechanism.mechanism); + + for (i = 0; i < n; i++) { + switch (templ[i].type) { + case CKA_CLASS: + if (*((CK_ULONG_PTR)templ[i].pValue) != CKO_PRIVATE_KEY) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_KEY_TYPE: + if ((op_info->op.gen.rsa == CK_TRUE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_RSA) || + (op_info->op.gen.rsa == CK_FALSE && (*((CK_KEY_TYPE *)templ[i].pValue)) != CKK_ECDSA)) + return CKR_TEMPLATE_INCONSISTENT; + + break; + + case CKA_PUBLIC_EXPONENT: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_MECHANISM_PARAM_INVALID; + + // Only support F4 + if (templ[i].ulValueLen != 3 || memcmp((CK_BYTE_PTR)templ[i].pValue, "\x01\x00\x01", 3) != 0) + return CKR_MECHANISM_PARAM_INVALID; + + break; + + case CKA_MODULUS_BITS: + if (op_info->op.gen.rsa == CK_FALSE) + return CKR_MECHANISM_PARAM_INVALID; + + if (*((CK_ULONG_PTR)templ[i].pValue) != 1024 && + *((CK_ULONG_PTR) templ[i].pValue) != 2048) // TODO: make define? + return CKR_MECHANISM_PARAM_INVALID; + + op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); // TODO: check length? + break; + + case CKA_SENSITIVE: + case CKA_DECRYPT: + case CKA_UNWRAP: + case CKA_SIGN: + case CKA_PRIVATE: + case CKA_TOKEN: + // Ignore these attributes for now + break; + + default: + return CKR_MECHANISM_PARAM_INVALID; + } + } + + return CKR_OK; + +} diff --git a/ykcs11/mechanisms.h b/ykcs11/mechanisms.h index 1869774..c49ca04 100644 --- a/ykcs11/mechanisms.h +++ b/ykcs11/mechanisms.h @@ -12,4 +12,8 @@ CK_RV apply_sign_mechanism_init(op_info_t *op_info); CK_RV apply_sign_mechanism_update(op_info_t *op_info, CK_BYTE_PTR in, CK_ULONG in_len); CK_RV apply_sign_mechanism_finalize(op_info_t *op_info); +CK_RV check_generation_mechanism(const ykcs11_session_t *s, CK_MECHANISM_PTR m); +CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); +CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG n); + #endif diff --git a/ykcs11/objects.c b/ykcs11/objects.c index 8f0ff96..a72c2cb 100644 --- a/ykcs11/objects.c +++ b/ykcs11/objects.c @@ -264,7 +264,7 @@ CK_RV get_doa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { switch (template->type) { case CKA_CLASS: - DBG(("CLASS\n")); + DBG(("CLASS")); len = 1; tmp[0] = CKO_DATA; data = tmp; @@ -272,51 +272,51 @@ CK_RV get_doa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_TOKEN: // Technically all these objects are token objects - DBG(("TOKEN\n")); + DBG(("TOKEN")); len = 1; tmp[0] = piv_objects[obj].token; data = tmp; break; case CKA_PRIVATE: - DBG(("PRIVATE\n")); + DBG(("PRIVATE")); len = 1; tmp[0] = piv_objects[obj].private; data = tmp; break; case CKA_LABEL: - DBG(("LABEL\n")); + DBG(("LABEL")); len = strlen(piv_objects[obj].label) + 1; data = piv_objects[obj].label; break; case CKA_APPLICATION: - DBG(("APPLICATION\n")); + DBG(("APPLICATION")); len = strlen(piv_objects[obj].label) + 1; data = piv_objects[obj].label; break; case CKA_VALUE: // TODO: this can be done with -r and -d|-a - DBG(("VALUE TODO!!!\n")); + DBG(("VALUE TODO!!!")); return CKR_FUNCTION_FAILED; case CKA_OBJECT_ID: // TODO: how about just storing the OID in DER ? - DBG(("OID\n")); + DBG(("OID")); strcpy((char *)tmp, data_objects[piv_objects[obj].sub_id].oid); asn1_encode_oid(tmp, tmp, &len); data = tmp; break; case CKA_MODIFIABLE: - DBG(("MODIFIABLE\n")); + DBG(("MODIFIABLE")); len = 1; tmp[0] = piv_objects[obj].modifiable; data = tmp; break; default: - DBG(("UNKNOWN ATTRIBUTE %lx\n", template[0].type)); + DBG(("UNKNOWN ATTRIBUTE %lx", template[0].type)); template->ulValueLen = CK_UNAVAILABLE_INFORMATION; return CKR_ATTRIBUTE_TYPE_INVALID; } @@ -347,7 +347,7 @@ CK_RV get_coa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { switch (template->type) { // TODO: is this needed here? or is it enough ot have one a "level" above? case CKA_CLASS: - DBG(("CLASS\n")); + DBG(("CLASS")); len = 1; tmp[0] = CKO_CERTIFICATE; data = tmp; @@ -355,72 +355,72 @@ CK_RV get_coa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_TOKEN: // Technically all these objects are token objects - DBG(("TOKEN\n")); + DBG(("TOKEN")); len = 1; tmp[0] = piv_objects[obj].token; data = tmp; break; case CKA_PRIVATE: - DBG(("PRIVATE\n")); + DBG(("PRIVATE")); len = 1; tmp[0] = piv_objects[obj].private; data = tmp; break; case CKA_LABEL: - DBG(("LABEL\n")); + DBG(("LABEL")); len = strlen(piv_objects[obj].label) + 1; data = piv_objects[obj].label; break; case CKA_VALUE: - DBG(("VALUE TODO\n")); + DBG(("VALUE TODO")); return CKR_FUNCTION_FAILED; case CKA_CERTIFICATE_TYPE: - DBG(("CERTIFICATE TYPE\n")); + DBG(("CERTIFICATE TYPE")); len = 1; tmp[0] = CKC_X_509; // Support only X.509 certs data = tmp; break; case CKA_ISSUER: - DBG(("ISSUER TODO\n")); // Default empty + DBG(("ISSUER TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_SERIAL_NUMBER: - DBG(("SERIAL NUMBER TODO\n")); // Default empty + DBG(("SERIAL NUMBER TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_SUBJECT: - DBG(("SUBJECT TODO\n")); // Required + DBG(("SUBJECT TODO")); // Required return CKR_FUNCTION_FAILED; case CKA_ID: - DBG(("ID\n")); + DBG(("ID")); len = 1; tmp[0] = piv_objects[obj].sub_id; data = tmp; break; case CKA_START_DATE: - DBG(("START DATE TODO\n")); // Default empty + DBG(("START DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_END_DATE: - DBG(("END DATE TODO\n")); // Default empty + DBG(("END DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_MODIFIABLE: - DBG(("MODIFIABLE\n")); + DBG(("MODIFIABLE")); len = 1; tmp[0] = piv_objects[obj].modifiable; data = tmp; break; default: // TODO: there are other attributes for a (x509) certificate - DBG(("UNKNOWN ATTRIBUTE %lx\n", template[0].type)); + DBG(("UNKNOWN ATTRIBUTE %lx", template[0].type)); template->ulValueLen = CK_UNAVAILABLE_INFORMATION; return CKR_ATTRIBUTE_TYPE_INVALID; } @@ -452,7 +452,7 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { switch (template->type) { case CKA_CLASS: - DBG(("CLASS\n")); + DBG(("CLASS")); len = sizeof(CK_ULONG); ul_tmp = CKO_PRIVATE_KEY; data = (CK_BYTE_PTR) &ul_tmp; @@ -460,27 +460,27 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_TOKEN: // Technically all these objects are token objects - DBG(("TOKEN\n")); + DBG(("TOKEN")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].token; data = b_tmp; break; case CKA_PRIVATE: - DBG(("PRIVATE\n")); + DBG(("PRIVATE")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].private; data = b_tmp; break; case CKA_LABEL: - DBG(("LABEL\n")); + DBG(("LABEL")); len = strlen(piv_objects[obj].label) + 1; data = piv_objects[obj].label; break; case CKA_KEY_TYPE: - DBG(("KEY TYPE\n")); + DBG(("KEY TYPE")); len = sizeof(CK_ULONG); ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk if (ul_tmp == CKK_VENDOR_DEFINED) @@ -489,62 +489,62 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { break; case CKA_SUBJECT: - DBG(("SUBJECT TODO\n")); // Default empty + DBG(("SUBJECT TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_ID: - DBG(("ID\n")); + DBG(("ID")); len = sizeof(CK_BYTE); ul_tmp = piv_objects[obj].sub_id; data = (CK_BYTE_PTR) &ul_tmp; break; case CKA_SENSITIVE: - DBG(("SENSITIVE TODO\n")); // Default empty + DBG(("SENSITIVE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_DECRYPT: - DBG(("DECRYPT\n")); // Default empty + DBG(("DECRYPT")); // Default empty len = sizeof(CK_BBOOL); b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].decrypt; data = b_tmp; break; case CKA_UNWRAP: - DBG(("UNWRAP\n")); // Default empty + DBG(("UNWRAP")); // Default empty len = sizeof(CK_BBOOL); b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].unwrap; data = b_tmp; break; case CKA_SIGN: - DBG(("SIGN\n")); // Default empty + DBG(("SIGN")); // Default empty len = sizeof(CK_BBOOL); b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].sign; data = b_tmp; break; case CKA_SIGN_RECOVER: - DBG(("SIGN RECOVER TODO\n")); // Default empty + DBG(("SIGN RECOVER TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_DERIVE: - DBG(("DERIVE\n")); // Default false + DBG(("DERIVE")); // Default false len = sizeof(CK_BBOOL); b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].derive; data = b_tmp; break; case CKA_START_DATE: - DBG(("START DATE TODO\n")); // Default empty + DBG(("START DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_END_DATE: - DBG(("END DATE TODO\n")); // Default empty + DBG(("END DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_MODULUS: - DBG(("MODULUS\n")); + DBG(("MODULUS")); len = sizeof(b_tmp); if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) return CKR_FUNCTION_FAILED; @@ -554,7 +554,7 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_EC_POINT: // We're trying to get the key length, get the ec point of the PUBLIC key // TODO: or just give an error and explicitly fetch the pubk len when needed - DBG(("EC_POINT\n")); + DBG(("EC_POINT")); len = sizeof(b_tmp); if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) return CKR_FUNCTION_FAILED; @@ -562,7 +562,7 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { break; case CKA_MODULUS_BITS: - DBG(("MODULUS BITS\n")); + DBG(("MODULUS BITS")); len = sizeof(CK_ULONG); ul_tmp = get_modulus_bits(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk if (ul_tmp == 0) @@ -584,21 +584,21 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { /* case CKA_VALUE_LEN: */ /* case CKA_EXTRACTABLE: */ case CKA_LOCAL: - DBG(("LOCAL TODO\n")); // Required + DBG(("LOCAL TODO")); // Required return CKR_FUNCTION_FAILED; /* case CKA_NEVER_EXTRACTABLE: */ /*case CKA_ALWAYS_SENSITIVE:*/ case CKA_ALWAYS_AUTHENTICATE: - DBG(("ALWAYS AUTHENTICATE\n")); + DBG(("ALWAYS AUTHENTICATE")); len = sizeof(CK_BBOOL); b_tmp[0] = pvtkey_objects[piv_objects[obj].sub_id].always_auth; data = b_tmp; break; case CKA_MODIFIABLE: - DBG(("MODIFIABLE\n")); + DBG(("MODIFIABLE")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].modifiable; data = b_tmp; @@ -606,7 +606,7 @@ CK_RV get_proa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { /*case CKA_VENDOR_DEFINED:*/ default: - DBG(("UNKNOWN ATTRIBUTE %lx\n", template[0].type)); // TODO: there are other parameters for public keys, plus there is more if the key is RSA + DBG(("UNKNOWN ATTRIBUTE %lx", template[0].type)); // TODO: there are other parameters for public keys, plus there is more if the key is RSA template->ulValueLen = CK_UNAVAILABLE_INFORMATION; return CKR_ATTRIBUTE_TYPE_INVALID; } @@ -638,7 +638,7 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { switch (template->type) { case CKA_CLASS: - DBG(("CLASS\n")); + DBG(("CLASS")); len = sizeof(CK_ULONG); ul_tmp = CKO_PUBLIC_KEY; data = (CK_BYTE_PTR) &ul_tmp; @@ -646,21 +646,21 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_TOKEN: // Technically all these objects are token objects - DBG(("TOKEN\n")); + DBG(("TOKEN")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].token; data = b_tmp; break; case CKA_PRIVATE: - DBG(("PRIVATE\n")); + DBG(("PRIVATE")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].private; data = b_tmp; break; case CKA_LABEL: - DBG(("LABEL\n")); + DBG(("LABEL")); len = strlen(piv_objects[obj].label) + 1; data = piv_objects[obj].label; break; @@ -668,7 +668,7 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { // case CKA_VALUE: // TODO: this can be done with -r and -d|-a case CKA_KEY_TYPE: - DBG(("KEY TYPE\n")); + DBG(("KEY TYPE")); len = sizeof(CK_ULONG); ul_tmp = get_key_type(pubkey_objects[piv_objects[obj].sub_id].data); if (ul_tmp == CKK_VENDOR_DEFINED) // This value is used as an error here @@ -677,55 +677,55 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { break; case CKA_SUBJECT: - DBG(("SUBJECT TODO\n")); // Default empty + DBG(("SUBJECT TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_ID: - DBG(("ID\n")); + DBG(("ID")); len = sizeof(CK_BYTE); b_tmp[0] = piv_objects[obj].sub_id; data = b_tmp; break; case CKA_ENCRYPT: - DBG(("ENCRYPT\n")); + DBG(("ENCRYPT")); len = sizeof(CK_BBOOL); b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].encrypt; data = b_tmp; break; case CKA_VERIFY: // TODO: what about verify recover ? - DBG(("VERIFY\n")); + DBG(("VERIFY")); len = sizeof(CK_BBOOL); b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].verify; data = b_tmp; break; case CKA_WRAP: - DBG(("WRAP\n")); + DBG(("WRAP")); len = sizeof(CK_BBOOL); b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].wrap; data = b_tmp; break; case CKA_DERIVE: - DBG(("DERIVE\n")); + DBG(("DERIVE")); len = sizeof(CK_BBOOL); b_tmp[0] = pubkey_objects[piv_objects[obj].sub_id].derive; data = b_tmp; break; case CKA_START_DATE: - DBG(("START DATE TODO\n")); // Default empty + DBG(("START DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_END_DATE: - DBG(("END DATE TODO\n")); // Default empty + DBG(("END DATE TODO")); // Default empty return CKR_FUNCTION_FAILED; case CKA_EC_POINT: // We're trying to get the key length, get the ec point of the PUBLIC key - DBG(("EC_POINT\n")); + DBG(("EC_POINT")); len = sizeof(b_tmp); if (get_public_key(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) return CKR_FUNCTION_FAILED; @@ -734,7 +734,7 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { case CKA_EC_PARAMS: // Here we want the curve parameters (DER encoded OID) - DBG(("EC_PARAMS\n")); + DBG(("EC_PARAMS")); len = sizeof(b_tmp); if (get_curve_parameters(pubkey_objects[piv_objects[obj].sub_id].data, b_tmp, &len) != CKR_OK) return CKR_FUNCTION_FAILED; @@ -742,7 +742,7 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { break; case CKA_MODULUS_BITS: - DBG(("MODULUS BITS\n")); + DBG(("MODULUS BITS")); len = sizeof(CK_ULONG); ul_tmp = get_modulus_bits(pubkey_objects[piv_objects[obj].sub_id].data); // Getting the info from the pubk if (ul_tmp == 0) @@ -751,18 +751,18 @@ CK_RV get_puoa(CK_OBJECT_HANDLE obj, CK_ATTRIBUTE_PTR template) { break; case CKA_LOCAL: - DBG(("LOCAL TODO\n")); // Required + DBG(("LOCAL TODO")); // Required return CKR_FUNCTION_FAILED; case CKA_MODIFIABLE: - DBG(("MODIFIABLE\n")); + DBG(("MODIFIABLE")); len = sizeof(CK_BBOOL); b_tmp[0] = piv_objects[obj].modifiable; data = b_tmp; break; default: - DBG(("UNKNOWN ATTRIBUTE %lx\n", template[0].type)); // TODO: there are other parameters for public keys + DBG(("UNKNOWN ATTRIBUTE %lx", template[0].type)); // TODO: there are other parameters for public keys template->ulValueLen = CK_UNAVAILABLE_INFORMATION; return CKR_ATTRIBUTE_TYPE_INVALID; } @@ -892,7 +892,7 @@ CK_RV get_available_certificate_ids(ykcs11_session_t *s, piv_obj_id_t *cert_ids, if (IS_CERT(s->slot->token->objects[i]) == CK_TRUE) cert_ids[j++] = s->slot->token->objects[i]; - DBG(("Just to check: %lu %lu\n", j, n_certs)); + DBG(("Just to check: %lu %lu", j, n_certs)); return CKR_OK; } diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index 2d8bb3f..3eac7e2 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -1,6 +1,7 @@ #include "openssl_utils.h" #include #include "../tool/util.h" // TODO: share this better? +#include "debug.h" CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert) { diff --git a/ykcs11/token_vendors.c b/ykcs11/token_vendors.c index 7fd1bfc..96045b8 100644 --- a/ykcs11/token_vendors.c +++ b/ykcs11/token_vendors.c @@ -1,6 +1,49 @@ #include "token_vendors.h" #include "yubico_token.h" +static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, CK_BYTE key, CK_ULONG key_len) { + // TODO: make a function in ykpiv for this + unsigned char in_data[5]; + unsigned char data[1024]; + unsigned char templ[] = {0, YKPIV_INS_GENERATE_ASYMMERTRIC, 0, 0}; + unsigned long recv_len = sizeof(data); + unsigned long received = 0; + int sw; + + templ[3] = key; + + in_data[0] = 0xac; + in_data[1] = 3; + in_data[2] = 0x80; + in_data[3] = 1; + + switch(key_len) { + case 2048: + in_data[4] = YKPIV_ALGO_RSA2048; + break; + + case 1024: + in_data[4] = YKPIV_ALGO_RSA1024; + break; + + case 256: + in_data[4] = YKPIV_ALGO_ECCP256; + break; + + default: + return CKR_FUNCTION_FAILED; + } + //DBG(("Generating key %x with algorithm %u and length %lu", templ[3], in_data[4], key_len)); + if(ykpiv_transfer_data(state, templ, in_data, sizeof(in_data), data, &recv_len, &sw) != YKPIV_OK || + sw != 0x9000) + return CKR_DEVICE_ERROR; + + + /* to drop the 90 00 and the 7f 49 at the start */ + received += recv_len - 4; + return CKR_OK; +} + token_vendor_t get_token_vendor(vendor_id_t vid) { token_vendor_t v; @@ -18,6 +61,7 @@ token_vendor_t get_token_vendor(vendor_id_t vid) { v.get_token_objects_num = YUBICO_get_token_objects_num; v.get_token_object_list = YUBICO_get_token_object_list; v.get_token_raw_certificate = YUBICO_get_token_raw_certificate; + v.token_generate_key = COMMON_token_generate_key; break; case UNKNOWN: @@ -34,6 +78,7 @@ token_vendor_t get_token_vendor(vendor_id_t vid) { v.get_token_objects_num = NULL; v.get_token_object_list = NULL; v.get_token_raw_certificate = NULL; + v.token_generate_key = NULL; } return v; diff --git a/ykcs11/token_vendors.h b/ykcs11/token_vendors.h index 4df53e0..b143ca8 100644 --- a/ykcs11/token_vendors.h +++ b/ykcs11/token_vendors.h @@ -18,7 +18,12 @@ typedef CK_RV (*get_t_mechanism_info_f)(CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR typedef CK_RV (*get_t_objects_num_f)(ykpiv_state *, CK_ULONG_PTR, CK_ULONG_PTR); typedef CK_RV (*get_t_object_list_f)(ykpiv_state *, piv_obj_id_t *, CK_ULONG); typedef CK_RV (*get_t_raw_certificate_f)(ykpiv_state *, piv_obj_id_t, CK_BYTE_PTR, CK_ULONG); + +// Common token functions below +typedef CK_RV (*t_generate_key_f)(ykpiv_state *, CK_BBOOL, CK_BYTE, CK_ULONG); + // TODO: replace all the common call with functions defined in .c that use libykpiv + typedef struct { get_t_label_f get_token_label; get_t_manufacturer_f get_token_manufacturer; @@ -32,6 +37,7 @@ typedef struct { get_t_objects_num_f get_token_objects_num; get_t_object_list_f get_token_object_list; get_t_raw_certificate_f get_token_raw_certificate; + t_generate_key_f token_generate_key; } token_vendor_t; token_vendor_t get_token_vendor(vendor_id_t vid); diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index 30235a6..78d881d 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -37,9 +37,6 @@ static struct { op_info_t op_info; -/*static piv_obj_id_t token_objects[PIV_CERT_OBJ_LAST]; // TODO: tidy this up, also build at runtime (during open session)? And include inside a session struct? - static CK_ULONG n_token_objects = 0;*/ - extern CK_FUNCTION_LIST function_list; // TODO: check all return values /* General Purpose */ @@ -489,14 +486,14 @@ CK_DEFINE_FUNCTION(CK_RV, C_OpenSession)( return CKR_HOST_MEMORY; } - // Save a list of all the available objects in the token // TODO: change behavior based on login status + // Save a list of all the available objects in the token rv = token.get_token_object_list(piv_state, session.slot->token->objects, session.slot->token->n_objects); if (rv != CKR_OK) { DBG(("Unable to retrieve token objects")); goto failure; } - // Get a list object ids for available certificates object from the session + // Get a list of object ids for available certificates object from the session rv = get_available_certificate_ids(&session, cert_ids, session.slot->token->n_certs); // TODO: better to get this from token? how? if (rv != CKR_OK) { DBG(("Unable to retrieve certificate ids from the session")); @@ -537,7 +534,7 @@ failure: cert_ids = NULL; } - free_certs(); // TODO + free_certs(); // TODO: remove the one allocated so far return rv; } @@ -659,14 +656,10 @@ CK_DEFINE_FUNCTION(CK_RV, C_Login)( return CKR_CRYPTOKI_NOT_INITIALIZED; } - if (userType != CKU_USER && - userType != CKU_SO && + if (userType != CKU_SO && // TODO: what can SO do? + userType != CKU_USER && userType != CKU_CONTEXT_SPECIFIC) - return CKR_ARGUMENTS_BAD; - - if (ulPinLen < PIV_MIN_PIN_LEN || - ulPinLen > PIV_MAX_PIN_LEN) - return CKR_ARGUMENTS_BAD; + return CKR_USER_TYPE_INVALID; DBG(("user %lu, pin %s, pinlen %lu", userType, pPin, ulPinLen)); @@ -678,11 +671,6 @@ CK_DEFINE_FUNCTION(CK_RV, C_Login)( if (hSession != session.handle) return CKR_SESSION_HANDLE_INVALID; - if (userType != CKU_SO && // TODO: what can SO do? - userType != CKU_USER && - userType != CKU_CONTEXT_SPECIFIC) - return CKR_USER_TYPE_INVALID; - if ((session.info.flags & CKF_RW_SESSION) == 0) { // TODO: make macros for these? DBG(("Tried to log-in to a read-only session")); return CKR_SESSION_READ_ONLY_EXISTS; @@ -690,17 +678,53 @@ CK_DEFINE_FUNCTION(CK_RV, C_Login)( switch (userType) { case CKU_USER: - if (session.info.state == CKS_RW_USER_FUNCTIONS) + if (ulPinLen < PIV_MIN_PIN_LEN || ulPinLen > PIV_MAX_PIN_LEN) + return CKR_ARGUMENTS_BAD; + + if (session.info.state == CKS_RW_USER_FUNCTIONS) // TODO: make sure to set session default state as not logged return CKR_USER_ALREADY_LOGGED_IN; + if (session.info.state == CKS_RW_SO_FUNCTIONS) + return CKR_USER_ANOTHER_ALREADY_LOGGED_IN; + tries = 0; if (ykpiv_verify(piv_state, pPin, (int *)&tries) != YKPIV_OK) { // TODO: call this from vendors.c DBG(("You loose! %lu", tries)); return CKR_PIN_INCORRECT; } + + if ((session.info.flags & CKF_RW_SESSION) == 0) // TODO: double check with the if line 678 for R/O + session.info.state = CKS_RO_USER_FUNCTIONS; + else + session.info.state = CKS_RW_USER_FUNCTIONS; break; case CKU_SO: + // TODO: check for mgmt key length? + if (session.info.state == CKS_RW_SO_FUNCTIONS) + return CKR_USER_ALREADY_LOGGED_IN; + + if (session.info.state == CKS_RO_USER_FUNCTIONS || + session.info.state == CKS_RW_USER_FUNCTIONS) + return CKR_USER_ANOTHER_ALREADY_LOGGED_IN; + + /***** TODO: replace this with a token function *****/ + unsigned char key[24]; + size_t key_len = sizeof(key); + if(ykpiv_hex_decode(pPin, ulPinLen, key, &key_len) != YKPIV_OK) { + DBG(("Failed decoding key")); + return CKR_FUNCTION_FAILED; + } + + if(ykpiv_authenticate(piv_state, key) != YKPIV_OK) { + DBG(("Failed to authenticate")); + return CKR_PIN_INCORRECT; + } + /***************************************************/ + + session.info.state = CKS_RW_SO_FUNCTIONS; + break; + case CKU_CONTEXT_SPECIFIC: default: return CKR_USER_TYPE_INVALID; // TODO: only allow regular user for now @@ -708,11 +732,6 @@ CK_DEFINE_FUNCTION(CK_RV, C_Login)( DBG(("You win! %lu", tries)); - if ((session.info.flags & CKF_RW_SESSION) == 0) - session.info.state = CKS_RO_USER_FUNCTIONS; - else - session.info.state = CKS_RW_USER_FUNCTIONS; - DOUT; return CKR_OK; } @@ -804,8 +823,8 @@ CK_DEFINE_FUNCTION(CK_RV, C_GetAttributeValue)( if (pTemplate == NULL_PTR || ulCount == 0) return CKR_ARGUMENTS_BAD; - if (find_obj.active != CK_TRUE) - return CKR_OPERATION_NOT_INITIALIZED; + /*if (find_obj.active != CK_TRUE) + return CKR_OPERATION_NOT_INITIALIZED; actually this can be called from many other functions*/ rv_final = CKR_OK; for (i = 0; i < ulCount; i++) { @@ -1205,6 +1224,11 @@ CK_DEFINE_FUNCTION(CK_RV, C_SignInit)( if (hSession != session.handle) return CKR_SESSION_HANDLE_INVALID; + if (op_info.type != YKCS11_NOOP) { + DBG(("Other operation in process")); + return CKR_OPERATION_ACTIVE; + } + if (pMechanism == NULL_PTR || hKey == NULL_PTR) return CKR_ARGUMENTS_BAD; @@ -1213,7 +1237,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_SignInit)( // Check if mechanism is supported if (check_sign_mechanism(&session, pMechanism) != CKR_OK) { - DBG(("Mechanism %lu is not supported either by the token or the slot", pMechanism->mechanism)); + DBG(("Mechanism %lu is not supported either by the token or the module", pMechanism->mechanism)); return CKR_MECHANISM_INVALID; // TODO: also the key has a list of allowed mechanisms, check that } memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); @@ -1282,7 +1306,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_SignInit)( DBG(("Key length is %lu bit", op_info.op.sign.key_len)); - op_info.op.sign.key_id = piv_2_ykpiv(hKey); + op_info.op.sign.key_id = piv_2_ykpiv(hKey); // TODO: have to set this!!! if (op_info.op.sign.key_id == 0) { DBG(("Incorrect key %lu", hKey)); return CKR_KEY_HANDLE_INVALID; @@ -1319,11 +1343,10 @@ CK_DEFINE_FUNCTION(CK_RV, C_Sign)( { DIN; - if (op_info.type == YKCS11_NOOP) + if (op_info.type != YKCS11_SIGN) { + DBG(("Signature operation not initialized")); return CKR_OPERATION_NOT_INITIALIZED; - - if (op_info.type != YKCS11_SIGN) - return CKR_OPERATION_ACTIVE; + } // TODO: check other conditions ykpiv_rc r; // TODO: delete this @@ -1564,7 +1587,79 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( ) { DIN; - DBG(("TODO!!!")); + CK_RV rv; + token_vendor_t token; + + if (piv_state == NULL) { + DBG(("libykpiv is not initialized or already finalized")); + return CKR_CRYPTOKI_NOT_INITIALIZED; + } + + if (session.handle != YKCS11_SESSION_ID) { + DBG(("Session is not open")); + return CKR_SESSION_CLOSED; + } + + if (hSession != session.handle) + return CKR_SESSION_HANDLE_INVALID; + + if (session.info.state != CKS_RW_SO_FUNCTIONS) { + DBG(("Authentication required to generate keys")); + return CKR_SESSION_READ_ONLY; + } + + if (op_info.type != YKCS11_NOOP) { + DBG(("Other operation in process")); + return CKR_OPERATION_ACTIVE; + } + + if (pMechanism == NULL_PTR || + pPublicKeyTemplate == NULL_PTR || + pPrivateKeyTemplate == NULL_PTR || + phPublicKey == NULL_PTR || + phPrivateKey == NULL_PTR) + return CKR_ARGUMENTS_BAD; + + DBG(("Trying to generate a key pair with mechanism %lu", pMechanism->mechanism)); + + DBG(("Found %lu attributes for the public key and %lu attributes for the private key", ulPublicKeyAttributeCount, ulPrivateKeyAttributeCount)); + + // Check if mechanism is supported + if ((rv = check_generation_mechanism(&session, pMechanism)) != CKR_OK) { + DBG(("Mechanism %lu is not supported either by the token or the module", pMechanism->mechanism)); + return rv; + } + memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); + + // Check the template for the public key + if ((rv = check_pubkey_template(&op_info, pPublicKeyTemplate, ulPublicKeyAttributeCount)) != CKR_OK) { + DBG(("Invalid public key template")); + return rv; + } + + // Check the template for the public key + if ((rv = check_pvtkey_template(&op_info, pPrivateKeyTemplate, ulPrivateKeyAttributeCount)) != CKR_OK) { + DBG(("Invalid private key template")); + return rv; + } + + if (op_info.op.gen.key_len == 0) { + op_info.op.gen.key_len = 1024; // TODO: for testing purpose set it to rsa2048; + } + + if (op_info.op.gen.key_id == 0) { + op_info.op.gen.key_id = PIV_PVTK_OBJ_KM; // TODO: set default key or error? + } + + token = get_token_vendor(session.slot->token->vid); + + if ((rv = token.token_generate_key(piv_state, op_info.op.gen.rsa, piv_2_ykpiv(op_info.op.gen.key_id), op_info.op.gen.key_len)) != CKR_OK) { + DBG(("Unable to generate key pair")); + return rv; + } + + // TODO: save return object handlers + DOUT; return CKR_OK; } diff --git a/ykcs11/ykcs11.h b/ykcs11/ykcs11.h index 5482b3c..96d3193 100644 --- a/ykcs11/ykcs11.h +++ b/ykcs11/ykcs11.h @@ -31,16 +31,23 @@ typedef struct { typedef enum { YKCS11_NOOP, + YKCS11_GEN, YKCS11_SIGN, YKCS11_HASH, YKCS11_DECRYPT } ykcs11_op_type_t; +typedef struct { + CK_BBOOL rsa; // RSA or EC key + CK_BYTE key_id; // Key id + CK_ULONG key_len; // Length in bits +} gen_info_t; + typedef struct { ykcs11_md_ctx_t *md_ctx; // Digest context CK_BYTE_PTR key; // Raw public key (needed for PSS) - CK_BYTE algo; // Algo for ykpiv - CK_ULONG key_id; // Key id for ykpiv + CK_BYTE algo; // Algo for ykpiv // TODO: infer this from the key length? + CK_ULONG key_id; // Key id for ykpiv // TODO: make this a ULONG and store the id {0, 1, 2, 3} CK_ULONG key_len; // Length in bits } sign_info_t; @@ -53,6 +60,7 @@ typedef struct { } decrypt_info_t; typedef union { + gen_info_t gen; sign_info_t sign; hash_info_t hash; decrypt_info_t decrypt; diff --git a/ykcs11/yubico_token.c b/ykcs11/yubico_token.c index fc09edc..93671e0 100644 --- a/ykcs11/yubico_token.c +++ b/ykcs11/yubico_token.c @@ -233,7 +233,7 @@ static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only, pvtkeys[n_cert] = PIV_PVTK_OBJ_PIV_AUTH; pubkeys[n_cert] = PIV_PUBK_OBJ_PIV_AUTH; n_cert++; - DBG(("Found AUTH cert (9a)\n")); + DBG(("Found AUTH cert (9a)")); } buf_len = sizeof(buf); @@ -242,7 +242,7 @@ static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only, pvtkeys[n_cert] = PIV_PVTK_OBJ_CARD_AUTH; pubkeys[n_cert] = PIV_PUBK_OBJ_CARD_AUTH; n_cert++; - DBG(("Found CARD AUTH cert (9e)\n")); + DBG(("Found CARD AUTH cert (9e)")); } buf_len = sizeof(buf); @@ -251,7 +251,7 @@ static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only, pvtkeys[n_cert] = PIV_PVTK_OBJ_DS; pubkeys[n_cert] = PIV_PUBK_OBJ_DS; n_cert++; - DBG(("Found SIGNATURE cert (9c)\n")); + DBG(("Found SIGNATURE cert (9c)")); } buf_len = sizeof(buf); @@ -260,10 +260,10 @@ static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only, pvtkeys[n_cert] = PIV_PVTK_OBJ_KM; pubkeys[n_cert] = PIV_PUBK_OBJ_KM; n_cert++; - DBG(("Found KMK cert (9d)\n")); + DBG(("Found KMK cert (9d)")); } - DBG(("The total number of objects for this token is %lu\n", (n_cert * 3) + token_objects_num)); + DBG(("The total number of objects for this token is %lu", (n_cert * 3) + token_objects_num)); if (num_only == CK_TRUE) { // We just want the number of objects