gnupg/tkd/pkcs11.c

1436 lines
32 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include "tkdaemon.h"
#include <gcrypt.h>
#include "../common/util.h"
#include "pkcs11.h"
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That
* length needs to small compared to the maximum Assuan line length. */
#define MAXLEN_PIN 100
/* Maximum allowed total data size for VALUE. */
#define MAXLEN_VALUE 4096
#define ck_function_list _CK_FUNCTION_LIST
#define ck_token_info _CK_TOKEN_INFO
#define ck_attribute _CK_ATTRIBUTE
#define ck_mechanism _CK_MECHANISM
#define parameter pParameter
#define parameter_len ulParameterLen
#define ck_slot_id_t CK_SLOT_ID
#define ck_session_handle_t CK_SESSION_HANDLE
#define ck_notification_t CK_NOTIFICATION
#define ck_flags_t CK_FLAGS
#define ck_object_handle_t CK_OBJECT_HANDLE
#define ck_mechanism_type_t CK_MECHANISM_TYPE
/*
* d_list -> dev
* session -> key_list -> key
*
*/
/*
* Major use cases:
* a few keys (two or three at maximum)
* with a single device, which only has one slot.
*
* So, static fixed allocation is better.
*/
#define MAX_KEYS 10
#define MAX_SLOTS 10
enum key_type {
KEY_RSA,
KEY_EC,
KEY_EDDSA,
};
#define KEY_FLAG_VALID (1 << 0)
#define KEY_FLAG_NO_PUBKEY (1 << 1)
#define KEY_FLAG_USAGE_SIGN (1 << 2)
#define KEY_FLAG_USAGE_DECRYPT (1 << 3)
struct key {
struct token *token; /* Back pointer. */
unsigned long flags;
int key_type;
char keygrip[2*KEYGRIP_LEN+1];
gcry_sexp_t pubkey;
/* PKCS#11 interface */
unsigned char label[256];
unsigned long label_len;
unsigned char id[256];
unsigned long id_len;
ck_object_handle_t p11_keyid;
ck_mechanism_type_t mechanism;
};
struct token {
struct cryptoki *ck; /* Back pointer. */
int valid;
ck_slot_id_t slot_id;
int login_required;
ck_session_handle_t session;
int num_keys;
struct key key_list[MAX_KEYS];
};
struct cryptoki {
void *handle; /* DL handle to PKCS#11 Module. */
struct ck_function_list *f;
int num_slots;
struct token token_list[MAX_SLOTS];
};
/* Possibly, we will extend this to support multiple PKCS#11 modules.
* For now, it's only one.
*/
static struct cryptoki ck_instance[1];
static long
get_function_list (struct cryptoki *ck)
{
unsigned long r = 0;
unsigned long (*p_func) (struct ck_function_list **);
p_func = (CK_C_GetFunctionList)dlsym (ck->handle, "C_GetFunctionList");
if (p_func == NULL)
{
return -1;
}
r = p_func (&ck->f);
if (r || ck->f == NULL)
{
return -1;
}
r = ck->f->C_Initialize (NULL);
if (r)
{
return -1;
}
return 0;
}
static long
get_slot_list (struct cryptoki *ck,
unsigned long *num_slot_p,
ck_slot_id_t *slot_list)
{
unsigned long err = 0;
/* Scute requires first call with NULL, to rescan. */
err = ck->f->C_GetSlotList (TRUE, NULL, num_slot_p);
if (err)
return err;
err = ck->f->C_GetSlotList (TRUE, slot_list, num_slot_p);
if (err)
{
return err;
}
return 0;
}
static long
get_token_info (struct token *token, struct ck_token_info *tk_info)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
ck_slot_id_t slot_id = token->slot_id;
err = ck->f->C_GetTokenInfo (slot_id, tk_info);
if (err)
{
return err;
}
return 0;
}
/* XXX Implement some useful things to be notified... */
struct p11dev {
int d;
};
static struct p11dev p11_priv;
static unsigned long
notify_cb (ck_session_handle_t session,
ck_notification_t event, void *application)
{
struct p11dev *priv = application;
(void)priv;
(void)session;
(void)event;
(void)application;
return 0;
}
static long
open_session (struct token *token)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
ck_slot_id_t slot_id = token->slot_id;
ck_session_handle_t session_handle;
ck_flags_t session_flags;
session_flags = CKU_USER;
// session_flags = session_flags | CKF_RW_SESSION;
session_flags = session_flags | CKF_SERIAL_SESSION;
err = ck->f->C_OpenSession (slot_id, session_flags,
(void *)&p11_priv, notify_cb, &session_handle);
if (err)
{
log_debug ("open_session: %ld\n", err);
return -1;
}
token->session = session_handle;
token->valid = 1;
token->num_keys = 0;
return 0;
}
static long
close_session (struct token *token)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
if (!token->valid)
return -1;
err = ck->f->C_CloseSession (token->session);
if (err)
{
return -1;
}
return 0;
}
static long
login (struct token *token,
const unsigned char *pin, int pin_len)
{
unsigned long err = 0;
unsigned long user_type = CKU_USER;
struct cryptoki *ck = token->ck;
err = ck->f->C_Login (token->session, user_type,
(unsigned char *)pin, pin_len);
if (err)
{
return -1;
}
return 0;
}
static long
logout (struct token *token)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
err = ck->f->C_Logout (token->session);
if (err)
{
return -1;
}
return 0;
}
static void
compute_keygrip_rsa (char *keygrip, gcry_sexp_t *r_pubkey,
const char *modulus, unsigned long modulus_len,
const char *exponent, unsigned long exponent_len)
{
gpg_error_t err;
gcry_sexp_t s_pkey = NULL;
const char *format = "(public-key(rsa(n%b)(e%b)))";
unsigned char grip[20];
*r_pubkey = NULL;
err = gcry_sexp_build (&s_pkey, NULL, format,
(int)modulus_len, modulus,
(int)exponent_len, exponent);
if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
err = gpg_error (GPG_ERR_INTERNAL);
else
{
bin2hex (grip, 20, keygrip);
log_debug ("keygrip: %s\n", keygrip);
*r_pubkey = s_pkey;
}
}
static void
compute_keygrip_ec (char *keygrip, gcry_sexp_t *r_pubkey,
const char *curve, const char *ecpoint,
unsigned long ecpoint_len)
{
gpg_error_t err;
gcry_sexp_t s_pkey = NULL;
const char *format = "(public-key(ecc(curve %s)(q%b)))";
unsigned char grip[20];
*r_pubkey = NULL;
err = gcry_sexp_build (&s_pkey, NULL, format, curve, (int)ecpoint_len,
ecpoint);
if (!err && !gcry_pk_get_keygrip (s_pkey, grip))
err = gpg_error (GPG_ERR_INTERNAL);
else
{
bin2hex (grip, 20, keygrip);
log_debug ("keygrip: %s\n", keygrip);
*r_pubkey = s_pkey;
}
}
static long
examine_public_key (struct token *token, struct key *k, unsigned long keytype,
int update_keyid, ck_object_handle_t obj)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
unsigned char modulus[1024];
unsigned char exponent[8];
unsigned char ecparams[256];
unsigned char ecpoint[256];
struct ck_attribute templ[3];
unsigned long mechanisms[3];
unsigned char supported;
if (keytype == CKK_RSA)
{
if (update_keyid)
k->p11_keyid = obj;
k->key_type = KEY_RSA;
templ[0].type = CKA_MODULUS;
templ[0].pValue = (void *)modulus;
templ[0].ulValueLen = sizeof (modulus);
templ[1].type = CKA_PUBLIC_EXPONENT;
templ[1].pValue = (void *)exponent;
templ[1].ulValueLen = sizeof (exponent);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 2);
if (err)
{
k->flags |= KEY_FLAG_NO_PUBKEY;
return 1;
}
k->flags |= KEY_FLAG_VALID;
k->flags &= ~KEY_FLAG_NO_PUBKEY;
if ((modulus[0] & 0x80))
{
memmove (modulus+1, modulus, templ[0].ulValueLen);
templ[0].ulValueLen++;
modulus[0] = 0;
}
/* Found a RSA key. */
log_debug ("RSA: %ld %ld\n",
templ[0].ulValueLen,
templ[1].ulValueLen);
compute_keygrip_rsa (k->keygrip, &k->pubkey,
modulus, templ[0].ulValueLen,
exponent, templ[1].ulValueLen);
k->mechanism = CKM_RSA_PKCS;
}
else if (keytype == CKK_EC)
{
char *curve_oid = NULL;
const char *curve;
if (update_keyid)
k->p11_keyid = obj;
k->key_type = KEY_EC;
templ[0].type = CKA_EC_PARAMS;
templ[0].pValue = ecparams;
templ[0].ulValueLen = sizeof (ecparams);
templ[1].type = CKA_EC_POINT;
templ[1].pValue = (void *)ecpoint;
templ[1].ulValueLen = sizeof (ecpoint);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 2);
if (err)
{
k->flags |= KEY_FLAG_NO_PUBKEY;
return 1;
}
k->flags |= KEY_FLAG_VALID;
k->flags &= ~KEY_FLAG_NO_PUBKEY;
/* Found an ECC key. */
log_debug ("ECC: %ld %ld\n",
templ[0].ulValueLen,
templ[1].ulValueLen);
curve_oid = openpgp_oidbuf_to_str (ecparams+1, templ[0].ulValueLen-1);
curve = openpgp_oid_to_curve (curve_oid, 1);
xfree (curve_oid);
compute_keygrip_ec (k->keygrip, &k->pubkey,
curve, ecpoint, templ[1].ulValueLen);
templ[0].type = CKA_ALLOWED_MECHANISMS;
templ[0].pValue = (void *)mechanisms;
templ[0].ulValueLen = sizeof (mechanisms);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
if (!err)
{
if (templ[0].ulValueLen)
{
/* Scute works well. */
log_debug ("mechanism: %lx %ld\n", mechanisms[0], templ[0].ulValueLen);
k->mechanism = mechanisms[0];
}
else
{
log_debug ("SoftHSMv2???");
k->mechanism = CKM_ECDSA;
}
}
else
{
/* Yubkey YKCS doesn't offer CKA_ALLOWED_MECHANISMS,
unfortunately. */
log_debug ("Yubikey???");
k->mechanism = CKM_ECDSA_SHA256;
}
}
templ[0].type = CKA_SIGN;
templ[0].pValue = (void *)&supported;
templ[0].ulValueLen = sizeof (supported);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
if (!err)
{
/* XXX: Scute has the attribute, but not set. */
k->flags |= KEY_FLAG_USAGE_SIGN;
}
templ[0].type = CKA_DECRYPT;
templ[0].pValue = (void *)&supported;
templ[0].ulValueLen = sizeof (supported);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
if (!err && supported)
{
k->flags |= KEY_FLAG_USAGE_DECRYPT;
}
return 0;
}
static long
detect_private_keys (struct token *token)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
struct ck_attribute templ[8];
unsigned long class;
unsigned long keytype;
unsigned long cnt = 0;
ck_object_handle_t obj;
class = CKO_PRIVATE_KEY;
templ[0].type = CKA_CLASS;
templ[0].pValue = (void *)&class;
templ[0].ulValueLen = sizeof (class);
token->num_keys = 0;
err = ck->f->C_FindObjectsInit (token->session, templ, 1);
if (!err)
{
while (TRUE)
{
unsigned long any;
struct key *k = &token->key_list[cnt]; /* Allocate a key. */
k->token = token;
k->flags = 0;
/* Portable way to get objects... is get it one by one. */
err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
if (err || any == 0)
break;
templ[0].type = CKA_KEY_TYPE;
templ[0].pValue = &keytype;
templ[0].ulValueLen = sizeof (keytype);
templ[1].type = CKA_LABEL;
templ[1].pValue = (void *)k->label;
templ[1].ulValueLen = sizeof (k->label) - 1;
templ[2].type = CKA_ID;
templ[2].pValue = (void *)k->id;
templ[2].ulValueLen = sizeof (k->id) - 1;
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
if (err)
{
continue;
}
cnt++;
k->label_len = templ[1].ulValueLen;
k->label[k->label_len] = 0;
k->id_len = templ[2].ulValueLen;
k->id[k->id_len] = 0;
log_debug ("slot: %lx handle: %ld label: %s key_type: %ld id: %s\n",
token->slot_id, obj, k->label, keytype, k->id);
if (examine_public_key (token, k, keytype, 1, obj))
continue;
}
token->num_keys = cnt;
err = ck->f->C_FindObjectsFinal (token->session);
if (err)
{
return -1;
}
}
return 0;
}
static long
check_public_keys (struct token *token)
{
unsigned long err = 0;
struct cryptoki *ck = token->ck;
struct ck_attribute templ[8];
unsigned char label[256];
unsigned long class;
unsigned long keytype;
unsigned char id[256];
ck_object_handle_t obj;
int i;
class = CKO_PUBLIC_KEY;
templ[0].type = CKA_CLASS;
templ[0].pValue = (void *)&class;
templ[0].ulValueLen = sizeof (class);
err = ck->f->C_FindObjectsInit (token->session, templ, 1);
if (!err)
{
while (TRUE)
{
unsigned long any;
struct key *k = NULL;
/* Portable way to get objects... is get it one by one. */
err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
if (err || any == 0)
break;
templ[0].type = CKA_LABEL;
templ[0].pValue = (void *)label;
templ[0].ulValueLen = sizeof (label);
templ[1].type = CKA_KEY_TYPE;
templ[1].pValue = &keytype;
templ[1].ulValueLen = sizeof (keytype);
templ[2].type = CKA_ID;
templ[2].pValue = (void *)id;
templ[2].ulValueLen = sizeof (id);
err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
if (err)
{
continue;
}
label[templ[0].ulValueLen] = 0;
id[templ[2].ulValueLen] = 0;
/* Locate matching private key. */
for (i = 0; i < token->num_keys; i++)
{
k = &token->key_list[i];
if ((k->flags & KEY_FLAG_NO_PUBKEY)
&& k->label_len == templ[0].ulValueLen
&& memcmp (label, k->label, k->label_len) == 0
&& ((keytype == CKK_RSA && k->key_type == KEY_RSA)
|| (keytype == CKK_EC && k->key_type == KEY_EC))
&& k->id_len == templ[2].ulValueLen
&& memcmp (id, k->id, k->id_len) == 0)
break;
}
if (i == token->num_keys)
continue;
log_debug ("pub: slot: %lx handle: %ld label: %s key_type: %ld id: %s\n",
token->slot_id, obj, label, keytype, id);
if (examine_public_key (token, k, keytype, 0, obj))
continue;
}
err = ck->f->C_FindObjectsFinal (token->session);
if (err)
{
return -1;
}
}
return 0;
}
static long
get_certificate (struct token *token,
unsigned char **r_cert, size_t *r_certlen)
{
unsigned long r = 0;
struct cryptoki *ck = token->ck;
struct ck_attribute templ[1];
unsigned long class;
unsigned char certificate[4096];
int certificate_available = 0;
unsigned char *cert;
size_t certlen;
class = CKO_CERTIFICATE;
templ[0].type = CKA_CLASS;
templ[0].pValue = (void *)&class;
templ[0].ulValueLen = sizeof (class);
r = ck->f->C_FindObjectsInit (token->session, templ, 1);
if (!r)
{
ck_object_handle_t obj;
while (TRUE)
{
unsigned long any;
/* Portable way to get objects... is get it one by one. */
r = ck->f->C_FindObjects (token->session, &obj, 1, &any);
if (r || any == 0)
break;
templ[0].type = CKA_VALUE;
templ[0].pValue = (void *)certificate;
templ[0].ulValueLen = sizeof (certificate);
r = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
if (!r)
{
certificate_available = 1;
break;
}
}
r = ck->f->C_FindObjectsFinal (token->session);
if (r)
{
return -1;
}
}
if (!certificate_available)
return -1;
certlen = templ[0].ulValueLen;
cert = xtrymalloc (certlen);
if (!cert)
{
return gpg_error_from_syserror ();
}
memcpy (cert, certificate, certlen);
*r_cert = cert;
*r_certlen = certlen;
return 0;
}
static long
learn_keys (struct token *token)
{
int i;
/* Detect private keys on the token.
* It's good if it also offers raw public key material.
*/
detect_private_keys (token);
/*
* In some implementations (EC key on SoftHSMv2, for example),
* attributes for raw public key material is not available in
* a CKO_PRIVATE_KEY object.
*
* We try to examine CKO_PUBLIC_KEY objects, too see if it provides
* raw public key material in a CKO_PUBLIC_KEY object.
*/
check_public_keys (token);
for (i = 0; i < token->num_keys; i++)
{
struct key *k = &token->key_list[i];
if ((k->flags & KEY_FLAG_NO_PUBKEY))
k->flags &= ~KEY_FLAG_NO_PUBKEY;
}
return 0;
}
static long
find_key (struct cryptoki *ck, const char *keygrip, struct key **r_key)
{
int i;
int j;
log_debug ("find_key: %s\n", keygrip);
*r_key = NULL;
for (i = 0; i < ck->num_slots; i++)
{
struct token *token = &ck->token_list[i];
if (!token->valid)
continue;
for (j = 0; j < token->num_keys; j++)
{
struct key *k = &token->key_list[j];
if ((k->flags & KEY_FLAG_VALID) == 0)
continue;
if (memcmp (k->keygrip, keygrip, 40) == 0)
{
*r_key = k;
log_debug ("found a key at %d:%d\n", i, j);
return 0;
}
}
}
return -1;
}
struct iter_key {
struct cryptoki *ck;
int i;
int j;
unsigned long mask;
int st;
};
static void
iter_find_key_setup (struct iter_key *iter, struct cryptoki *ck, int cap)
{
iter->st = 0;
iter->ck = ck;
iter->i = 0;
iter->j = 0;
iter->mask = 0;
if (cap == GCRY_PK_USAGE_SIGN)
iter->mask |= KEY_FLAG_USAGE_SIGN;
else if (cap == GCRY_PK_USAGE_ENCR)
iter->mask = KEY_FLAG_USAGE_DECRYPT;
else
iter->mask = KEY_FLAG_USAGE_SIGN | KEY_FLAG_USAGE_DECRYPT;
}
static int
iter_find_key (struct iter_key *iter, struct key **r_key)
{
struct cryptoki *ck = iter->ck;
struct token *token;
struct key *k;
*r_key = NULL;
if (iter->i < ck->num_slots)
token = &ck->token_list[iter->i];
else
token = NULL;
switch (iter->st)
while (1)
{
case 0:
if (iter->i < ck->num_slots)
{
token = &ck->token_list[iter->i++];
if (!token->valid)
continue;
}
else
{
iter->st = 2;
/*FALLTHROUGH*/
default:
return 0;
}
iter->j = 0;
while (1)
{
/*FALLTHROUGH*/
case 1:
if (token && iter->j < token->num_keys)
{
k = &token->key_list[iter->j++];
if ((k->flags & KEY_FLAG_VALID) && (k->flags & iter->mask))
{
/* Found */
*r_key = k;
iter->st = 1;
return 1;
}
}
else
break;
}
}
}
static gpg_error_t
setup_pksign (struct key *key, int hash_algo,
unsigned char **r_signature, unsigned long *r_signature_len)
{
gpg_error_t err = 0;
unsigned long r = 0;
struct token *token = key->token;
struct cryptoki *ck = token->ck;
ck_mechanism_type_t mechanism;
struct ck_mechanism mechanism_struct;
unsigned int nbits;
unsigned long siglen;
unsigned char *sig;
nbits = gcry_pk_get_nbits (key->pubkey);
mechanism = key->mechanism;
if (key->key_type == KEY_RSA)
{
/* It's CKM_RSA_PKCS, it requires that hash algo OID included in
the data to be signed. */
if (!hash_algo)
return gpg_error (GPG_ERR_DIGEST_ALGO);
siglen = (nbits+7)/8;
}
else if (key->key_type == KEY_EC)
{
siglen = ((nbits+7)/8) * 2;
}
else if (key->key_type == KEY_EDDSA)
{
mechanism = CKM_EDDSA;
siglen = ((nbits+7)/8)*2;
}
else
return gpg_error (GPG_ERR_BAD_SECKEY);
mechanism_struct.mechanism = mechanism;
mechanism_struct.parameter = NULL;
mechanism_struct.parameter_len = 0;
r = ck->f->C_SignInit (token->session, &mechanism_struct,
key->p11_keyid);
if (r)
{
log_error ("C_SignInit error: %ld", r);
return gpg_error (GPG_ERR_INV_RESPONSE);
}
sig = xtrymalloc (siglen);
if (!sig)
return gpg_error_from_syserror ();
*r_signature = sig;
*r_signature_len = siglen;
return err;
}
static gpg_error_t
do_pksign (struct key *key, int hash_algo,
const unsigned char *u_data, unsigned long u_data_len,
unsigned char *signature,
unsigned long *r_signature_len)
{
gpg_error_t err = 0;
unsigned long r = 0;
struct token *token = key->token;
struct cryptoki *ck = token->ck;
ck_mechanism_type_t mechanism;
unsigned char data[1024];
unsigned long data_len;
mechanism = key->mechanism;
if (key->key_type == KEY_RSA)
{
size_t asnlen = sizeof (data);
gcry_md_get_asnoid (hash_algo, data, &asnlen);
/* u_data_len == gcry_md_get_algo_dlen (hash_algo) */
memcpy (data+asnlen, u_data, u_data_len);
data_len = asnlen+gcry_md_get_algo_dlen (hash_algo);
}
else if (key->key_type == KEY_EC)
{
if (mechanism == CKM_ECDSA)
{
/* SoftHSMv2 */
memcpy (data, u_data, u_data_len);
data_len = u_data_len;
}
else
{
if (!hash_algo)
{
/* Not specified by user, determine from MECHANISM */
if (mechanism == CKM_ECDSA_SHA256)
hash_algo = GCRY_MD_SHA256;
else if (mechanism == CKM_ECDSA_SHA384)
hash_algo = GCRY_MD_SHA384;
else if (mechanism == CKM_ECDSA_SHA384)
hash_algo = GCRY_MD_SHA512;
else
return gpg_error (GPG_ERR_DIGEST_ALGO);
}
/* Scute, YKCS11 */
/* u_data_len == gcry_md_get_algo_dlen (hash_algo) */
memcpy (data, u_data, u_data_len);
data_len = gcry_md_get_algo_dlen (hash_algo);
}
}
else if (key->key_type == KEY_EDDSA)
{
mechanism = CKM_EDDSA;
memcpy (data, u_data, u_data_len);
data_len = u_data_len;
}
else
return gpg_error (GPG_ERR_BAD_SECKEY);
r = ck->f->C_Sign (token->session,
data, data_len,
signature, r_signature_len);
if (r)
{
return gpg_error (GPG_ERR_INV_RESPONSE);
}
return err;
}
static gpg_error_t
token_open (assuan_context_t ctx, struct cryptoki *ck, struct token *token,
ck_slot_id_t slot_id)
{
gpg_error_t err = 0;
struct ck_token_info tk_info;
long r;
token->ck = ck;
token->valid = 0;
token->slot_id = slot_id;
if (get_token_info (token, &tk_info))
return gpg_error (GPG_ERR_INV_RESPONSE);
if ((tk_info.flags & CKF_TOKEN_INITIALIZED) == 0
|| (tk_info.flags & CKF_TOKEN_PRESENT) == 0
|| (tk_info.flags & CKF_USER_PIN_LOCKED) != 0)
return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
token->login_required = (tk_info.flags & CKF_LOGIN_REQUIRED);
r = open_session (token);
if (r)
{
log_error ("Error at open_session: %ld\n", r);
return gpg_error (GPG_ERR_INV_RESPONSE);
}
if (token->login_required)
{
char *command;
int rc;
unsigned char *value;
size_t valuelen;
log_debug ("asking for PIN '%ld'\n", token->slot_id);
rc = gpgrt_asprintf (&command, "NEEDPIN %ld", token->slot_id);
if (rc < 0)
return gpg_error (gpg_err_code_from_errno (errno));
assuan_begin_confidential (ctx);
err = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
assuan_end_confidential (ctx);
xfree (command);
if (err)
{
close_session (token);
token->session = 0;
return err;
}
login (token, value, valuelen);
xfree (value);
}
r = learn_keys (token);
return 0;
}
static gpg_error_t
token_close (struct token *token)
{
int j;
long r;
int num_keys = token->num_keys;
if (!token->valid)
return 0;
if (token->login_required)
logout (token);
r = close_session (token);
if (r)
log_error ("Error at close_session: %ld\n", r);
token->ck = NULL;
token->slot_id = 0;
token->login_required = 0;
token->session = 0;
token->num_keys = 0;
for (j = 0; j < num_keys; j++)
{
struct key *k = &token->key_list[j];
if ((k->flags & KEY_FLAG_VALID))
{
gcry_sexp_release (k->pubkey);
k->pubkey = NULL;
}
k->token = NULL;
k->flags = 0;
k->key_type = 0;
k->label_len = 0;
k->id_len = 0;
k->p11_keyid = 0;
k->mechanism = 0;
}
token->valid = 0;
return 0;
}
static gpg_error_t
token_check (struct token *token)
{
struct ck_token_info tk_info;
if (get_token_info (token, &tk_info))
{
/* Possibly, invalidate the token and close session.
* Now, ingore the error. */
return gpg_error (GPG_ERR_INV_RESPONSE);
}
if ((tk_info.flags & CKF_TOKEN_INITIALIZED) == 0
|| (tk_info.flags & CKF_TOKEN_PRESENT) == 0
|| (tk_info.flags & CKF_USER_PIN_LOCKED) != 0)
{
token_close (token);
return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
}
return 0;
}
gpg_error_t
tkd_init (ctrl_t ctrl, assuan_context_t ctx, int rescan)
{
gpg_error_t err = 0;
long r;
struct cryptoki *ck = ck_instance;
unsigned long num_slots = MAX_SLOTS;
ck_slot_id_t slot_list[MAX_SLOTS];
int i;
const char *module_name;
module_name = opt.pkcs11_driver;
if (!module_name)
return gpg_error (GPG_ERR_NO_NAME);
if (ck->handle == NULL)
{
void *handle;
int num_tokens = 0;
handle = dlopen (module_name, RTLD_NOW);
if (handle == NULL)
{
return -1;
}
ck->handle = handle;
r = get_function_list (ck);
if (r)
{
dlclose (ck->handle);
ck->handle = NULL;
return gpg_error (GPG_ERR_INV_RESPONSE);
}
r = get_slot_list (ck, &num_slots, slot_list);
if (r)
{
dlclose (ck->handle);
ck->handle = NULL;
return gpg_error (GPG_ERR_INV_RESPONSE);
}
for (i = 0; i < num_slots; i++)
{
struct token *token = &ck->token_list[num_tokens]; /* Allocate one token in CK */
err = token_open (ctx, ck, token, slot_list[i]);
if (!err)
num_tokens++;
}
ck->num_slots = num_tokens;
return 0;
}
else if (rescan == 0)
return 0;
/* Rescan the slots to see the changes. */
r = get_slot_list (ck, &num_slots, slot_list);
if (r)
{
tkd_fini (ctrl, ctx);
return gpg_error (GPG_ERR_INV_RESPONSE);
}
for (i = 0; i < num_slots; i++)
{
int j;
ck_slot_id_t slot_id = slot_list[i];
struct token *token = NULL;
for (j = 0; j < ck->num_slots; j++)
if (slot_id == ck->token_list[j].slot_id)
{
token = &ck->token_list[j];
break;
}
if (token)
{
err = token_check (token);
}
else
/* new token */
{
/* Allocate one token in CK */
token = &ck->token_list[ck->num_slots];
err = token_open (ctx, ck, token, slot_id);
if (!err)
ck->num_slots++;
}
}
return err;
}
gpg_error_t
tkd_fini (ctrl_t ctrl, assuan_context_t ctx)
{
long r;
struct cryptoki *ck = ck_instance;
int i;
(void)ctrl;
(void)ctx;
for (i = 0; i < ck->num_slots; i++)
{
struct token *token = &ck->token_list[i];
token_close (token);
}
ck->num_slots = 0;
r = ck->f->C_Finalize (NULL);
if (r)
{
return -1;
}
ck->f = NULL;
dlclose (ck->handle);
ck->handle = NULL;
return 0;
}
gpg_error_t
tkd_sign (ctrl_t ctrl, assuan_context_t ctx,
const char *keygrip, int hash_algo,
unsigned char **r_outdata, size_t *r_outdatalen)
{
gpg_error_t err;
struct key *k;
struct cryptoki *ck = ck_instance;
unsigned long r;
(void)ctrl;
/* mismatch: size_t for GnuPG, unsigned long for PKCS#11 */
/* mismatch: application prepare buffer for PKCS#11 */
if (!ck->handle)
{
err = tkd_init (ctrl, ctx, 0);
if (err)
return err;
}
*r_outdata = NULL;
r = find_key (ck, keygrip, &k);
if (r)
return gpg_error (GPG_ERR_NO_SECKEY);
else
{
const char *cmd;
unsigned char *value;
size_t valuelen;
unsigned char *sig = NULL;
err = setup_pksign (k, hash_algo, &sig, r_outdatalen);
if (err)
return err;
cmd = "EXTRA";
err = assuan_inquire (ctx, cmd, &value, &valuelen, MAXLEN_VALUE);
if (err)
{
xfree (sig);
return err;
}
err = do_pksign (k, hash_algo, value, valuelen, sig, r_outdatalen);
wipememory (value, valuelen);
xfree (value);
if (err)
{
xfree (sig);
return err;
}
*r_outdata = sig;
}
return err;
}
static const char *
get_usage_string (struct key *k)
{
const char *usage = NULL;
if ((k->flags & KEY_FLAG_USAGE_SIGN))
{
if ((k->flags & KEY_FLAG_USAGE_DECRYPT))
usage = "se";
else
usage = "s";
}
else
{
if ((k->flags & KEY_FLAG_USAGE_DECRYPT))
usage = "e";
else
usage = "-";
}
return usage;
}
gpg_error_t
tkd_readkey (ctrl_t ctrl, assuan_context_t ctx, const char *keygrip)
{
gpg_error_t err = 0;
struct key *k;
struct cryptoki *ck = ck_instance;
unsigned long r;
unsigned char *pk;
size_t pklen;
(void)ctrl;
(void)ctx;
if (!ck->handle)
{
err = tkd_init (ctrl, ctx, 0);
if (err)
return err;
}
r = find_key (ck, keygrip, &k);
if (r)
return gpg_error (GPG_ERR_NO_SECKEY);
pklen = gcry_sexp_sprint (k->pubkey, GCRYSEXP_FMT_CANON, NULL, 0);
pk = xtrymalloc (pklen);
if (!pk)
{
return gpg_error_from_syserror ();
}
gcry_sexp_sprint (k->pubkey, GCRYSEXP_FMT_CANON, pk, pklen);
err = assuan_send_data (ctx, pk, pklen);
xfree (pk);
return err;
}
gpg_error_t
tkd_readcert (ctrl_t ctrl, assuan_context_t ctx, const char *keygrip)
{
gpg_error_t err = 0;
struct key *k;
struct cryptoki *ck = ck_instance;
unsigned long r;
unsigned char *cert;
size_t certlen;
(void)ctrl;
(void)ctx;
if (!ck->handle)
{
err = tkd_init (ctrl, ctx, 0);
if (err)
return err;
}
r = find_key (ck, keygrip, &k);
if (r)
return gpg_error (GPG_ERR_NO_SECKEY);
if (get_certificate (k->token, &cert, &certlen))
return gpg_error (GPG_ERR_NOT_FOUND);
err = assuan_send_data (ctx, cert, certlen);
xfree (cert);
return err;
}
gpg_error_t
tkd_keyinfo (ctrl_t ctrl, assuan_context_t ctx, const char *keygrip,
int opt_data, int cap)
{
gpg_error_t err = 0;
struct cryptoki *ck = ck_instance;
struct key *k;
const char *usage;
if (!ck->handle)
{
err = tkd_init (ctrl, ctx, 0);
if (err)
return err;
}
if (keygrip)
{
unsigned long r;
r = find_key (ck, keygrip, &k);
if (r)
return gpg_error (GPG_ERR_NO_SECKEY);
usage = get_usage_string (k);
send_keyinfo (ctrl, opt_data, keygrip,
k->label_len ? (const char *)k->label : "-",
k->id_len ? (const char *)k->id : "-",
usage);
}
else
{
struct iter_key iter;
iter_find_key_setup (&iter, ck, cap);
while (iter_find_key (&iter, &k))
{
usage = get_usage_string (k);
send_keyinfo (ctrl, opt_data, k->keygrip,
k->label_len ? (const char *)k->label : "-",
k->id_len ? (const char *)k->id : "-",
usage);
}
}
return err;
}