Files
yubikey.rs/ykcs11/yubico_token.c
T
2016-08-12 15:30:06 +02:00

389 lines
13 KiB
C

/*
* Copyright (c) 2015-2016 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 "yubico_token.h"
#include "pkcs11.h"
#include <string.h>
#include "debug.h"
#include "objects.h"
#define MIN_RSA_KEY_SIZE 1024
#define MAX_RSA_KEY_SIZE 2048
#define MIN_ECC_KEY_SIZE 256
#define MAX_ECC_KEY_SIZE 384
static const char *token_label = "YubiKey PIV";
static const char *token_manufacturer = "Yubico";
static const char *token_model = "YubiKey XXX";
static const CK_FLAGS token_flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED;
static const char *token_serial = "1234";
static const CK_MECHANISM_TYPE token_mechanisms[] = { // KEEP ALIGNED WITH token_mechanism_infos
CKM_RSA_PKCS_KEY_PAIR_GEN,
CKM_RSA_PKCS,
CKM_RSA_PKCS_PSS,
CKM_RSA_X_509,
CKM_SHA1_RSA_PKCS,
CKM_SHA256_RSA_PKCS,
CKM_SHA384_RSA_PKCS,
CKM_SHA512_RSA_PKCS,
CKM_SHA1_RSA_PKCS_PSS,
CKM_SHA256_RSA_PKCS_PSS,
CKM_SHA384_RSA_PKCS_PSS,
CKM_SHA512_RSA_PKCS_PSS,
CKM_EC_KEY_PAIR_GEN,
//CKM_ECDSA_KEY_PAIR_GEN, Same as CKM_EC_KEY_PAIR_GEN, deprecated in 2.11
CKM_ECDSA,
CKM_ECDSA_SHA1,
CKM_ECDSA_SHA256,
CKM_SHA_1,
CKM_SHA256,
CKM_SHA384,
CKM_SHA512
// SUPPORT FOR OATH?
};
static const CK_ULONG token_mechanisms_num = sizeof(token_mechanisms) / sizeof(CK_MECHANISM_TYPE);
static const CK_MECHANISM_INFO token_mechanism_infos[] = { // KEEP ALIGNED WITH token_mechanisms
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_GENERATE_KEY_PAIR}, // CKM_RSA_PKCS_KEY_PAIR_GEN
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_DECRYPT | CKF_SIGN}, // CKM_RSA_PKCS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_RSA_PKCS_PSS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_DECRYPT | CKF_SIGN}, // CKM_RSA_X_509
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA1_RSA_PKCS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA256_RSA_PKCS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA384_RSA_PKCS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA512_RSA_PKCS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA1_RSA_PKCS_PSS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA256_RSA_PKCS_PSS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA384_RSA_PKCS_PSS
{MIN_RSA_KEY_SIZE, MAX_RSA_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_SHA512_RSA_PKCS_PSS
{MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_GENERATE_KEY_PAIR}, // CKM_EC_KEY_PAIR_GEN
//{, , }, // CKM_ECDSA_KEY_PAIR_GEN Same as CKM_EC_KEY_PAIR_GEN deprecated in 2.11
{MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA
{MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA_SHA1
{MIN_ECC_KEY_SIZE, MAX_ECC_KEY_SIZE, CKF_HW | CKF_SIGN}, // CKM_ECDSA_SHA256
{0, 0, CKF_DIGEST}, // CKM_SHA_1
{0, 0, CKF_DIGEST}, // CKM_SHA256
{0, 0, CKF_DIGEST}, // CKM_SHA384
{0, 0, CKF_DIGEST} // CKM_SHA512
};
static const piv_obj_id_t token_objects[] = { // TODO: is there a way to get this from the token?
PIV_DATA_OBJ_X509_PIV_AUTH, // PIV authentication
PIV_DATA_OBJ_X509_CARD_AUTH, // card authentication
PIV_DATA_OBJ_X509_DS, // digital signature
PIV_DATA_OBJ_X509_KM, // key management
PIV_DATA_OBJ_X509_RETIRED1, // Retired key 1
PIV_DATA_OBJ_X509_RETIRED2, // Retired key 2
PIV_DATA_OBJ_X509_RETIRED3, // Retired key 3
PIV_DATA_OBJ_X509_RETIRED4, // Retired key 4
PIV_DATA_OBJ_X509_RETIRED5, // Retired key 5
PIV_DATA_OBJ_X509_RETIRED6, // Retired key 6
PIV_DATA_OBJ_X509_RETIRED7, // Retired key 7
PIV_DATA_OBJ_X509_RETIRED8, // Retired key 8
PIV_DATA_OBJ_X509_RETIRED9, // Retired key 9
PIV_DATA_OBJ_X509_RETIRED10, // Retired key 10
PIV_DATA_OBJ_X509_RETIRED11, // Retired key 11
PIV_DATA_OBJ_X509_RETIRED12, // Retired key 12
PIV_DATA_OBJ_X509_RETIRED13, // Retired key 13
PIV_DATA_OBJ_X509_RETIRED14, // Retired key 14
PIV_DATA_OBJ_X509_RETIRED15, // Retired key 15
PIV_DATA_OBJ_X509_RETIRED16, // Retired key 16
PIV_DATA_OBJ_X509_RETIRED17, // Retired key 17
PIV_DATA_OBJ_X509_RETIRED18, // Retired key 18
PIV_DATA_OBJ_X509_RETIRED19, // Retired key 19
PIV_DATA_OBJ_X509_RETIRED20, // Retired key 20
PIV_DATA_OBJ_CCC, // Card capability container
PIV_DATA_OBJ_CHUI, // Cardholder unique id
PIV_DATA_OBJ_CHF, // Cardholder fingerprints
PIV_DATA_OBJ_SEC_OBJ, // Security object
PIV_DATA_OBJ_CHFI, // Cardholder facial images
//PIV_DATA_OBJ_PI, // Cardholder printed information
//PIV_DATA_OBJ_DISCOVERY, // Discovery object
//PIV_DATA_OBJ_HISTORY, // History object
//PIV_DATA_OBJ_IRIS_IMAGE, // Cardholder iris images
//PIV_DATA_OBJ_BITGT, // Biometric information templates group template
//PIV_DATA_OBJ_SM_SIGNER, // Secure messaging signer
//PIV_DATA_OBJ_PC_REF_DATA, // Pairing code reference data
};
static const CK_ULONG neo_token_objects_num = sizeof(token_objects) / sizeof(piv_obj_id_t) - 20;
static const CK_ULONG yk4_token_objects_num = sizeof(token_objects) / sizeof(piv_obj_id_t);
CK_RV YUBICO_get_token_label(CK_UTF8CHAR_PTR str, CK_ULONG len) {
if (strlen(token_label) > len)
return CKR_BUFFER_TOO_SMALL;
memcpy(str, token_label, strlen(token_label));
return CKR_OK;
}
CK_RV YUBICO_get_token_manufacturer(CK_UTF8CHAR_PTR str, CK_ULONG len) {
if (strlen(token_manufacturer) > len)
return CKR_BUFFER_TOO_SMALL;
memcpy(str, token_manufacturer, strlen(token_manufacturer));
return CKR_OK;
}
CK_RV YUBICO_get_token_model(ykpiv_state *state, CK_UTF8CHAR_PTR str, CK_ULONG len) {
char buf[16];
if (strlen(token_model) > len)
return CKR_BUFFER_TOO_SMALL;
if (ykpiv_get_version(state, buf, sizeof(buf)) != YKPIV_OK)
return CKR_FUNCTION_FAILED;
memcpy(str, token_model, strlen(token_model));
if (buf[0] >= '4')
memcpy(str + strlen(token_model) - 3, "YK4", 3);
else
memcpy(str + strlen(token_model) - 3, "NEO", 3);
return CKR_OK;
}
CK_RV YUBICO_get_token_flags(CK_FLAGS_PTR flags) {
*flags = token_flags;
return CKR_OK;
}
CK_RV YUBICO_get_token_version(ykpiv_state *state, CK_VERSION_PTR version) {
char buf[16];
if (version == NULL)
return CKR_ARGUMENTS_BAD;
if (ykpiv_get_version(state, buf, sizeof(buf)) != YKPIV_OK)
return CKR_FUNCTION_FAILED;
version->major = (buf[0] - '0');
version->minor = (buf[2] - '0') * 100 + (buf[4] - '0');
return CKR_OK;
}
CK_RV YUBICO_get_token_serial(CK_CHAR_PTR str, CK_ULONG len) {
if (strlen(token_serial) > len)
return CKR_BUFFER_TOO_SMALL;
memcpy(str, token_serial, strlen(token_serial));
return CKR_OK;
}
CK_RV YUBICO_get_token_mechanisms_num(CK_ULONG_PTR num) {
*num = token_mechanisms_num;
return CKR_OK;
}
CK_RV YUBICO_get_token_mechanism_list(CK_MECHANISM_TYPE_PTR mec, CK_ULONG num) {
if (token_mechanisms_num > num)
return CKR_BUFFER_TOO_SMALL;
memcpy(mec, token_mechanisms, token_mechanisms_num * sizeof(CK_MECHANISM_TYPE));
return CKR_OK;
}
CK_RV YUBICO_get_token_mechanism_info(CK_MECHANISM_TYPE mec, CK_MECHANISM_INFO_PTR info) {
CK_ULONG i;
for (i = 0; i < token_mechanisms_num; i++)
if (token_mechanisms[i] == mec) {
memcpy((CK_BYTE_PTR) info, (CK_BYTE_PTR) (token_mechanism_infos + i), sizeof(CK_MECHANISM_INFO));
return CKR_OK;
}
return CKR_MECHANISM_INVALID;
}
static CK_RV get_objects(ykpiv_state *state, CK_BBOOL num_only,
piv_obj_id_t *obj, CK_ULONG_PTR len, CK_ULONG_PTR num_certs) {
CK_BYTE buf[2048];
CK_ULONG buf_len;
CK_BYTE major;
CK_ULONG i;
piv_obj_id_t certs[24];
piv_obj_id_t pvtkeys[24];
piv_obj_id_t pubkeys[24];
CK_ULONG n_cert = 0;
CK_ULONG token_objects_num = neo_token_objects_num;
if (state == NULL || len == NULL_PTR)
return CKR_ARGUMENTS_BAD;
if (num_only == CK_FALSE && obj == NULL)
return CKR_ARGUMENTS_BAD;
if (ykpiv_get_version(state, (char *) buf, sizeof(buf)) != YKPIV_OK)
return CKR_FUNCTION_FAILED;
major = buf[0] - '0';
buf_len = sizeof(buf);
if (ykpiv_fetch_object(state, YKPIV_OBJ_AUTHENTICATION, buf, &buf_len) == YKPIV_OK) {
certs[n_cert] = PIV_CERT_OBJ_X509_PIV_AUTH;
pvtkeys[n_cert] = PIV_PVTK_OBJ_PIV_AUTH;
pubkeys[n_cert] = PIV_PUBK_OBJ_PIV_AUTH;
n_cert++;
DBG("Found AUTH cert (9a)");
}
buf_len = sizeof(buf);
if (ykpiv_fetch_object(state, YKPIV_OBJ_CARD_AUTH, buf, &buf_len) == YKPIV_OK) {
certs[n_cert] = PIV_CERT_OBJ_X509_CARD_AUTH;
pvtkeys[n_cert] = PIV_PVTK_OBJ_CARD_AUTH;
pubkeys[n_cert] = PIV_PUBK_OBJ_CARD_AUTH;
n_cert++;
DBG("Found CARD AUTH cert (9e)");
}
buf_len = sizeof(buf);
if (ykpiv_fetch_object(state, YKPIV_OBJ_SIGNATURE, buf, &buf_len) == YKPIV_OK) {
certs[n_cert] = PIV_CERT_OBJ_X509_DS;
pvtkeys[n_cert] = PIV_PVTK_OBJ_DS;
pubkeys[n_cert] = PIV_PUBK_OBJ_DS;
n_cert++;
DBG("Found SIGNATURE cert (9c)");
}
buf_len = sizeof(buf);
if (ykpiv_fetch_object(state, YKPIV_OBJ_KEY_MANAGEMENT, buf, &buf_len) == YKPIV_OK) {
certs[n_cert] = PIV_CERT_OBJ_X509_KM;
pvtkeys[n_cert] = PIV_PVTK_OBJ_KM;
pubkeys[n_cert] = PIV_PUBK_OBJ_KM;
n_cert++;
DBG("Found KMK cert (9d)");
}
if (major >= 4) {
for (i = 0; i < 20; i++) {
buf_len = sizeof(buf);
if (ykpiv_fetch_object(state, YKPIV_OBJ_RETIRED1 + i, buf, &buf_len) == YKPIV_OK) {
certs[n_cert] = PIV_CERT_OBJ_X509_RETIRED1 + i;
pvtkeys[n_cert] = PIV_PVTK_OBJ_RETIRED1 + i;
pubkeys[n_cert] = PIV_PUBK_OBJ_RETIRED1 + i;
n_cert++;
DBG("Found RETIRED cert (%lx)", 0x82 + i);
}
}
token_objects_num = yk4_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
// Each cert object counts for 3: cert, pub key, pvt key
*len = (n_cert * 3) + token_objects_num;
if (num_certs != NULL)
*num_certs = n_cert;
return CKR_OK;
}
if (*len < (n_cert * 3) + token_objects_num)
return CKR_BUFFER_TOO_SMALL;
// Copy data objects
if (major >= 4) {
// YK4: just copy all the objects
memcpy(obj, token_objects, token_objects_num * sizeof(piv_obj_id_t));
}
else {
// NEO: remove retired keys
memcpy(obj, token_objects, 4 * sizeof(piv_obj_id_t));
memcpy(obj + 4, token_objects + 24, (neo_token_objects_num - 4) * sizeof(piv_obj_id_t));
}
// Copy certificates
if (n_cert > 0) {
memcpy(obj + token_objects_num, certs, n_cert * sizeof(piv_obj_id_t));
memcpy(obj + token_objects_num + n_cert, pvtkeys, n_cert * sizeof(piv_obj_id_t));
memcpy(obj + token_objects_num + (2 * n_cert), pubkeys, n_cert * sizeof(piv_obj_id_t));
}
return CKR_OK;
}
CK_RV YUBICO_get_token_objects_num(ykpiv_state *state, CK_ULONG_PTR num, CK_ULONG_PTR num_certs) {
return get_objects(state, CK_TRUE, NULL, num, num_certs);
}
CK_RV YUBICO_get_token_object_list(ykpiv_state *state, piv_obj_id_t *obj, CK_ULONG num) {
return get_objects(state, CK_FALSE, obj, &num, NULL);
}
CK_RV YUBICO_get_token_raw_certificate(ykpiv_state *state, piv_obj_id_t obj, CK_BYTE_PTR data, CK_ULONG_PTR len) {
if (ykpiv_fetch_object(state, piv_2_ykpiv(obj), data, len) != YKPIV_OK)
return CKR_FUNCTION_FAILED;
return CKR_OK;
}
CK_RV YUBICO_token_change_pin(ykpiv_state *state, CK_USER_TYPE user_type, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) {
int tries;
ykpiv_rc res;
if (user_type != CKU_USER) {
DBG("TODO implement other users pin change");
return CKR_FUNCTION_FAILED;
}
res = ykpiv_change_pin(state, pOldPin, ulOldLen, pNewPin, ulNewLen, &tries);
switch (res) {
case YKPIV_OK:
return CKR_OK;
case YKPIV_SIZE_ERROR:
return CKR_PIN_LEN_RANGE;
case YKPIV_WRONG_PIN:
return CKR_PIN_INCORRECT;
case YKPIV_PIN_LOCKED:
return CKR_PIN_LOCKED;
default:
return CKR_FUNCTION_FAILED;
}
}