diff --git a/ykcs11/debug.h b/ykcs11/debug.h index a89f49c..5debc5d 100644 --- a/ykcs11/debug.h +++ b/ykcs11/debug.h @@ -1,7 +1,7 @@ #ifndef DEBUG_H #define DEBUG_H -#define YKCS11_DBG 1 // General debug, must be either 1 or 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 #define D(x) do { \ diff --git a/ykcs11/mechanisms.c b/ykcs11/mechanisms.c index 1e94f39..0012622 100644 --- a/ykcs11/mechanisms.c +++ b/ykcs11/mechanisms.c @@ -1,5 +1,8 @@ #include "mechanisms.h" +#define F4 "\x01\x00\x01" +#define PRIME256V1 "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07" + // Supported mechanisms for signature static const CK_MECHANISM_TYPE sign_mechanisms[] = { CKM_RSA_PKCS, @@ -264,7 +267,7 @@ CK_RV apply_sign_mechanism_finalize(op_info_t *op_info) { rv = do_md_finalize(op_info->op.sign.md_ctx, op_info->buf, &op_info->buf_len, &nid); if (rv != CKR_OK) return CKR_FUNCTION_FAILED; - DBG(("The hashed value is %lu long and looks like\n", op_info->buf_len)); +// DBG(("The hashed value is %lu long and looks like\n", op_info->buf_len)); dump_hex(op_info->buf, op_info->buf_len, stderr, CK_TRUE); case CKM_RSA_PKCS: @@ -274,7 +277,7 @@ CK_RV apply_sign_mechanism_finalize(op_info_t *op_info) { if (rv != CKR_OK) return CKR_FUNCTION_FAILED; - DBG(("After adding digestinfo is %lu long and looks like\n", op_info->buf_len)); +// DBG(("After adding digestinfo is %lu long and looks like\n", op_info->buf_len)); dump_hex(op_info->buf, op_info->buf_len, stderr, CK_TRUE); } @@ -350,27 +353,39 @@ CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG case CKA_PUBLIC_EXPONENT: if (op_info->op.gen.rsa == CK_FALSE) - return CKR_MECHANISM_PARAM_INVALID; + return CKR_ATTRIBUTE_VALUE_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; + if (templ[i].ulValueLen != 3 || memcmp((CK_BYTE_PTR)templ[i].pValue, F4, 3) != 0) + return CKR_ATTRIBUTE_VALUE_INVALID; break; case CKA_MODULUS_BITS: if (op_info->op.gen.rsa == CK_FALSE) - return CKR_MECHANISM_PARAM_INVALID; + return CKR_ATTRIBUTE_VALUE_INVALID; if (*((CK_ULONG_PTR)templ[i].pValue) != 1024 && *((CK_ULONG_PTR) templ[i].pValue) != 2048) // TODO: make define? - return CKR_MECHANISM_PARAM_INVALID; + return CKR_ATTRIBUTE_VALUE_INVALID; - op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); // TODO: check length? + op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); + break; + + case CKA_EC_PARAMS: + // Only support PRIME256V1 + if (templ[i].ulValueLen != 10 || memcmp((CK_BYTE_PTR)templ[i].pValue, PRIME256V1, 10) != 0) + return CKR_CURVE_NOT_SUPPORTED; + + op_info->op.gen.key_len = 256; break; case CKA_ID: - // TODO: get pvt key with attributed id and store it's id into op_info + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + op_info->op.gen.key_id = PIV_PVTK_OBJ_PIV_AUTH + *((CK_BYTE_PTR)templ[i].pValue); + break; case CKA_TOKEN: case CKA_ENCRYPT: @@ -378,9 +393,9 @@ CK_RV check_pubkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG case CKA_WRAP: // Ignore these attributes for now break; - + default: - return CKR_MECHANISM_PARAM_INVALID; + return CKR_ATTRIBUTE_VALUE_INVALID; } } @@ -410,17 +425,7 @@ CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG 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: +/* case CKA_MODULUS_BITS: if (op_info->op.gen.rsa == CK_FALSE) return CKR_MECHANISM_PARAM_INVALID; @@ -429,6 +434,19 @@ CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG return CKR_MECHANISM_PARAM_INVALID; op_info->op.gen.key_len = *((CK_ULONG_PTR) templ[i].pValue); // TODO: check length? + break;*/ + + case CKA_ID: + if (is_valid_key_id(*((CK_BYTE_PTR)templ[i].pValue)) == CK_FALSE) + return CKR_ATTRIBUTE_VALUE_INVALID; + + // Check if ID was already specified in the public key template + // In that case it has to match + if (op_info->op.gen.key_id != 0 && + op_info->op.gen.key_id != (*((CK_BYTE_PTR)templ[i].pValue) + PIV_PVTK_OBJ_PIV_AUTH)) + return CKR_TEMPLATE_INCONSISTENT; + + op_info->op.gen.key_id = PIV_PVTK_OBJ_PIV_AUTH + *((CK_BYTE_PTR)templ[i].pValue); break; case CKA_SENSITIVE: @@ -441,7 +459,7 @@ CK_RV check_pvtkey_template(op_info_t *op_info, CK_ATTRIBUTE_PTR templ, CK_ULONG break; default: - return CKR_MECHANISM_PARAM_INVALID; + return CKR_ATTRIBUTE_VALUE_INVALID; } } diff --git a/ykcs11/openssl_utils.c b/ykcs11/openssl_utils.c index 3eac7e2..f4e8964 100644 --- a/ykcs11/openssl_utils.c +++ b/ykcs11/openssl_utils.c @@ -3,7 +3,6 @@ #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) { const unsigned char *p = data; // Mandatory temp variable required by OpenSSL @@ -34,6 +33,177 @@ CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert) { return CKR_OK; } +#include "debug.h" +CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, CK_ULONG key_len, + CK_BYTE_PTR out, CK_ULONG_PTR out_len) { + + X509 *cert = NULL; + EVP_PKEY *key = NULL; + RSA *rsa = NULL; + BIGNUM *bignum_n = NULL; + BIGNUM *bignum_e = NULL; + EC_KEY *eck = NULL; + EC_GROUP *ecg = NULL; + EC_POINT *ecp = NULL; + ASN1_TIME *tm = NULL; + time_t t; + + unsigned char *data_ptr; + unsigned char *p; + int len; + + CK_RV rv = CKR_FUNCTION_FAILED; + + cert = X509_new(); + if (cert == NULL) + goto create_empty_cert_cleanup; + + key = EVP_PKEY_new(); + if (key == NULL) + goto create_empty_cert_cleanup; + + if (is_rsa) { + // RSA + rsa = RSA_new(); + if (rsa == NULL) + goto create_empty_cert_cleanup; + + data_ptr = in + 5; + dump_hex(in, in_len, stderr, CK_TRUE); + if (*data_ptr != 0x81) + goto create_empty_cert_cleanup; + + data_ptr++; + data_ptr += get_length(data_ptr, &len); + bignum_n = BN_bin2bn(data_ptr, len, NULL); + if(bignum_n == NULL) + goto create_empty_cert_cleanup; + + data_ptr += len; + + if(*data_ptr != 0x82) + goto create_empty_cert_cleanup; + + data_ptr++; + data_ptr += get_length(data_ptr, &len); + bignum_e = BN_bin2bn(data_ptr, len, NULL); + if(bignum_e == NULL) + goto create_empty_cert_cleanup; + + rsa->n = bignum_n; + rsa->e = bignum_e; + + if (EVP_PKEY_set1_RSA(key, rsa) == 0) + goto create_empty_cert_cleanup; + } + else { + // ECCP256 + data_ptr = in + 3; + + eck = EC_KEY_new(); + if (eck == NULL) + goto create_empty_cert_cleanup; + + ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + if (ecg == NULL) + goto create_empty_cert_cleanup; + + EC_GROUP_set_asn1_flag(ecg, NID_X9_62_prime256v1); + EC_KEY_set_group(eck, ecg); + ecp = EC_POINT_new(ecg); + + if(*data_ptr++ != 0x86) + goto create_empty_cert_cleanup; + + // The curve point should always be 65 bytes + if(*data_ptr++ != 65) + goto create_empty_cert_cleanup; + + if(EC_POINT_oct2point(ecg, ecp, data_ptr, 65, NULL) == 0) + goto create_empty_cert_cleanup; + + if(EC_KEY_set_public_key(eck, ecp) == 0) + goto create_empty_cert_cleanup; + + if (EVP_PKEY_set1_EC_KEY(key, eck) == 0) + goto create_empty_cert_cleanup; + } + + if (X509_set_pubkey(cert, key) == 0) // TODO: there is also X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey); + goto create_empty_cert_cleanup; + + p = in; + if ((*out_len = i2d_X509(cert, &p)) == 0) + goto create_empty_cert_cleanup; + + // TODO: add more info like issuer? + tm = ASN1_TIME_new(); + if (tm == NULL) + goto create_empty_cert_cleanup; + + ASN1_TIME_set_string(tm, "000001010000Z"); + X509_set_notBefore(cert, tm); + X509_set_notAfter(cert, tm); + + /* TODO REMOVE THIS */ + BIO *STDout = BIO_new_fp(stderr, BIO_NOCLOSE); + + X509_print_ex(STDout, cert, 0, 0); + + BIO_free(STDout); + /********************/ + + rv = CKR_OK; + +create_empty_cert_cleanup: + + if (tm != NULL) { + ASN1_STRING_free(tm); + tm = NULL; + } + + if (bignum_n != NULL) { + BN_free(bignum_n); + bignum_n = NULL; + } + + if (bignum_e != NULL) { + BN_free(bignum_e); + bignum_e = NULL; + } + +/* if (rsa != NULL) { // TODO: adding this generates an error. Automatically free'd by EVP_PKEY_free ? + RSA_free(rsa); + rsa = NULL; + }*/ + + if (ecp != NULL) { + EC_POINT_free(ecp); + ecp = NULL; + } + + if (ecg != NULL) { + EC_GROUP_free(ecg); + ecg = NULL; + } + + if (eck != NULL) { + EC_KEY_free(eck); + eck = NULL; + } + + if (key != NULL) { + EVP_PKEY_free(key); + key = NULL; + } + + if (cert != NULL) { + X509_free(cert); + cert = NULL; + } + + return rv; +} CK_RV free_cert(X509 *cert) { @@ -142,6 +312,9 @@ CK_RV do_get_public_key(EVP_PKEY *key, CK_BYTE_PTR data, CK_ULONG_PTR len) { return CKR_FUNCTION_FAILED; } + EC_KEY_free(eck); + eck = NULL; + return CKR_OK; } diff --git a/ykcs11/openssl_utils.h b/ykcs11/openssl_utils.h index e146001..c29fd18 100644 --- a/ykcs11/openssl_utils.h +++ b/ykcs11/openssl_utils.h @@ -10,6 +10,8 @@ #include "pkcs11t.h" CK_RV do_store_cert(CK_BYTE_PTR data, CK_ULONG len, X509 **cert); +CK_RV do_create_empty_cert(CK_BYTE_PTR in, CK_ULONG in_len, CK_BBOOL is_rsa, CK_ULONG key_len, + CK_BYTE_PTR out, CK_ULONG_PTR out_len); CK_RV free_cert(X509 *cert); CK_RV do_store_pubk(X509 *cert, EVP_PKEY **key); diff --git a/ykcs11/pkcs11t.h b/ykcs11/pkcs11t.h index 7ced074..5b64d63 100644 --- a/ykcs11/pkcs11t.h +++ b/ykcs11/pkcs11t.h @@ -838,6 +838,10 @@ typedef CK_ULONG CK_RV; /* These are new to v2.0 */ #define CKR_RANDOM_NO_RNG 0x00000121UL +/* Next two are new for v2.2 */ +#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL +#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL +/* ************************* */ #define CKR_BUFFER_TOO_SMALL 0x00000150UL #define CKR_SAVED_STATE_INVALID 0x00000160UL #define CKR_INFORMATION_SENSITIVE 0x00000170UL diff --git a/ykcs11/token_vendors.c b/ykcs11/token_vendors.c index 96045b8..f6b285e 100644 --- a/ykcs11/token_vendors.c +++ b/ykcs11/token_vendors.c @@ -10,6 +10,8 @@ static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, CK_BYTE unsigned long received = 0; int sw; + CK_RV rv; + templ[3] = key; in_data[0] = 0xac; @@ -41,6 +43,12 @@ static CK_RV COMMON_token_generate_key(ykpiv_state *state, CK_BBOOL rsa, CK_BYTE /* to drop the 90 00 and the 7f 49 at the start */ received += recv_len - 4; + + + /* Create a new empty certificate for the key */ + if ((rv = do_create_empty_cert(data, recv_len, rsa, key_len, in_data, &recv_len)) != CKR_OK) + return rv; + return CKR_OK; } diff --git a/ykcs11/utils.c b/ykcs11/utils.c index 00eb483..170fd37 100644 --- a/ykcs11/utils.c +++ b/ykcs11/utils.c @@ -165,3 +165,13 @@ void destroy_token(ykcs11_slot_t *slot) { free(slot->token); slot->token = NULL; } + +CK_BBOOL is_valid_key_id(CK_BYTE id) { + + // Valid ids are 0, 1, 2, 3 + if (id > 3) + return CK_FALSE; + + return CK_TRUE; + +} diff --git a/ykcs11/utils.h b/ykcs11/utils.h index 5bcb8db..8ada4dc 100644 --- a/ykcs11/utils.h +++ b/ykcs11/utils.h @@ -9,4 +9,6 @@ CK_RV parse_readers(const CK_BYTE_PTR readers, const CK_ULONG len, CK_RV create_token(CK_BYTE_PTR p, ykcs11_slot_t *slot); void destroy_token(ykcs11_slot_t *slot); +CK_BBOOL is_valid_key_id(CK_BYTE id); + #endif diff --git a/ykcs11/ykcs11.c b/ykcs11/ykcs11.c index 78d881d..1200018 100644 --- a/ykcs11/ykcs11.c +++ b/ykcs11/ykcs11.c @@ -534,7 +534,7 @@ failure: cert_ids = NULL; } - free_certs(); // TODO: remove the one allocated so far + //free_certs(); // TODO: remove the one allocated so far return rv; } @@ -680,7 +680,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_Login)( case CKU_USER: 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; @@ -866,7 +866,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)( CK_ULONG j; CK_ULONG total; CK_BBOOL private; - + if (piv_state == NULL) { DBG(("libykpiv is not initialized or already finalized")); @@ -925,7 +925,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)( total--; continue; } - + for (j = 0; j < ulCount; j++) { DBG(("Parameter %lu\nType: %lu Value: %lu Len: %lu", j, pTemplate[j].type, *((CK_ULONG_PTR)pTemplate[j].pValue), pTemplate[j].ulValueLen)); @@ -941,9 +941,9 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjectsInit)( } DBG(("%lu object(s) left after attribute matching", total)); - + find_obj.active = CK_TRUE; - + DOUT; return CKR_OK; } @@ -984,7 +984,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_FindObjects)( while(find_obj.idx < find_obj.num && find_obj.objects[find_obj.idx] == OBJECT_INVALID) find_obj.idx++; - + if (find_obj.idx == find_obj.num) { *pulObjectCount = 0; DOUT; @@ -1272,7 +1272,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_SignInit)( return CKR_HOST_MEMORY; template[2].pValue = op_info.op.sign.key; - template[2].ulValueLen = key_len; + template[2].ulValueLen = key_len; if (get_attribute(&session, hKey, template + 2) != CKR_OK) { DBG(("Unable to get public key")); @@ -1620,7 +1620,7 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( phPrivateKey == NULL_PTR) return CKR_ARGUMENTS_BAD; - DBG(("Trying to generate a key pair with mechanism %lu", pMechanism->mechanism)); + DBG(("Trying to generate a key pair with mechanism %lx", pMechanism->mechanism)); DBG(("Found %lu attributes for the public key and %lu attributes for the private key", ulPublicKeyAttributeCount, ulPrivateKeyAttributeCount)); @@ -1631,24 +1631,37 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( } memcpy(&op_info.mechanism, pMechanism, sizeof(CK_MECHANISM)); + // Clear values + op_info.op.gen.key_len = 0; + op_info.op.gen.key_id = 0; + // 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 + // Check the template for the private 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; + DBG(("Key length not specified")); + return CKR_TEMPLATE_INCOMPLETE; } if (op_info.op.gen.key_id == 0) { - op_info.op.gen.key_id = PIV_PVTK_OBJ_KM; // TODO: set default key or error? + DBG(("Key id not specified, using default value")); + return CKR_TEMPLATE_INCOMPLETE; + } + + if (op_info.op.gen.rsa) { + DBG(("Generating %lu bit RSA key in object %u", op_info.op.gen.key_len, op_info.op.gen.key_id)); + } + else { + DBG(("Generating %lu bit EC key in object %u", op_info.op.gen.key_len, op_info.op.gen.key_id)); } token = get_token_vendor(session.slot->token->vid); @@ -1658,8 +1671,9 @@ CK_DEFINE_FUNCTION(CK_RV, C_GenerateKeyPair)( return rv; } - // TODO: save return object handlers - + *phPrivateKey = op_info.op.gen.key_id; + *phPublicKey = op_info.op.gen.key_id - PIV_PVTK_OBJ_KM + PIV_PUBK_OBJ_KM; // TODO: make function for these? + DOUT; return CKR_OK; } diff --git a/ykcs11/ykcs11.h b/ykcs11/ykcs11.h index 96d3193..057c155 100644 --- a/ykcs11/ykcs11.h +++ b/ykcs11/ykcs11.h @@ -47,7 +47,7 @@ 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 // 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_id; // Key id for ykpiv // TODO: make this a BYTE and store the id {0, 1, 2, 3} CK_ULONG key_len; // Length in bits } sign_info_t;