diff --git a/src/cert-object.c b/src/cert-object.c index b2a5b06..bae3aea 100644 --- a/src/cert-object.c +++ b/src/cert-object.c @@ -1,780 +1,800 @@ /* cert-object.c - Convert a GPGSM certificate into a PKCS #11 object. * Copyright (C) 2006, 2007 g10 Code GmbH * * This file is part of Scute. * * Scute is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Scute is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "cryptoki.h" #include "support.h" #include "cert.h" #include "debug.h" #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #if 0 /* Currently not used. */ static bool time_to_ck_date (time_t *atime, CK_DATE *ckdate) { struct tm broken_time; int nr; if (!*atime) return false; #ifdef HAVE_LOCALTIME_R if (!localtime_r (atime, &broken_time)) return false; #else { /* FIXME: This is not thread-safe, but it minimizes risk. */ struct tm *b_time = localtime (atime); if (!b_time) return false; memcpy (&broken_time, b_time, sizeof (*b_time)); } #endif /* We can only represent years until 9999. */ if (!(broken_time.tm_year >= 0 && broken_time.tm_year <= 8099 && broken_time.tm_mon >= 0 && broken_time.tm_mon <= 11 && broken_time.tm_mday >= 1 && broken_time.tm_mday <= 31)) { DEBUG (DBG_INFO, "unrepresentable time %i-%i-%i", broken_time.tm_year, broken_time.tm_mon, broken_time.tm_mday); return false; } #define LAST_DIGIT(d) (((d) % 10) + '0') nr = broken_time.tm_year + 1900; ckdate->year[3] = LAST_DIGIT (nr); nr = nr / 10; ckdate->year[2] = LAST_DIGIT (nr); nr = nr / 10; ckdate->year[1] = LAST_DIGIT (nr); nr = nr / 10; ckdate->year[0] = LAST_DIGIT (nr); nr = broken_time.tm_mon + 1; ckdate->month[1] = LAST_DIGIT (nr); nr = nr / 10; ckdate->month[0] = LAST_DIGIT (nr); nr = broken_time.tm_mday; ckdate->day[1] = LAST_DIGIT (nr); nr = nr / 10; ckdate->day[0] = LAST_DIGIT (nr); return true; } #endif /*0*/ static gpg_error_t asn1_get_len (unsigned char **asn1, int *asn1_len, int *rlen) { unsigned char *ptr = *asn1; int len = *asn1_len; int cnt; int result = 0; if (len < 1) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*ptr & 0x80) { cnt = *ptr & 0x7f; ptr++; len--; } else cnt = 1; /* We only support a limited number of length bytes. */ if (cnt > 2) { DEBUG (DBG_INFO, "unsupported length field"); return gpg_error (GPG_ERR_GENERAL); } if (len < cnt) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } while (cnt--) { result = (result << 8) | *ptr; ptr++; len--; } *asn1 = ptr; *asn1_len = len; *rlen = result; return 0; } /* A path to an ASN.1 element that can be looked up with asn1_get_element. The last element in the list is returned (that one should have ENTER being false. */ struct asn1_path { unsigned char tag; /* True if we should enter the element, false if we should skip it. */ bool enter; }; static gpg_error_t asn1_get_element (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len, struct asn1_path *path, int path_size) { gpg_error_t err; unsigned char *prev_certp = NULL; unsigned char *certp = cert; int cert_left = cert_len; int len; int i; for (i = 0; i < path_size; i++) { prev_certp = certp; if (cert_left < 1) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != path[i].tag) { DEBUG (DBG_INFO, "wrong element in lookup path"); return gpg_error (GPG_ERR_GENERAL); } certp++; cert_left--; err = asn1_get_len (&certp, &cert_left, &len); if (err) return err; if (!path[i].enter) { if (cert_left < len) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } certp += len; cert_left -= len; } else { /* Special code to deal with ASN.1 data encapsulated in a bit string. */ if (path[i].tag == '\x03') { if (cert_left < 1) { DEBUG (DBG_INFO, "unexpected end of certificate"); return gpg_error (GPG_ERR_GENERAL); } if (*certp != '\x00') { DEBUG (DBG_INFO, "expected binary encapsulation missing"); return gpg_error (GPG_ERR_GENERAL); } certp++; cert_left--; } } } /* We found the subject. */ *sub_start = prev_certp; *sub_len = certp - prev_certp; return 0; } static gpg_error_t asn1_get_issuer (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { /* The path to the issuer entry in the DER file. This is Sequence->Sequence->Version,Serial,AlgID,Issuer. */ struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false }, { '\x30', false }, { '\x30', false } }; return asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); } static gpg_error_t asn1_get_subject (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { /* The path to the subject entry in the DER file. This is Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject. */ struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', false } }; return asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); } static gpg_error_t asn1_get_serial (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { /* The path to the serial entry in the DER file. This is Sequence->Sequence->Version,Serial. */ struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false } }; return asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); } static gpg_error_t asn1_get_modulus (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { gpg_error_t err; int len; struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', true }, { '\x30', false }, { '\x03', true }, { '\x30', true }, { '\x02', false } }; /* The path to the modulus entry in the DER file. This is Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject, Sequence->Sequence,Bitstring->Sequence->Integer,Integer */ err = asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); if (err) return err; if (*sub_len < 1) { DEBUG (DBG_INFO, "modulus too short"); return gpg_error (GPG_ERR_GENERAL); } (*sub_start)++; (*sub_len)--; err = asn1_get_len (sub_start, sub_len, &len); if (err) return err; /* PKCS #11 expects an unsigned big integer. */ while (**sub_start == '\x00' && *sub_len > 0) { (*sub_start)++; (*sub_len)--; } return 0; } static gpg_error_t asn1_get_public_exp (unsigned char *cert, int cert_len, unsigned char **sub_start, int *sub_len) { gpg_error_t err; int len; /* The path to the public exp entry in the DER file. This is Sequence->Sequence->Version,Serial,AlgID,Issuer,Time,Subject, Sequence->Sequence,Bitstring->Sequence->Integer,Integer */ struct asn1_path path[] = { { '\x30', true }, { '\x30', true }, { '\xa0', false }, { '\x02', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', false }, { '\x30', true }, { '\x30', false }, { '\x03', true }, { '\x30', true }, { '\x02', false }, { '\x02', false } }; err = asn1_get_element (cert, cert_len, sub_start, sub_len, path, DIM (path)); if (err) return err; if (*sub_len < 1) { DEBUG (DBG_INFO, "public exponent too short"); return gpg_error (GPG_ERR_GENERAL); } (*sub_start)++; (*sub_len)--; err = asn1_get_len (sub_start, sub_len, &len); if (err) return err; /* PKCS #11 expects an unsigned big integer. */ while (**sub_start == '\x00' && *sub_len > 0) { (*sub_start)++; (*sub_len)--; } return 0; } static gpg_error_t attr_one (CK_ATTRIBUTE_PTR attr, CK_ULONG *attr_count, CK_ATTRIBUTE_TYPE type, CK_VOID_PTR val, CK_ULONG size) { CK_ULONG i = *attr_count; attr[i].type = type; attr[i].ulValueLen = size; attr[i].pValue = malloc (size); if (attr[i].pValue == NULL) { DEBUG (DBG_CRIT, "out of memory"); return gpg_error (GPG_ERR_ENOMEM); } memcpy (attr[i].pValue, val, size); (*attr_count)++; return 0; } static gpg_error_t attr_empty (CK_ATTRIBUTE_PTR attr, CK_ULONG *attr_count, CK_ATTRIBUTE_TYPE type) { CK_ULONG i = *attr_count; attr[i].type = type; attr[i].ulValueLen = 0; attr[i].pValue = NULL_PTR; (*attr_count)++; return 0; } void scute_attr_free (CK_ATTRIBUTE_PTR attr, CK_ULONG attr_count) { while (0 < attr_count--) free (attr[attr_count].pValue); } gpg_error_t scute_attr_cert (struct cert *cert, const char *grip, CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp) { CK_RV err = 0; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; unsigned char *subject_start; int subject_len; unsigned char *issuer_start; int issuer_len; unsigned char *serial_start; int serial_len; CK_OBJECT_CLASS obj_class = CKO_CERTIFICATE; CK_BBOOL obj_token = CK_TRUE; CK_BBOOL obj_private = CK_FALSE; CK_BBOOL obj_modifiable = CK_FALSE; CK_CERTIFICATE_TYPE obj_cert_type = CKC_X_509; CK_BBOOL obj_trusted = cert->is_trusted; CK_ULONG obj_cert_cat = 0; CK_BYTE obj_check_value[3] = { '\0', '\0', '\0' }; CK_DATE obj_start_date; CK_DATE obj_end_date; CK_ULONG obj_java_midp_sec_domain = 0; err = asn1_get_subject (cert->cert_der, cert->cert_der_len, &subject_start, &subject_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get subject: %s", gpg_strerror (err)); return err; } err = asn1_get_issuer (cert->cert_der, cert->cert_der_len, &issuer_start, &issuer_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get issuer: %s", gpg_strerror (err)); return err; } err = asn1_get_serial (cert->cert_der, cert->cert_der_len, &serial_start, &serial_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get serial: %s", gpg_strerror (err)); return err; } #define NR_ATTR_CERT 20 attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_CERT); attr_count = 0; if (!attr) { DEBUG (DBG_INFO, "out of memory"); return gpg_error (GPG_ERR_ENOMEM); } if (!err) err = attr_one (attr, &attr_count, CKA_CLASS, &obj_class, sizeof obj_class); if (!err) err = attr_one (attr, &attr_count, CKA_TOKEN, &obj_token, sizeof obj_token); if (!err) err = attr_one (attr, &attr_count, CKA_PRIVATE, &obj_private, sizeof obj_private); if (!err) err = attr_one (attr, &attr_count, CKA_MODIFIABLE, &obj_modifiable, sizeof obj_modifiable); if (!err) err = attr_one (attr, &attr_count, CKA_LABEL, "Scute", 5); if (!err) err = attr_one (attr, &attr_count, CKA_CERTIFICATE_TYPE, &obj_cert_type, sizeof obj_cert_type); if (!err) err = attr_one (attr, &attr_count, CKA_TRUSTED, &obj_trusted, sizeof obj_trusted); if (!err) err = attr_one (attr, &attr_count, CKA_CERTIFICATE_CATEGORY, &obj_cert_cat, sizeof obj_cert_cat); /* FIXME: Calculate check_value. */ if (!err) err = attr_one (attr, &attr_count, CKA_CHECK_VALUE, &obj_check_value, sizeof obj_check_value); #if 0 if (time_to_ck_date (&cert->timestamp, &obj_start_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_START_DATE, &obj_start_date, sizeof obj_start_date); } if (time_to_ck_date (&cert->expires, &obj_end_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_END_DATE, &obj_end_date, sizeof obj_end_date); } #else /* For now, we disable these fields. We can parse them from the certificate just as the other data. However, we would like to avoid parsing the certificates at all, let's see how much functionality we really need in the PKCS#11 token first. */ (void)obj_start_date; (void)obj_end_date; if (!err) err = attr_empty (attr, &attr_count, CKA_START_DATE); if (!err) err = attr_empty (attr, &attr_count, CKA_END_DATE); #endif /* Note: This attribute is mandatory. Without it, Firefox client authentication won't work. */ if (!err) err = attr_one (attr, &attr_count, CKA_SUBJECT, subject_start, subject_len); if (!err) err = attr_one (attr, &attr_count, CKA_ID, (void *)grip, strlen (grip)); if (!err) err = attr_one (attr, &attr_count, CKA_ISSUER, issuer_start, issuer_len); if (!err) err = attr_one (attr, &attr_count, CKA_SERIAL_NUMBER, serial_start, serial_len); if (!err) err = attr_one (attr, &attr_count, CKA_VALUE, cert->cert_der, cert->cert_der_len); if (!err) err = attr_empty (attr, &attr_count, CKA_URL); if (!err) err = attr_empty (attr, &attr_count, CKA_HASH_OF_SUBJECT_PUBLIC_KEY); if (!err) err = attr_empty (attr, &attr_count, CKA_HASH_OF_ISSUER_PUBLIC_KEY); if (!err) err = attr_one (attr, &attr_count, CKA_JAVA_MIDP_SECURITY_DOMAIN, &obj_java_midp_sec_domain, sizeof obj_java_midp_sec_domain); if (err) { DEBUG (DBG_INFO, "could not build certificate object: %s", gpg_strerror (err)); scute_attr_free (attr, attr_count); return err; } /* FIXME: Not completely safe. */ assert (NR_ATTR_CERT >= attr_count); *attrp = attr; *attr_countp = attr_count; return 0; } gpg_error_t scute_attr_prv (struct cert *cert, const char *grip, CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp) { CK_RV err = 0; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; unsigned char *subject_start; int subject_len; unsigned char *modulus_start; int modulus_len; unsigned char *public_exp_start; int public_exp_len; CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; CK_BBOOL obj_token = CK_TRUE; CK_BBOOL obj_private = CK_FALSE; CK_BBOOL obj_modifiable = CK_FALSE; CK_KEY_TYPE obj_key_type = CKK_RSA; CK_DATE obj_start_date; CK_DATE obj_end_date; CK_BBOOL obj_derive = CK_FALSE; CK_BBOOL obj_local = CK_FALSE; /* FIXME: Unknown. */ - /* FIXME: appropriate machanism should be chosen. */ - /* FIXME: CKM_EC_KEY_PAIR_GEN - CKM_EC_EDWARDS_KEY_PAIR_GEN - CKM_EC_MONTGOMERY_KEY_PAIR_GEN - */ - CK_MECHANISM_TYPE obj_key_gen = CKM_RSA_PKCS_KEY_PAIR_GEN; - CK_MECHANISM_TYPE obj_mechanisms[] = { CKM_RSA_PKCS }; - + CK_MECHANISM_TYPE obj_key_gen; + CK_MECHANISM_TYPE obj_mechanisms[1]; CK_BBOOL obj_sensitive = CK_TRUE; CK_BBOOL obj_decrypt = CK_FALSE; /* Updated below. */ CK_BBOOL obj_sign = CK_FALSE; /* Updated below. */ CK_BBOOL obj_sign_recover = CK_FALSE; CK_BBOOL obj_unwrap = CK_FALSE; CK_BBOOL obj_extractable = CK_FALSE; CK_BBOOL obj_always_sensitive = CK_TRUE; CK_BBOOL obj_never_extractable = CK_TRUE; CK_BBOOL obj_wrap_with_trusted = CK_FALSE; CK_BBOOL obj_always_authenticate = CK_FALSE; + if (cert->pubkey_algo == 1) /* GCRY_PK_RSA==1 from gpgsm */ + { + obj_key_gen = CKM_RSA_PKCS_KEY_PAIR_GEN; + obj_mechanisms[0] = CKM_RSA_PKCS; + } + else if (cert->pubkey_algo == 18) /* GCRY_PK_ECC==18 from gpgsm */ + { + obj_key_gen = CKM_EC_KEY_PAIR_GEN; + if (cert->length == 256) + obj_mechanisms[0] = CKM_ECDSA_SHA256; + else if (cert->length == 384) + obj_mechanisms[0] = CKM_ECDSA_SHA384; + else /* if (cert->length == 512) */ + obj_mechanisms[0] = CKM_ECDSA_SHA512; + } + else + { + obj_key_gen = CKM_EC_EDWARDS_KEY_PAIR_GEN; + obj_mechanisms[0] = CKM_EDDSA; + } + obj_sign = CK_TRUE; err = asn1_get_subject (cert->cert_der, cert->cert_der_len, &subject_start, &subject_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get subject: %s", gpg_strerror (err)); return err; } err = asn1_get_modulus (cert->cert_der, cert->cert_der_len, &modulus_start, &modulus_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get modulus: %s", gpg_strerror (err)); return err; } err = asn1_get_public_exp (cert->cert_der, cert->cert_der_len, &public_exp_start, &public_exp_len); if (err) { DEBUG (DBG_INFO, "rejecting certificate: could not get public exp: %s", gpg_strerror (err)); return err; } #define NR_ATTR_PRV 27 attr = malloc (sizeof (CK_ATTRIBUTE) * NR_ATTR_PRV); attr_count = 0; if (!attr) { DEBUG (DBG_INFO, "out of core"); return gpg_error (GPG_ERR_ENOMEM); } if (!err) err = attr_one (attr, &attr_count, CKA_CLASS, &obj_class, sizeof obj_class); if (!err) err = attr_one (attr, &attr_count, CKA_TOKEN, &obj_token, sizeof obj_token); if (!err) err = attr_one (attr, &attr_count, CKA_PRIVATE, &obj_private, sizeof obj_private); if (!err) err = attr_one (attr, &attr_count, CKA_MODIFIABLE, &obj_modifiable, sizeof obj_modifiable); if (!err) err = attr_one (attr, &attr_count, CKA_LABEL, "Scute", 5); if (!err) err = attr_one (attr, &attr_count, CKA_KEY_TYPE, &obj_key_type, sizeof obj_key_type); if (!err) err = attr_one (attr, &attr_count, CKA_ID, (void *)grip, strlen (grip)); #if 0 /* For now, we disable these fields. We can parse them from the certificate just as the other data. However, we would like to avoid parsing the certificates at all, let's see how much functionality we really need in the PKCS#11 token first. */ /* This code currently only works for certificates retrieved through gpgsm. */ if (time_to_ck_date (&cert->timestamp, &obj_start_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_START_DATE, &obj_start_date, sizeof obj_start_date); } if (time_to_ck_date (&cert->expires, &obj_end_date)) { if (!err) err = attr_one (attr, &attr_count, CKA_END_DATE, &obj_end_date, sizeof obj_end_date); } #else /* For now, we disable these fields. We can parse them from the certificate just as the other data. However, we would like to avoid parsing the certificates at all, let's see how much functionality we really need in the PKCS#11 token first. */ (void)obj_start_date; (void)obj_end_date; if (!err) err = attr_empty (attr, &attr_count, CKA_START_DATE); if (!err) err = attr_empty (attr, &attr_count, CKA_END_DATE); #endif if (!err) err = attr_one (attr, &attr_count, CKA_DERIVE, &obj_derive, sizeof obj_derive); if (!err) err = attr_one (attr, &attr_count, CKA_LOCAL, &obj_local, sizeof obj_local); if (!err) err = attr_one (attr, &attr_count, CKA_KEY_GEN_MECHANISM, &obj_key_gen, sizeof obj_key_gen); if (!err) err = attr_one (attr, &attr_count, CKA_ALLOWED_MECHANISMS, &obj_mechanisms, sizeof obj_mechanisms); if (!err) err = attr_one (attr, &attr_count, CKA_SUBJECT, subject_start, subject_len); if (!err) err = attr_one (attr, &attr_count, CKA_SENSITIVE, &obj_sensitive, sizeof obj_sensitive); if (!err) err = attr_one (attr, &attr_count, CKA_DECRYPT, &obj_decrypt, sizeof obj_decrypt); if (!err) err = attr_one (attr, &attr_count, CKA_SIGN, &obj_sign, sizeof obj_sign); if (!err) err = attr_one (attr, &attr_count, CKA_SIGN_RECOVER, &obj_sign_recover, sizeof obj_sign_recover); if (!err) err = attr_one (attr, &attr_count, CKA_UNWRAP, &obj_unwrap, sizeof obj_unwrap); if (!err) err = attr_one (attr, &attr_count, CKA_EXTRACTABLE, &obj_extractable, sizeof obj_extractable); if (!err) err = attr_one (attr, &attr_count, CKA_ALWAYS_SENSITIVE, &obj_always_sensitive, sizeof obj_always_sensitive); if (!err) err = attr_one (attr, &attr_count, CKA_NEVER_EXTRACTABLE, &obj_never_extractable, sizeof obj_never_extractable); if (!err) err = attr_one (attr, &attr_count, CKA_WRAP_WITH_TRUSTED, &obj_wrap_with_trusted, sizeof obj_wrap_with_trusted); if (!err) err = attr_empty (attr, &attr_count, CKA_UNWRAP_TEMPLATE); if (!err) err = attr_one (attr, &attr_count, CKA_ALWAYS_AUTHENTICATE, &obj_always_authenticate, sizeof obj_always_authenticate); /* FIXME: appropriate objects should be provided. */ + if (cert->pubkey_algo == 1) + { + if (!err) + err = attr_one (attr, &attr_count, CKA_MODULUS, + modulus_start, modulus_len); + if (!err) + err = attr_one (attr, &attr_count, CKA_PUBLIC_EXPONENT, + public_exp_start, public_exp_len); + } /* FIXME: CKA_EC_POINT, CKA_EC_PARAMS */ - if (!err) - err = attr_one (attr, &attr_count, CKA_MODULUS, - modulus_start, modulus_len); - if (!err) - err = attr_one (attr, &attr_count, CKA_PUBLIC_EXPONENT, - public_exp_start, public_exp_len); if (err) { DEBUG (DBG_INFO, "could not build private certificate object: %s", gpg_strerror (err)); scute_attr_free (attr, attr_count); return err; } +#if 0 /* FIXME: Not completely safe. */ assert (NR_ATTR_PRV >= attr_count); +#endif *attrp = attr; *attr_countp = attr_count; return 0; } diff --git a/src/slots.c b/src/slots.c index a7241d6..f9a6524 100644 --- a/src/slots.c +++ b/src/slots.c @@ -1,1153 +1,1196 @@ /* slots.c - Slot management. * Copyright (C) 2006, 2019 g10 Code GmbH * * This file is part of Scute. * * Scute is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Scute is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "cryptoki.h" #include "table.h" #include "error-mapping.h" #include "slots.h" #include "agent.h" #include "support.h" #include "gpgsm.h" #include "debug.h" /* A session is just a slot identifier with a per-slot session identifier. */ /* Must be power of two. */ #define SLOT_MAX (1 << 15) #define SESSION_SLOT_MASK (SLOT_MAX - 1) #define SESSION_SLOT_SHIFT 16 #define SESSION_MAX (1 << SESSION_SLOT_SHIFT) #define SESSION_ID_MASK (SESSION_MAX - 1) /* Get slot ID from session. */ #define SESSION_SLOT(session) \ ((session >> SESSION_SLOT_SHIFT) & SESSION_SLOT_MASK) /* Get session ID from session. */ #define SESSION_ID(session) (session & SESSION_ID_MASK) /* Because the slot is already 1-based, we can make the session 0-based. */ #define SESSION_BUILD_ID(slot, session) \ (((slot & SESSION_SLOT_MASK) << SESSION_SLOT_SHIFT) \ | (session & SESSION_ID_MASK)) /* We use one-based IDs. */ #define OBJECT_ID_TO_IDX(id) (id - 1) #define OBJECT_IDX_TO_ID(idx) (idx + 1) struct object { CK_ATTRIBUTE_PTR attributes; CK_ULONG attributes_count; }; /* A mechanism. */ struct mechanism { CK_MECHANISM_TYPE type; CK_MECHANISM_INFO info; }; /* We use one-based IDs. */ #define MECHANISM_ID_TO_IDX(id) (id - 1) #define MECHANISM_IDX_TO_ID(idx) (idx + 1) /* The session state. */ struct session { /* True iff read-write session. */ bool rw; /* The list of objects for the current search. */ object_iterator_t *search_result; /* The length of the list of objects for the current search. */ int search_result_len; /* The signing key. */ CK_OBJECT_HANDLE signing_key; /* The signing mechanism type. */ CK_MECHANISM_TYPE signing_mechanism_type; /* The decryption key. */ CK_OBJECT_HANDLE decryption_key; }; /* The slot status. */ typedef enum { SLOT_STATUS_USED = 0, SLOT_STATUS_DEAD = 1 } slot_status_t; struct slot { /* The slot status. Starts out as 0 (pristine). */ slot_status_t status; /* The slot login status. Starts out as 0 (public). */ slot_login_t login; /* True iff a token is present. */ bool token_present; /* The supported mechanisms. */ scute_table_t mechanisms; /* The sessions. */ scute_table_t sessions; /* The objects on the token. */ scute_table_t objects; /* The keygrip to the key in hex string. */ char grip[41]; /* The serial number. */ char serialno[33]; }; /* The slot table. */ static scute_table_t slot_table; /* Deallocator for mechanisms. */ static void mechanism_dealloc (void *data) { free (data); } /* Allocator for mechanisms. The hook must be a pointer to a CK_FLAGS that should be a combination of CKF_SIGN and/or CKF_DECRYPT. */ static gpg_error_t mechanism_alloc (void **data_r, void *hook) { struct mechanism *mechanism; struct slot *slot = hook; + int oid; + CK_MECHANISM_TYPE m = CKM_VENDOR_DEFINED; + + /* Register the signing mechanism. */ + + oid = scute_table_first (slot->objects); + while (!scute_table_last (slot->objects, oid)) + { + struct object *object; + CK_ATTRIBUTE_PTR attr; + CK_ULONG attr_count; + CK_ULONG i; + + object = scute_table_data (slot->objects, oid); + if (!object) + { + attr = NULL; + break; + } + + attr = object->attributes; + attr_count = object->attributes_count; + + for (i = 0; i < attr_count; i++) + if (attr[i].type == CKA_ALLOWED_MECHANISMS) + break; + + if (i != attr_count) + { + if (attr[i].ulValueLen == sizeof (CK_MECHANISM_TYPE)) + memcpy (&m, attr[i].pValue, attr[i].ulValueLen); + break; + } + + oid = scute_table_next (slot->objects, oid); + } + + if (m == CKM_VENDOR_DEFINED) + return gpg_error (GPG_ERR_BAD_DATA); mechanism = calloc (1, sizeof (*mechanism)); if (mechanism == NULL) return gpg_error_from_syserror (); - /* Register the signing mechanism. */ - /* FIXME: examine slot->objects to setup the mechanism. */ - (void)slot; - - mechanism->type = CKM_RSA_PKCS; - mechanism->info.ulMinKeySize = 1024; - mechanism->info.ulMaxKeySize = 4096; + mechanism->type = m; + if (m == CKM_RSA_PKCS) + { + mechanism->info.ulMinKeySize = 1024; + mechanism->info.ulMaxKeySize = 4096; + } + else + { + mechanism->info.ulMinKeySize = 256; + mechanism->info.ulMaxKeySize = 512; + } mechanism->info.flags = CKF_HW | CKF_SIGN; *data_r = mechanism; return 0; } static void object_dealloc (void *data) { struct object *obj = data; while (0 < obj->attributes_count--) free (obj->attributes[obj->attributes_count].pValue); free (obj->attributes); free (obj); } /* Allocator for objects. The hook is currently unused. */ static gpg_error_t object_alloc (void **data_r, void *hook) { struct object *object; (void) hook; object = calloc (1, sizeof (*object)); if (object == NULL) return gpg_error_from_syserror (); *data_r = object; return 0; } static void session_dealloc (void *data) { struct session *session = data; if (session->search_result) free (session->search_result); free (session); } /* Allocator for sessions. The hook is currently unused. */ static gpg_error_t session_alloc (void **data_r, void *hook) { struct session *session; (void) hook; session = calloc (1, sizeof (*session)); if (session == NULL) return gpg_error_from_syserror (); *data_r = session; return 0; } /* Deallocator for slots. */ static void slot_dealloc (void *data) { struct slot *slot = data; scute_table_destroy (slot->sessions); scute_table_destroy (slot->mechanisms); scute_table_destroy (slot->objects); free (slot); } /* Allocator for slots. The hook does not indicate anything at this point. */ static gpg_error_t slot_alloc (void **data_r, void *hook) { gpg_error_t err; struct slot *slot; (void) hook; slot = calloc (1, sizeof (*slot)); if (slot == NULL) return gpg_error_from_syserror (); err = scute_table_create (&slot->mechanisms, mechanism_alloc, mechanism_dealloc); if (err) goto slot_alloc_out; err = scute_table_create (&slot->sessions, session_alloc, session_dealloc); if (err) goto slot_alloc_out; err = scute_table_create (&slot->objects, object_alloc, object_dealloc); if (err) goto slot_alloc_out; slot->status = SLOT_STATUS_USED; slot->token_present = false; slot->login = SLOT_LOGIN_PUBLIC; *data_r = slot; slot_alloc_out: if (err) slot_dealloc (slot); return err; } static gpg_error_t add_object (void *hook, CK_ATTRIBUTE_PTR attrp, CK_ULONG attr_countp); static gpg_error_t slot_init (slot_iterator_t id); /* Initialize the slot list. */ CK_RV scute_slots_initialize (void) { gpg_error_t err; struct keyinfo *keyinfo = NULL; struct keyinfo *ki; err = scute_agent_serialno (); if (err) { if (gpg_err_code (err) != GPG_ERR_ENODEV) return scute_gpg_err_to_ck (err); } err = scute_agent_keyinfo_list (&keyinfo); if (err) return scute_gpg_err_to_ck (err); err = scute_table_create (&slot_table, slot_alloc, slot_dealloc); if (err) return err; for (ki = keyinfo; ki; ki = ki->next) { struct slot *slot; int slot_idx; err = scute_table_alloc (slot_table, &slot_idx, (void **)&slot, NULL); if (err) { scute_slots_finalize (); break; } else { memset (slot->serialno, 0, sizeof (slot->serialno)); if (ki->serialno) { int len; len = strlen (ki->serialno); if (len >= 32) memcpy (slot->serialno, &ki->serialno[len-32], 32); else memcpy (slot->serialno, ki->serialno, len); } memcpy (slot->grip, ki->grip, 41); err = scute_gpgsm_get_cert (slot->grip, add_object, slot); if (!err) err = slot_init (slot_idx); if (err) { scute_table_dealloc (slot_table, &slot_idx); err = 0; } } } scute_agent_free_keyinfo (keyinfo); return scute_gpg_err_to_ck (err); } void scute_slots_finalize (void) { if (!slot_table) return; /* This recursively releases all slots and any objects associated with them. */ scute_table_destroy (slot_table); slot_table = NULL; } /* Reset the slot SLOT after the token has been removed. */ static void slot_reset (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); int oid; /* This also resets the login state. */ slot_close_all_sessions (id); oid = scute_table_first (slot->objects); while (!scute_table_last (slot->objects, oid)) scute_table_dealloc (slot->objects, &oid); assert (scute_table_used (slot->objects) == 0); slot->token_present = false; } static gpg_error_t add_object (void *hook, CK_ATTRIBUTE_PTR attrp, CK_ULONG attr_countp) { gpg_error_t err; struct slot *slot = hook; struct object *object; unsigned int oidx; void *objp; err = scute_table_alloc (slot->objects, &oidx, &objp, NULL); if (err) return err; object = objp; object->attributes = attrp; object->attributes_count = attr_countp; return 0; } /* Initialize the slot after a token has been inserted. */ static gpg_error_t slot_init (slot_iterator_t id) { gpg_error_t err = 0; struct slot *slot = scute_table_data (slot_table, id); int idx; /* FIXME: Perform the rest of the initialization of the token. */ slot->token_present = true; err = scute_table_alloc (slot->mechanisms, &idx, NULL, slot); if (err) slot_reset (id); return err; } /* Begin iterating over the list of slots. */ CK_RV slots_iterate_first (slot_iterator_t *slot) { *slot = scute_table_first (slot_table); return CKR_OK; } /* Continue iterating over the list of slots. */ CK_RV slots_iterate_next (slot_iterator_t *slot) { *slot = scute_table_next (slot_table, *slot); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool slots_iterate_last (slot_iterator_t *slot) { return scute_table_last (slot_table, *slot); } /* Acquire the slot for the slot ID ID. */ CK_RV slots_lookup (CK_SLOT_ID id, slot_iterator_t *id_r) { struct slot *slot = scute_table_data (slot_table, id); if (slot == NULL) return CKR_SLOT_ID_INVALID; *id_r = id; return CKR_OK; } /* Return true iff a token is present in slot SLOT. */ bool slot_token_present (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); return slot->token_present; } /* Return the token label. We use the dispserialno here too because * Firefox prints that value in the prompt ("Stored at:"). */ const char * slot_token_label (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); return slot->serialno; } /* Get the manufacturer of the token. */ const char * slot_token_manufacturer (slot_iterator_t id) { /* FIXME */ (void)id; return "scdaemon"; } /* Get the application used on the token. */ const char * slot_token_application (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); if (!slot) return "[ooops]"; /* slots_update() makes sure this is correct. */ /* FIXME */ return "gpg-agent"; } /* Get the LSB of serial number of the token. */ const char * slot_token_serial (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); return &slot->serialno[16]; } /* Get the manufacturer of the token. */ void slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor, CK_BYTE *fw_major, CK_BYTE *fw_minor) { /* FIXME */ (void)id; *hw_major = 0; *hw_minor = 0; *fw_major = 0; *fw_minor = 0; } /* Get the maximum and minimum pin length. */ void slot_token_maxpinlen (slot_iterator_t id, CK_ULONG *max, CK_ULONG *min) { /* FIXME */ (void)id; *max = 31; /* FIXME: This is true at least for the user pin (CHV1 and CHV2). */ *min = 6; } /* Get the maximum and the actual pin count. */ void slot_token_pincount (slot_iterator_t id, int *max, int *len) { (void)id; *max = 3; /* FIXME */ *len = 1; } /* Return the ID of slot SLOT. */ CK_SLOT_ID slot_get_id (slot_iterator_t slot) { return slot; } /* Return true if the token supports the GET CHALLENGE operation. */ bool slot_token_has_rng (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); (void)slot; /* FIXME */ return 1; } /* Mechanism management. */ /* Begin iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_first (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slot_table, id); *mechanism = scute_table_first (slot->mechanisms); return CKR_OK; } /* Continue iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_next (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slot_table, id); *mechanism = scute_table_next (slot->mechanisms, *mechanism); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool mechanisms_iterate_last (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slot_table, id); return scute_table_last (slot->mechanisms, *mechanism); } /* Acquire the mechanism TYPE for the slot id ID. */ CK_RV mechanisms_lookup (slot_iterator_t id, mechanism_iterator_t *mid_r, CK_MECHANISM_TYPE type) { struct slot *slot = scute_table_data (slot_table, id); int mid = scute_table_first (slot->mechanisms); while (!scute_table_last (slot->mechanisms, mid)) { struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); if (mechanism->type == type) { *mid_r = mid; return CKR_OK; } mid = scute_table_next (slot->mechanisms, mid); } return CKR_MECHANISM_INVALID; } /* Return the type of mechanism MID in slot ID. */ CK_MECHANISM_TYPE mechanism_get_type (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slot_table, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return mechanism->type; } /* Return the info of mechanism MID. */ CK_MECHANISM_INFO_PTR mechanism_get_info (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slot_table, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return &mechanism->info; } /* Session management. */ static int active_sessions; /* Create a new session. */ CK_RV slot_create_session (slot_iterator_t id, session_iterator_t *session, bool rw) { int err; struct slot *slot = scute_table_data (slot_table, id); unsigned int tsid; void *rawp; struct session *session_p; assert (slot); if (scute_table_used (slot->sessions) == SESSION_MAX) return CKR_SESSION_COUNT; if (slot->login == SLOT_LOGIN_SO && !rw) return CKR_SESSION_READ_WRITE_SO_EXISTS; err = scute_table_alloc (slot->sessions, &tsid, &rawp, NULL); if (err) return scute_sys_to_ck (err); session_p = rawp; session_p->rw = rw; session_p->search_result = NULL; session_p->search_result_len = 0; session_p->signing_key = CK_INVALID_HANDLE; session_p->decryption_key = CK_INVALID_HANDLE; *session = SESSION_BUILD_ID (id, tsid); active_sessions++; return CKR_OK; } /* Look up session. */ CK_RV slots_lookup_session (CK_SESSION_HANDLE sid, slot_iterator_t *id, session_iterator_t *session_id) { CK_RV err; unsigned int idx = SESSION_SLOT (sid); unsigned session_idx = SESSION_ID (sid); struct slot *slot; /* Verify the slot. */ err = slots_lookup (SESSION_SLOT (sid), id); if (err) return err; *session_id = session_idx; /* Verify the session. */ slot = scute_table_data (slot_table, idx); if (!scute_table_data (slot->sessions, session_idx)) return CKR_SESSION_HANDLE_INVALID; return 0; } /* Close the session. */ CK_RV slot_close_session (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slot_table, id); scute_table_dealloc (slot->sessions, &sid); /* At last session closed, return to public sessions. */ if (!scute_table_used (slot->sessions)) slot->login = SLOT_LOGIN_PUBLIC; active_sessions--; return CKR_OK; } /* Close all sessions. */ CK_RV slot_close_all_sessions (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); int sid = scute_table_first (slot->sessions); while (!scute_table_last (slot->sessions, sid)) { slot_close_session (id, sid); sid = scute_table_next (slot->sessions, sid); } assert (scute_table_used (slot->sessions) == 0); return CKR_OK; } /* Get the RW flag from the session SID in slot ID. */ bool session_get_rw (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slot_table, id); struct session *session = scute_table_data (slot->sessions, sid); return session->rw; } /* Get the login state from the slot ID. */ slot_login_t slot_get_login_status (slot_iterator_t id) { struct slot *slot = scute_table_data (slot_table, id); return slot->login; } /* Object management. */ /* Begin iterating over the list of objects. */ CK_RV objects_iterate_first (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slot_table, id); *object = scute_table_first (slot->objects); return CKR_OK; } /* Continue iterating over the list of objects. */ CK_RV objects_iterate_next (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slot_table, id); *object = scute_table_next (slot->objects, *object); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool objects_iterate_last (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slot_table, id); return scute_table_last (slot->objects, *object); } /* Return the max. number of objects in the slot. May overcount somewhat. */ CK_RV slot_get_object_count (slot_iterator_t id, int *nr) { struct slot *slot = scute_table_data (slot_table, id); *nr = scute_table_used (slot->objects); return CKR_OK; } /* Get the object information for object OBJECT_ID in slot ID. */ CK_RV slot_get_object (slot_iterator_t id, object_iterator_t oid, CK_ATTRIBUTE_PTR *obj, CK_ULONG *obj_count) { struct slot *slot = scute_table_data (slot_table, id); struct object *object = scute_table_data (slot->objects, oid); if (!object) return CKR_OBJECT_HANDLE_INVALID; *obj = object->attributes; *obj_count = object->attributes_count; return 0; } /* Set the result of a search for session SID in slot ID to SEARCH_RESULT and SEARCH_RESULT_LEN. */ CK_RV session_set_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t *search_result, int search_result_len) { struct slot *slot = scute_table_data (slot_table, id); struct session *session = scute_table_data (slot->sessions, sid); if (session->search_result && session->search_result != search_result) free (session->search_result); session->search_result = search_result; session->search_result_len = search_result_len; return 0; } /* Get the stored search result for the session SID in slot ID. */ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t **search_result, int *search_result_len) { struct slot *slot = scute_table_data (slot_table, id); struct session *session = scute_table_data (slot->sessions, sid); assert (search_result); assert (search_result_len); *search_result = session->search_result; *search_result_len = session->search_result_len; return 0; } /* Set the signing key for session SID in slot ID to KEY. This is the * core of C_SignInit. */ CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid, object_iterator_t key, CK_MECHANISM_TYPE mechanism_type) { struct slot *slot = scute_table_data (slot_table, id); struct session *session = scute_table_data (slot->sessions, sid); CK_RV err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; err = slot_get_object (id, key, &attr, &attr_count); if (err) return err; while (attr_count-- > 0) if (attr->type == CKA_CLASS) break; if (attr_count == (CK_ULONG) -1) return CKR_KEY_HANDLE_INVALID; if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) return CKR_KEY_HANDLE_INVALID; /* It's the private RSA key object. */ session->signing_key = key; session->signing_mechanism_type = mechanism_type; return 0; } /* The core of C_Sign - see there for a description. */ CK_RV session_sign (slot_iterator_t id, session_iterator_t sid, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { struct slot *slot = scute_table_data (slot_table, id); struct session *session = scute_table_data (slot->sessions, sid); int rc; gpg_error_t err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; unsigned int sig_len; CK_BYTE key_id[100]; int i; if (!session->signing_key) return CKR_OPERATION_NOT_INITIALIZED; rc = slot_get_object (id, session->signing_key, &attr, &attr_count); if (rc) goto leave; if (attr_count == (CK_ULONG) -1) { rc = CKR_KEY_HANDLE_INVALID; goto leave; } if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) { rc = CKR_KEY_HANDLE_INVALID; goto leave; } /* Find the CKA_ID */ for (i = 0; i < attr_count; i++) if (attr[i].type == CKA_ID) break; if (i == attr_count) { rc = CKR_GENERAL_ERROR; goto leave; } if (attr[i].ulValueLen >= sizeof key_id - 1) { rc = CKR_GENERAL_ERROR; goto leave; } strncpy (key_id, attr[i].pValue, attr[i].ulValueLen); key_id[attr[i].ulValueLen] = 0; DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id); sig_len = *pulSignatureLen; err = scute_agent_sign (key_id, session->signing_mechanism_type, pData, ulDataLen, pSignature, &sig_len); /* Take care of error codes which are not mapped by default. */ if (gpg_err_code (err) == GPG_ERR_INV_LENGTH) rc = CKR_BUFFER_TOO_SMALL; else if (gpg_err_code (err) == GPG_ERR_INV_ARG) rc = CKR_ARGUMENTS_BAD; else rc = scute_gpg_err_to_ck (err); /* Return the length. */ if (rc == CKR_OK || rc == CKR_BUFFER_TOO_SMALL) *pulSignatureLen = sig_len; leave: if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL) session->signing_key = 0; return rc; } /* Prepare a decryption for slot with SLOTID and the session SID using * MECHANISM and KEY. This is the core of C_DecryptInit. */ CK_RV session_init_decrypt (slot_iterator_t slotid, session_iterator_t sid, CK_MECHANISM *mechanism, object_iterator_t key) { struct slot *slot; struct session *session; CK_RV rv; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; if (mechanism->mechanism != CKM_RSA_PKCS) return CKR_MECHANISM_INVALID; slot = scute_table_data (slot_table, slotid); session = scute_table_data (slot->sessions, sid); rv = slot_get_object (slotid, key, &attr, &attr_count); if (rv) return rv; /* FIXME: What kind of strange loop is this? */ while (attr_count-- > 0) if (attr->type == CKA_CLASS) break; if (attr_count == (CK_ULONG) -1) return CKR_KEY_HANDLE_INVALID; if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) return CKR_KEY_HANDLE_INVALID; /* It's the private RSA key object. */ session->decryption_key = key; return 0; } /* The core of C_Decrypt - see there for a description. */ CK_RV session_decrypt (slot_iterator_t slotid, session_iterator_t sid, CK_BYTE *encdata, CK_ULONG encdatalen, CK_BYTE *r_plaindata, CK_ULONG *r_plaindatalen) { CK_RV rv; gpg_error_t err; struct slot *slot; struct session *session; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; CK_BYTE key_id[100]; int i; unsigned int plaindatalen; slot = scute_table_data (slot_table, slotid); session = scute_table_data (slot->sessions, sid); if (!session->decryption_key) return CKR_OPERATION_NOT_INITIALIZED; rv = slot_get_object (slotid, session->decryption_key, &attr, &attr_count); if (rv) goto leave; if (attr_count == (CK_ULONG) -1) { rv = CKR_KEY_HANDLE_INVALID; goto leave; } if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) { rv = CKR_KEY_HANDLE_INVALID; goto leave; } /* Find the CKA_ID */ for (i = 0; i < attr_count; i++) if (attr[i].type == CKA_ID) break; if (i == attr_count) { rv = CKR_GENERAL_ERROR; goto leave; } if (attr[i].ulValueLen >= sizeof key_id - 1) { rv = CKR_GENERAL_ERROR; goto leave; } strncpy (key_id, attr[i].pValue, attr[i].ulValueLen); key_id[attr[i].ulValueLen] = 0; DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id); plaindatalen = *r_plaindatalen; err = scute_agent_decrypt (key_id, encdata, encdatalen, r_plaindata, &plaindatalen); DEBUG (DBG_INFO, "agent returned gpg error %d datalen=%u", err, plaindatalen); /* Take care of error codes which are not mapped by default. */ if (gpg_err_code (err) == GPG_ERR_INV_LENGTH) rv = CKR_BUFFER_TOO_SMALL; else if (gpg_err_code (err) == GPG_ERR_INV_ARG) rv = CKR_ARGUMENTS_BAD; else rv = scute_gpg_err_to_ck (err); /* Return the length. */ if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) *r_plaindatalen = plaindatalen; leave: if (rv != CKR_BUFFER_TOO_SMALL) session->decryption_key = 0; DEBUG (DBG_INFO, "leaving decrypt with rv=%lu", rv); return rv; } CK_RV scute_slots_rescan_if_no_sessions (void) { if (active_sessions == 0) { scute_slots_finalize (); scute_slots_initialize (); } return CKR_OK; }