From c9f443454e160ff778e2c91b786200cfd1cc2af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Love=20H=C3=B6rnquist=20=C3=85strand?= Date: Mon, 14 Jan 2008 20:53:56 +0000 Subject: [PATCH] Add a PKCS11 provider supporting signing and verifing sigatures. git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@22435 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/hx509/ChangeLog | 7 + lib/hx509/Makefile.am | 18 +- lib/hx509/cert.c | 7 + lib/hx509/crypto.c | 198 ++- lib/hx509/softp11.c | 2017 +++++++++++++++++++++++++++++++ lib/hx509/test_soft_pkcs11.c | 213 ++++ lib/hx509/tst-crypto-available1 | 2 +- lib/hx509/tst-crypto-available3 | 2 +- lib/hx509/version-script.map | 2 + 9 files changed, 2396 insertions(+), 70 deletions(-) create mode 100644 lib/hx509/softp11.c create mode 100644 lib/hx509/test_soft_pkcs11.c diff --git a/lib/hx509/ChangeLog b/lib/hx509/ChangeLog index 224eaafc9..7eb0b309c 100644 --- a/lib/hx509/ChangeLog +++ b/lib/hx509/ChangeLog @@ -1,5 +1,12 @@ +2008-01-14 Love Hörnquist Åstrand + + * Add a PKCS11 provider supporting signing and verifing sigatures. + 2008-01-13 Love Hörnquist Åstrand + * version-script.map: Replace hx509_name_to_der_name with + hx509_name_binary. + * print.c: make print_func static 2007-12-26 Love Hörnquist Åstrand diff --git a/lib/hx509/Makefile.am b/lib/hx509/Makefile.am index e55dae216..b985d03fa 100644 --- a/lib/hx509/Makefile.am +++ b/lib/hx509/Makefile.am @@ -76,6 +76,7 @@ dist_libhx509_la_SOURCES = \ name.c \ peer.c \ print.c \ + softp11.c \ ref/pkcs11.h \ req.c \ revoke.c @@ -117,7 +118,7 @@ crmf_asn1_files: $(asn1_compile) $(srcdir)/crmf.asn1 $(libhx509_la_OBJECTS): $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h $(srcdir)/hx509-protos.h: - cd $(srcdir) && perl ../../cf/make-proto.pl -E HX509_LIB_FUNCTION -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h + cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB_FUNCTION -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h $(srcdir)/hx509-private.h: cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h @@ -161,7 +162,8 @@ CLEANFILES = $(BUILT_SOURCES) \ cert-proxy.der cert-ca.der cert-ee.der pkcs10-request.der \ wca.pem wuser.pem wdc.pem wcrl.crl \ random-data statfile crl.crl \ - test + test \ + test-rc-file.rc clean-local: @echo "cleaning PKITS" ; rm -rf PKITS_data @@ -171,10 +173,13 @@ clean-local: # check_SCRIPTS = $(SCRIPT_TESTS) -check_PROGRAMS = $(PROGRAM_TESTS) +check_PROGRAMS = $(PROGRAM_TESTS) test_soft_pkcs11 LDADD = libhx509.la +test_soft_pkcs11_LDADD = libhx509.la +test_soft_pkcs11_CPPFLAGS = -I$(srcdir)/ref + TESTS = $(SCRIPT_TESTS) $(PROGRAM_TESTS) PROGRAM_TESTS = \ @@ -188,6 +193,7 @@ SCRIPT_TESTS = \ test_crypto \ test_nist \ test_nist2 \ + test_pkcs11 \ test_nist_cert \ test_nist_pkcs12 \ test_req \ @@ -232,6 +238,11 @@ test_nist2: test_nist2.in Makefile chmod +x test_nist2.tmp mv test_nist2.tmp test_nist2 +test_pkcs11: test_pkcs11.in Makefile + $(do_subst) < $(srcdir)/test_pkcs11.in > test_pkcs11.tmp + chmod +x test_pkcs11.tmp + mv test_pkcs11.tmp test_pkcs11 + test_nist_cert: test_nist_cert.in Makefile $(do_subst) < $(srcdir)/test_nist_cert.in > test_nist_cert.tmp chmod +x test_nist_cert.tmp @@ -274,6 +285,7 @@ EXTRA_DIST = \ test_nist2.in \ test_nist_cert.in \ test_nist_pkcs12.in \ + test_pkcs11.in \ test_query.in \ test_req.in \ test_windows.in \ diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c index 287c94ea6..4cdeb2a98 100644 --- a/lib/hx509/cert.c +++ b/lib/hx509/cert.c @@ -1474,6 +1474,13 @@ _hx509_cert_private_key(hx509_cert p) return p->private_key; } +int +hx509_cert_have_private_key(hx509_cert p) +{ + return p->private_key ? 1 : 0; +} + + int _hx509_cert_private_key_exportable(hx509_cert p) { diff --git a/lib/hx509/crypto.c b/lib/hx509/crypto.c index 92e60c31f..7876a9b93 100644 --- a/lib/hx509/crypto.c +++ b/lib/hx509/crypto.c @@ -64,6 +64,7 @@ struct hx509_private_key_ops { int (*generate_private_key)(hx509_context, struct hx509_generate_private_context *, hx509_private_key); + BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *); int (*handle_alg)(const hx509_private_key, const AlgorithmIdentifier *, enum crypto_op_type); @@ -115,6 +116,9 @@ struct signature_alg { #define SIG_PUBLIC_SIG 0x200 #define SIG_SECRET 0x400 +#define RA_RSA_USES_DIGEST_INFO 0x1000000 + + int (*verify_signature)(hx509_context context, const struct signature_alg *, const Certificate *, @@ -248,43 +252,57 @@ rsa_verify_signature(hx509_context context, } if (retsize > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); - ret = decode_DigestInfo(to, retsize, &di, &size); - free(to); - if (ret) { - goto out; + + if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) { + + ret = decode_DigestInfo(to, retsize, &di, &size); + free(to); + if (ret) { + goto out; + } + + /* Check for extra data inside the sigature */ + if (size != retsize) { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); + goto out; + } + + if (sig_alg->digest_oid && + der_heim_oid_cmp(&di.digestAlgorithm.algorithm, + (*sig_alg->digest_oid)()) != 0) + { + ret = HX509_CRYPTO_OID_MISMATCH; + hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); + goto out; + } + + /* verify that the parameters are NULL or the NULL-type */ + if (di.digestAlgorithm.parameters != NULL && + (di.digestAlgorithm.parameters->length != 2 || + memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) + { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); + goto out; + } + + ret = _hx509_verify_signature(context, + NULL, + &di.digestAlgorithm, + data, + &di.digest); + } else { + if (retsize != data->length || + memcmp(to, data->data, retsize) != 0) + { + ret = HX509_CRYPTO_SIG_INVALID_FORMAT; + hx509_set_error_string(context, 0, ret, "RSA Signature incorrect"); + goto out; + } + free(to); } - /* Check for extra data inside the sigature */ - if (size != retsize) { - ret = HX509_CRYPTO_SIG_INVALID_FORMAT; - hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); - goto out; - } - - if (sig_alg->digest_oid && - der_heim_oid_cmp(&di.digestAlgorithm.algorithm, - (*sig_alg->digest_oid)()) != 0) - { - ret = HX509_CRYPTO_OID_MISMATCH; - hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); - goto out; - } - - /* verify that the parameters are NULL or the NULL-type */ - if (di.digestAlgorithm.parameters != NULL && - (di.digestAlgorithm.parameters->length != 2 || - memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) - { - ret = HX509_CRYPTO_SIG_INVALID_FORMAT; - hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); - goto out; - } - - ret = _hx509_verify_signature(context, - NULL, - &di.digestAlgorithm, - data, - &di.digest); out: free_DigestInfo(&di); RSA_free(rsa); @@ -303,7 +321,6 @@ rsa_create_signature(hx509_context context, const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; - DigestInfo di; size_t size; int ret; @@ -324,6 +341,8 @@ rsa_create_signature(hx509_context context, digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, oid_id_pkcs1_rsaEncryption()) == 0) { digest_alg = hx509_signature_sha1(); + } else if (der_heim_oid_cmp(sig_oid, oid_id_heim_rsa_pkcs1_x509()) == 0) { + digest_alg = NULL; } else return HX509_ALG_NOT_SUPP; @@ -335,29 +354,34 @@ rsa_create_signature(hx509_context context, } } - memset(&di, 0, sizeof(di)); + if (digest_alg) { + DigestInfo di; + memset(&di, 0, sizeof(di)); - ret = _hx509_create_signature(context, - NULL, - digest_alg, - data, - &di.digestAlgorithm, - &di.digest); - if (ret) - return ret; - ASN1_MALLOC_ENCODE(DigestInfo, - indata.data, - indata.length, - &di, - &size, - ret); - free_DigestInfo(&di); - if (ret) { - hx509_set_error_string(context, 0, ret, "out of memory"); - return ret; + ret = _hx509_create_signature(context, + NULL, + digest_alg, + data, + &di.digestAlgorithm, + &di.digest); + if (ret) + return ret; + ASN1_MALLOC_ENCODE(DigestInfo, + indata.data, + indata.length, + &di, + &size, + ret); + free_DigestInfo(&di); + if (ret) { + hx509_set_error_string(context, 0, ret, "out of memory"); + return ret; + } + if (indata.length != size) + _hx509_abort("internal ASN.1 encoder error"); + } else { + indata = *data; } - if (indata.length != size) - _hx509_abort("internal ASN.1 encoder error"); sig->length = RSA_size(signer->private_key.rsa); sig->data = malloc(sig->length); @@ -371,7 +395,8 @@ rsa_create_signature(hx509_context context, sig->data, signer->private_key.rsa, RSA_PKCS1_PADDING); - der_free_octet_string(&indata); + if (indata.data != data->data) + der_free_octet_string(&indata); if (ret <= 0) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, @@ -517,6 +542,18 @@ rsa_private_key_export(hx509_context context, return 0; } +static BIGNUM * +rsa_get_internal(hx509_context context, hx509_private_key key, const char *type) +{ + if (strcasecmp(type, "rsa-modulus") == 0) { + return BN_dup(key->private_key.rsa->n); + } else if (strcasecmp(type, "rsa-exponent") == 0) { + return BN_dup(key->private_key.rsa->e); + } else + return NULL; +} + + static hx509_private_key_ops rsa_private_key_ops = { "RSA PRIVATE KEY", @@ -524,7 +561,8 @@ static hx509_private_key_ops rsa_private_key_ops = { rsa_private_key2SPKI, rsa_private_key_export, rsa_private_key_import, - rsa_generate_private_key + rsa_generate_private_key, + rsa_get_internal }; @@ -833,13 +871,24 @@ md2_verify_signature(hx509_context context, return 0; } +static const struct signature_alg heim_rsa_pkcs1_x509 = { + "rsa-pkcs1-x509", + oid_id_heim_rsa_pkcs1_x509, + hx509_signature_rsa_pkcs1_x509, + oid_id_pkcs1_rsaEncryption, + NULL, + PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + rsa_verify_signature, + rsa_create_signature +}; + static const struct signature_alg pkcs1_rsa_sha1_alg = { "rsa", oid_id_pkcs1_rsaEncryption, hx509_signature_rsa_with_sha1, oid_id_pkcs1_rsaEncryption, NULL, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -850,7 +899,7 @@ static const struct signature_alg rsa_with_sha256_alg = { hx509_signature_rsa_with_sha256, oid_id_pkcs1_rsaEncryption, oid_id_sha256, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -861,7 +910,7 @@ static const struct signature_alg rsa_with_sha1_alg = { hx509_signature_rsa_with_sha1, oid_id_pkcs1_rsaEncryption, oid_id_secsig_sha_1, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -872,7 +921,7 @@ static const struct signature_alg rsa_with_md5_alg = { hx509_signature_rsa_with_md5, oid_id_pkcs1_rsaEncryption, oid_id_rsa_digest_md5, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -883,7 +932,7 @@ static const struct signature_alg rsa_with_md2_alg = { hx509_signature_rsa_with_md2, oid_id_pkcs1_rsaEncryption, oid_id_rsa_digest_md2, - PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, + PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, rsa_verify_signature, rsa_create_signature }; @@ -952,7 +1001,7 @@ static const struct signature_alg *sig_algs[] = { &pkcs1_rsa_sha1_alg, &rsa_with_md5_alg, &rsa_with_md2_alg, - &pkcs1_rsa_sha1_alg, + &heim_rsa_pkcs1_x509, &dsa_sha1_alg, &sha256_alg, &sha1_alg, @@ -1423,6 +1472,11 @@ const AlgorithmIdentifier _hx509_signature_rsa_data = { { 7, rk_UNCONST(rsa_oid) }, NULL }; +static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 }; +const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = { + { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL +}; + static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 }; const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = { { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL @@ -1490,6 +1544,10 @@ const AlgorithmIdentifier * hx509_signature_rsa(void) { return &_hx509_signature_rsa_data; } +const AlgorithmIdentifier * +hx509_signature_rsa_pkcs1_x509(void) +{ return &_hx509_signature_rsa_pkcs1_x509_data; } + const AlgorithmIdentifier * hx509_crypto_des_rsdi_ede3_cbc(void) { return &_hx509_des_rsdi_ede3_cbc_oid; } @@ -1597,6 +1655,16 @@ _hx509_private_key_exportable(hx509_private_key key) return 1; } +BIGNUM * +_hx509_private_key_get_internal(hx509_context context, + hx509_private_key key, + const char *type) +{ + if (key->ops->get_internal == NULL) + return NULL; + return (*key->ops->get_internal)(context, key, type); +} + int _hx509_private_key_export(hx509_context context, const hx509_private_key key, diff --git a/lib/hx509/softp11.c b/lib/hx509/softp11.c new file mode 100644 index 000000000..7097556e6 --- /dev/null +++ b/lib/hx509/softp11.c @@ -0,0 +1,2017 @@ +/* + * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "hx_locl.h" +#include "pkcs11.h" + +#define OBJECT_ID_MASK 0xfff +#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) +#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) + + +struct st_attr { + CK_ATTRIBUTE attribute; + int secret; +}; + +struct st_object { + CK_OBJECT_HANDLE object_handle; + struct st_attr *attrs; + int num_attributes; + enum { + STO_T_CERTIFICATE, + STO_T_PRIVATE_KEY, + STO_T_PUBLIC_KEY + } type; + hx509_cert cert; +}; + +static struct soft_token { + CK_VOID_PTR application; + CK_NOTIFY notify; + char *config_file; + hx509_certs certs; + struct { + struct st_object **objs; + int num_objs; + } object; + struct { + int hardware_slot; + int app_error_fatal; + int login_done; + } flags; + int open_sessions; + struct session_state { + CK_SESSION_HANDLE session_handle; + + struct { + CK_ATTRIBUTE *attributes; + CK_ULONG num_attributes; + int next_object; + } find; + + int encrypt_object; + CK_MECHANISM_PTR encrypt_mechanism; + int decrypt_object; + CK_MECHANISM_PTR decrypt_mechanism; + int sign_object; + CK_MECHANISM_PTR sign_mechanism; + int verify_object; + CK_MECHANISM_PTR verify_mechanism; + int digest_object; + } state[10]; +#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) + FILE *logfile; +} soft_token; + +static hx509_context context; + +static void +application_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + if (soft_token.flags.app_error_fatal) + abort(); +} + +static void +st_logf(const char *fmt, ...) +{ + va_list ap; + if (soft_token.logfile == NULL) + return; + va_start(ap, fmt); + vfprintf(soft_token.logfile, fmt, ap); + va_end(ap); + fflush(soft_token.logfile); +} + +static CK_RV +init_context(void) +{ + if (context == NULL) { + int ret = hx509_context_init(&context); + if (ret) + return CKR_GENERAL_ERROR; + } + return CKR_OK; +} + +#define INIT_CONTEXT() { CK_RV icret = init_context(); if (icret) return icret; } + +static void +snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) +{ + int len; + va_list ap; + len = vsnprintf(str, size, fmt, ap); + va_end(ap); + if (len < 0 || len > size) + return; + while(len < size) + str[len++] = fillchar; +} + +#ifndef TEST_APP +#define printf error_use_st_logf +#endif + +#define VERIFY_SESSION_HANDLE(s, state) \ +{ \ + CK_RV ret; \ + ret = verify_session_handle(s, state); \ + if (ret != CKR_OK) { \ + /* return CKR_OK */; \ + } \ +} + +static CK_RV +verify_session_handle(CK_SESSION_HANDLE hSession, + struct session_state **state) +{ + int i; + + for (i = 0; i < MAX_NUM_SESSION; i++){ + if (soft_token.state[i].session_handle == hSession) + break; + } + if (i == MAX_NUM_SESSION) { + application_error("use of invalid handle: 0x%08lx\n", + (unsigned long)hSession); + return CKR_SESSION_HANDLE_INVALID; + } + if (state) + *state = &soft_token.state[i]; + return CKR_OK; +} + +static CK_RV +object_handle_to_object(CK_OBJECT_HANDLE handle, + struct st_object **object) +{ + int i = HANDLE_OBJECT_ID(handle); + + *object = NULL; + if (i >= soft_token.object.num_objs) + return CKR_ARGUMENTS_BAD; + if (soft_token.object.objs[i] == NULL) + return CKR_ARGUMENTS_BAD; + if (soft_token.object.objs[i]->object_handle != handle) + return CKR_ARGUMENTS_BAD; + *object = soft_token.object.objs[i]; + return CKR_OK; +} + +static int +attributes_match(const struct st_object *obj, + const CK_ATTRIBUTE *attributes, + CK_ULONG num_attributes) +{ + CK_ULONG i; + int j; + st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); + + for (i = 0; i < num_attributes; i++) { + int match = 0; + for (j = 0; j < obj->num_attributes; j++) { + if (attributes[i].type == obj->attrs[j].attribute.type && + attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && + memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, + attributes[i].ulValueLen) == 0) { + match = 1; + break; + } + } + if (match == 0) { + st_logf("type %d attribute have no match\n", attributes[i].type); + return 0; + } + } + st_logf("attribute matches\n"); + return 1; +} + +static void +print_attributes(const CK_ATTRIBUTE *attributes, + CK_ULONG num_attributes) +{ + CK_ULONG i; + + st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); + + for (i = 0; i < num_attributes; i++) { + st_logf(" type: "); + switch (attributes[i].type) { + case CKA_TOKEN: { + CK_BBOOL *ck_true; + if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { + application_error("token attribute wrong length\n"); + break; + } + ck_true = attributes[i].pValue; + st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); + break; + } + case CKA_CLASS: { + CK_OBJECT_CLASS *class; + if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { + application_error("class attribute wrong length\n"); + break; + } + class = attributes[i].pValue; + st_logf("class "); + switch (*class) { + case CKO_CERTIFICATE: + st_logf("certificate"); + break; + case CKO_PUBLIC_KEY: + st_logf("public key"); + break; + case CKO_PRIVATE_KEY: + st_logf("private key"); + break; + case CKO_SECRET_KEY: + st_logf("secret key"); + break; + case CKO_DOMAIN_PARAMETERS: + st_logf("domain parameters"); + break; + default: + st_logf("[class %lx]", (long unsigned)*class); + break; + } + break; + } + case CKA_PRIVATE: + st_logf("private"); + break; + case CKA_LABEL: + st_logf("label"); + break; + case CKA_APPLICATION: + st_logf("application"); + break; + case CKA_VALUE: + st_logf("value"); + break; + case CKA_ID: + st_logf("id"); + break; + default: + st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); + break; + } + st_logf("\n"); + } +} + +static struct st_object * +add_st_object(void) +{ + struct st_object *o, **objs; + int i; + + o = malloc(sizeof(*o)); + if (o == NULL) + return NULL; + memset(o, 0, sizeof(*o)); + o->attrs = NULL; + o->num_attributes = 0; + + for (i = 0; i < soft_token.object.num_objs; i++) { + if (soft_token.object.objs == NULL) { + soft_token.object.objs[i] = o; + break; + } + } + if (i == soft_token.object.num_objs) { + objs = realloc(soft_token.object.objs, + (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); + if (objs == NULL) { + free(o); + return NULL; + } + soft_token.object.objs = objs; + soft_token.object.objs[soft_token.object.num_objs++] = o; + } + soft_token.object.objs[i]->object_handle = + (random() & (~OBJECT_ID_MASK)) | i; + + return o; +} + +static CK_RV +add_object_attribute(struct st_object *o, + int secret, + CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR pValue, + CK_ULONG ulValueLen) +{ + struct st_attr *a; + int i; + + i = o->num_attributes; + a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); + if (a == NULL) + return CKR_DEVICE_MEMORY; + o->attrs = a; + o->attrs[i].secret = secret; + o->attrs[i].attribute.type = type; + o->attrs[i].attribute.pValue = malloc(ulValueLen); + if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) + return CKR_DEVICE_MEMORY; + memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); + o->attrs[i].attribute.ulValueLen = ulValueLen; + o->num_attributes++; + + return CKR_OK; +} + +static CK_RV +add_pubkey_info(hx509_context hxctx, struct st_object *o, + CK_KEY_TYPE key_type, hx509_cert cert) +{ + BIGNUM *num; + CK_BYTE *modulus = NULL; + size_t modulus_len = 0; + CK_ULONG modulus_bits = 0; + CK_BYTE *exponent = NULL; + size_t exponent_len = 0; + + if (key_type != CKK_RSA) + return CKR_OK; + + num = _hx509_private_key_get_internal(context, + _hx509_cert_private_key(cert), + "rsa-modulus"); + if (num == NULL) + return CKR_GENERAL_ERROR; + modulus_bits = BN_num_bits(num); + + modulus_len = BN_num_bytes(num); + modulus = malloc(modulus_len); + BN_bn2bin(num, modulus); + BN_free(num); + + add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); + add_object_attribute(o, 0, CKA_MODULUS_BITS, + &modulus_bits, sizeof(modulus_bits)); + + free(modulus); + + num = _hx509_private_key_get_internal(context, + _hx509_cert_private_key(cert), + "rsa-exponent"); + if (num == NULL) + return CKR_GENERAL_ERROR; + + exponent_len = BN_num_bytes(num); + exponent = malloc(exponent_len); + BN_bn2bin(num, exponent); + BN_free(num); + + add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, + exponent, exponent_len); + + free(exponent); + + return CKR_OK; +} + + +struct foo { + char *label; + char *id; +}; + +static int +add_cert(hx509_context hxctx, void *ctx, hx509_cert cert) +{ + struct foo *foo = (struct foo *)ctx; + struct st_object *o = NULL; + CK_BBOOL bool_true = CK_TRUE; + CK_BBOOL bool_false = CK_FALSE; + CK_OBJECT_CLASS c; + CK_CERTIFICATE_TYPE cert_type = CKC_X_509; + CK_KEY_TYPE key_type; + CK_MECHANISM_TYPE mech_type; + CK_RV ret = CKR_GENERAL_ERROR; + int hret; + heim_octet_string cert_data, subject_data, issuer_data, serial_data; + + st_logf("adding certificate\n"); + + serial_data.data = NULL; + serial_data.length = 0; + cert_data = subject_data = issuer_data = serial_data; + + hret = hx509_cert_binary(hxctx, cert, &cert_data); + if (hret) + goto out; + + { + hx509_name name; + + hret = hx509_cert_get_issuer(cert, &name); + if (hret) + goto out; + hret = hx509_name_binary(name, &issuer_data); + hx509_name_free(&name); + if (hret) + goto out; + + hret = hx509_cert_get_subject(cert, &name); + if (hret) + goto out; + hret = hx509_name_binary(name, &subject_data); + hx509_name_free(&name); + if (hret) + goto out; + } + + + o = add_st_object(); + if (o == NULL) { + ret = CKR_DEVICE_MEMORY; + goto out; + } + o->type = STO_T_CERTIFICATE; + o->cert = hx509_cert_ref(cert); + + key_type = CKK_RSA; /* XXX */ + +#if 0 + switch (EVP_PKEY_type(public_key->type)) { + case EVP_PKEY_RSA: + key_type = CKK_RSA; + break; + case EVP_PKEY_DSA: + key_type = CKK_DSA; + break; + default: + /* XXX */ + break; + } +#endif + + c = CKO_CERTIFICATE; + add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); + add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); + + add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); + add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); + + add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); + add_object_attribute(o, 0, CKA_ISSUER, issuer_data.data, issuer_data.length); + add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data.data, serial_data.length); + add_object_attribute(o, 0, CKA_VALUE, cert_data.data, cert_data.length); + add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); + + st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); + + o = add_st_object(); + if (o == NULL) { + ret = CKR_DEVICE_MEMORY; + goto out; + } + o->type = STO_T_PUBLIC_KEY; + o->cert = hx509_cert_ref(cert); + + c = CKO_PUBLIC_KEY; + add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); + add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); + + add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); + add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); + add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ + add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ + add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); + mech_type = CKM_RSA_X_509; + add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); + + add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); + add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); + + add_pubkey_info(hxctx, o, key_type, cert); + + st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); + + if (hx509_cert_have_private_key(cert)) { + CK_FLAGS flags; + + o = add_st_object(); + if (o == NULL) { + ret = CKR_DEVICE_MEMORY; + goto out; + } + o->type = STO_T_PRIVATE_KEY; + o->cert = hx509_cert_ref(cert); + + c = CKO_PRIVATE_KEY; + add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); + add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); + + add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); + add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); + add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ + add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ + add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); + mech_type = CKM_RSA_X_509; + add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); + + add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); + add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); + flags = 0; + add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); + + add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); + add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); + add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); + + add_pubkey_info(hxctx, o, key_type, cert); + } + + ret = CKR_OK; + out: + if (ret != CKR_OK) { + st_logf("something went wrong when adding cert!\n"); + + /* XXX wack o */; + } + hx509_xfree(cert_data.data); + hx509_xfree(serial_data.data); + hx509_xfree(issuer_data.data); + hx509_xfree(subject_data.data); + + return 0; +} + + +static CK_RV +add_certificate(const char *cert_file, + const char *pin, + char *id, + char *label) +{ + int ret; + hx509_certs certs; + + struct foo foo; + foo.id = id; + foo.label = label; + + ret = hx509_certs_init(context, cert_file, 0, NULL, &certs); + if (ret) { + st_logf("failed to open file %s\n", cert_file); + return CKR_GENERAL_ERROR; + } + + ret = hx509_certs_iter(context, certs, add_cert, &foo); + hx509_certs_free(&certs); + if (ret) { + st_logf("failed adding certs from file %s\n", cert_file); + return CKR_GENERAL_ERROR; + } + + return CKR_OK; +} + +static void +find_object_final(struct session_state *state) +{ + if (state->find.attributes) { + CK_ULONG i; + + for (i = 0; i < state->find.num_attributes; i++) { + if (state->find.attributes[i].pValue) + free(state->find.attributes[i].pValue); + } + free(state->find.attributes); + state->find.attributes = NULL; + state->find.num_attributes = 0; + state->find.next_object = -1; + } +} + +static void +reset_crypto_state(struct session_state *state) +{ + state->encrypt_object = -1; + if (state->encrypt_mechanism) + free(state->encrypt_mechanism); + state->encrypt_mechanism = NULL_PTR; + state->decrypt_object = -1; + if (state->decrypt_mechanism) + free(state->decrypt_mechanism); + state->decrypt_mechanism = NULL_PTR; + state->sign_object = -1; + if (state->sign_mechanism) + free(state->sign_mechanism); + state->sign_mechanism = NULL_PTR; + state->verify_object = -1; + if (state->verify_mechanism) + free(state->verify_mechanism); + state->verify_mechanism = NULL_PTR; + state->digest_object = -1; +} + +static void +close_session(struct session_state *state) +{ + if (state->find.attributes) { + application_error("application didn't do C_FindObjectsFinal\n"); + find_object_final(state); + } + + state->session_handle = CK_INVALID_HANDLE; + soft_token.application = NULL_PTR; + soft_token.notify = NULL_PTR; + reset_crypto_state(state); +} + +static const char * +has_session(void) +{ + return soft_token.open_sessions > 0 ? "yes" : "no"; +} + +static CK_RV +read_conf_file(const char *fn, CK_USER_TYPE userType, const char *pin) +{ + char buf[1024], *type, *s, *p; + int anchor; + FILE *f; + CK_RV ret = CKR_OK; + + f = fopen(fn, "r"); + if (f == NULL) { + st_logf("can't open configuration file %s\n", fn); + return CKR_GENERAL_ERROR; + } + + while(fgets(buf, sizeof(buf), f) != NULL) { + buf[strcspn(buf, "\n")] = '\0'; + + anchor = 0; + + st_logf("line: %s\n", buf); + + p = buf; + while (isspace(*p)) + p++; + if (*p == '#') + continue; + while (isspace(*p)) + p++; + + s = NULL; + type = strtok_r(p, "\t", &s); + if (type == NULL) + continue; + + if (strcasecmp("certificate", type) == 0) { + char *cert, *id, *label; + + id = strtok_r(NULL, "\t", &s); + if (id == NULL) { + st_logf("no id\n"); + continue; + } + st_logf("id: %s\n", id); + label = strtok_r(NULL, "\t", &s); + if (label == NULL) { + st_logf("no label\n"); + continue; + } + cert = strtok_r(NULL, "\t", &s); + if (cert == NULL) { + st_logf("no certfiicate store\n"); + continue; + } + + st_logf("adding: %s: %s in file %s\n", id, label, cert); + + ret = add_certificate(cert, pin, id, label); + if (ret) + goto out; + } else { + st_logf("unknown type: %s\n", type); + } + } +out: + fclose(f); + + return ret; +} + +static CK_RV +func_not_supported(void) +{ + st_logf("function not supported\n"); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_Initialize(CK_VOID_PTR a) +{ + CK_C_INITIALIZE_ARGS_PTR args = a; + CK_RV ret; + int i; + + st_logf("Initialize\n"); + + INIT_CONTEXT(); + + OpenSSL_add_all_algorithms(); + + srandom(getpid() ^ time(NULL)); + + for (i = 0; i < MAX_NUM_SESSION; i++) { + soft_token.state[i].session_handle = CK_INVALID_HANDLE; + soft_token.state[i].find.attributes = NULL; + soft_token.state[i].find.num_attributes = 0; + soft_token.state[i].find.next_object = -1; + reset_crypto_state(&soft_token.state[i]); + } + + soft_token.flags.hardware_slot = 1; + soft_token.flags.app_error_fatal = 0; + soft_token.flags.login_done = 0; + + soft_token.object.objs = NULL; + soft_token.object.num_objs = 0; + + soft_token.logfile = NULL; +#if 0 + soft_token.logfile = stdout; +#endif +#if 0 + soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); +#endif + + if (a != NULL_PTR) { + st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); + st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); + st_logf("\tLockMutext\t%p\n", args->LockMutex); + st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); + st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); + } + + { + char *fn = NULL, *home = NULL; + + if (getuid() == geteuid()) { + fn = getenv("SOFTPKCS11RC"); + if (fn) + fn = strdup(fn); + home = getenv("HOME"); + } + if (fn == NULL && home == NULL) { + struct passwd *pw = getpwuid(getuid()); + if(pw != NULL) + home = pw->pw_dir; + } + if (fn == NULL) { + if (home) + asprintf(&fn, "%s/.soft-token.rc", home); + else + fn = strdup("/etc/soft-token.rc"); + } + + soft_token.config_file = fn; + } + + ret = read_conf_file(soft_token.config_file, CKU_USER, NULL); + if (ret == CKR_OK) + soft_token.flags.login_done = 1; + + return CKR_OK; +} + +CK_RV +C_Finalize(CK_VOID_PTR args) +{ + int i; + + INIT_CONTEXT(); + + st_logf("Finalize\n"); + + for (i = 0; i < MAX_NUM_SESSION; i++) { + if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { + application_error("application finalized without " + "closing session\n"); + close_session(&soft_token.state[i]); + } + } + + return CKR_OK; +} + +CK_RV +C_GetInfo(CK_INFO_PTR args) +{ + INIT_CONTEXT(); + + st_logf("GetInfo\n"); + + memset(args, 17, sizeof(*args)); + args->cryptokiVersion.major = 2; + args->cryptokiVersion.minor = 10; + snprintf_fill((char *)args->manufacturerID, + sizeof(args->manufacturerID), + ' ', + "SoftToken"); + snprintf_fill((char *)args->libraryDescription, + sizeof(args->libraryDescription), ' ', + "SoftToken"); + args->libraryVersion.major = 1; + args->libraryVersion.minor = 8; + + return CKR_OK; +} + +extern CK_FUNCTION_LIST funcs; + +CK_RV +C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) +{ + INIT_CONTEXT(); + + *ppFunctionList = &funcs; + return CKR_OK; +} + +CK_RV +C_GetSlotList(CK_BBOOL tokenPresent, + CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount) +{ + INIT_CONTEXT(); + st_logf("GetSlotList: %s\n", + tokenPresent ? "tokenPresent" : "token not Present"); + if (pSlotList) + pSlotList[0] = 1; + *pulCount = 1; + return CKR_OK; +} + +CK_RV +C_GetSlotInfo(CK_SLOT_ID slotID, + CK_SLOT_INFO_PTR pInfo) +{ + INIT_CONTEXT(); + st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); + + memset(pInfo, 18, sizeof(*pInfo)); + + if (slotID != 1) + return CKR_ARGUMENTS_BAD; + + snprintf_fill((char *)pInfo->slotDescription, + sizeof(pInfo->slotDescription), + ' ', + "SoftToken (slot)"); + snprintf_fill((char *)pInfo->manufacturerID, + sizeof(pInfo->manufacturerID), + ' ', + "SoftToken (slot)"); + pInfo->flags = CKF_TOKEN_PRESENT; + if (soft_token.flags.hardware_slot) + pInfo->flags |= CKF_HW_SLOT; + pInfo->hardwareVersion.major = 1; + pInfo->hardwareVersion.minor = 0; + pInfo->firmwareVersion.major = 1; + pInfo->firmwareVersion.minor = 0; + + return CKR_OK; +} + +CK_RV +C_GetTokenInfo(CK_SLOT_ID slotID, + CK_TOKEN_INFO_PTR pInfo) +{ + INIT_CONTEXT(); + st_logf("GetTokenInfo: %s\n", has_session()); + + memset(pInfo, 19, sizeof(*pInfo)); + + snprintf_fill((char *)pInfo->label, + sizeof(pInfo->label), + ' ', + "SoftToken (token)"); + snprintf_fill((char *)pInfo->manufacturerID, + sizeof(pInfo->manufacturerID), + ' ', + "SoftToken (token)"); + snprintf_fill((char *)pInfo->model, + sizeof(pInfo->model), + ' ', + "SoftToken (token)"); + snprintf_fill((char *)pInfo->serialNumber, + sizeof(pInfo->serialNumber), + ' ', + "4711"); + pInfo->flags = + CKF_TOKEN_INITIALIZED | + CKF_USER_PIN_INITIALIZED; + + if (soft_token.flags.login_done == 0) + pInfo->flags |= CKF_LOGIN_REQUIRED; + + /* CFK_RNG | + CKF_RESTORE_KEY_NOT_NEEDED | + */ + pInfo->ulMaxSessionCount = MAX_NUM_SESSION; + pInfo->ulSessionCount = soft_token.open_sessions; + pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; + pInfo->ulRwSessionCount = soft_token.open_sessions; + pInfo->ulMaxPinLen = 1024; + pInfo->ulMinPinLen = 0; + pInfo->ulTotalPublicMemory = 4711; + pInfo->ulFreePublicMemory = 4712; + pInfo->ulTotalPrivateMemory = 4713; + pInfo->ulFreePrivateMemory = 4714; + pInfo->hardwareVersion.major = 2; + pInfo->hardwareVersion.minor = 0; + pInfo->firmwareVersion.major = 2; + pInfo->firmwareVersion.minor = 0; + + return CKR_OK; +} + +CK_RV +C_GetMechanismList(CK_SLOT_ID slotID, + CK_MECHANISM_TYPE_PTR pMechanismList, + CK_ULONG_PTR pulCount) +{ + INIT_CONTEXT(); + st_logf("GetMechanismList\n"); + + *pulCount = 1; + if (pMechanismList == NULL_PTR) + return CKR_OK; + pMechanismList[1] = CKM_RSA_PKCS; + + return CKR_OK; +} + +CK_RV +C_GetMechanismInfo(CK_SLOT_ID slotID, + CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo) +{ + INIT_CONTEXT(); + st_logf("GetMechanismInfo: slot %d type: %d\n", + (int)slotID, (int)type); + memset(pInfo, 0, sizeof(*pInfo)); + + return CKR_OK; +} + +CK_RV +C_InitToken(CK_SLOT_ID slotID, + CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen, + CK_UTF8CHAR_PTR pLabel) +{ + INIT_CONTEXT(); + st_logf("InitToken: slot %d\n", (int)slotID); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_OpenSession(CK_SLOT_ID slotID, + CK_FLAGS flags, + CK_VOID_PTR pApplication, + CK_NOTIFY Notify, + CK_SESSION_HANDLE_PTR phSession) +{ + int i; + INIT_CONTEXT(); + st_logf("OpenSession: slot: %d\n", (int)slotID); + + if (soft_token.open_sessions == MAX_NUM_SESSION) + return CKR_SESSION_COUNT; + + soft_token.application = pApplication; + soft_token.notify = Notify; + + for (i = 0; i < MAX_NUM_SESSION; i++) + if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) + break; + if (i == MAX_NUM_SESSION) + abort(); + + soft_token.open_sessions++; + + soft_token.state[i].session_handle = + (CK_SESSION_HANDLE)(random() & 0xfffff); + *phSession = soft_token.state[i].session_handle; + + return CKR_OK; +} + +CK_RV +C_CloseSession(CK_SESSION_HANDLE hSession) +{ + struct session_state *state; + INIT_CONTEXT(); + st_logf("CloseSession\n"); + + if (verify_session_handle(hSession, &state) != CKR_OK) + application_error("closed session not open"); + else + close_session(state); + + return CKR_OK; +} + +CK_RV +C_CloseAllSessions(CK_SLOT_ID slotID) +{ + int i; + INIT_CONTEXT(); + + st_logf("CloseAllSessions\n"); + + for (i = 0; i < MAX_NUM_SESSION; i++) + if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) + close_session(&soft_token.state[i]); + + return CKR_OK; +} + +CK_RV +C_GetSessionInfo(CK_SESSION_HANDLE hSession, + CK_SESSION_INFO_PTR pInfo) +{ + st_logf("GetSessionInfo\n"); + INIT_CONTEXT(); + + VERIFY_SESSION_HANDLE(hSession, NULL); + + memset(pInfo, 20, sizeof(*pInfo)); + + pInfo->slotID = 1; + if (soft_token.flags.login_done) + pInfo->state = CKS_RO_USER_FUNCTIONS; + else + pInfo->state = CKS_RO_PUBLIC_SESSION; + pInfo->flags = CKF_SERIAL_SESSION; + pInfo->ulDeviceError = 0; + + return CKR_OK; +} + +CK_RV +C_Login(CK_SESSION_HANDLE hSession, + CK_USER_TYPE userType, + CK_UTF8CHAR_PTR pPin, + CK_ULONG ulPinLen) +{ + char *pin = NULL; + CK_RV ret; + INIT_CONTEXT(); + + st_logf("Login\n"); + + VERIFY_SESSION_HANDLE(hSession, NULL); + + if (pPin != NULL_PTR) { + asprintf(&pin, "%.*s", (int)ulPinLen, pPin); + st_logf("type: %d password: %s\n", (int)userType, pin); + } + + /* + * Login + */ + + ret = read_conf_file(soft_token.config_file, userType, pin); + if (ret == CKR_OK) + soft_token.flags.login_done = 1; + + free(pin); + + return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; +} + +CK_RV +C_Logout(CK_SESSION_HANDLE hSession) +{ + st_logf("Logout\n"); + INIT_CONTEXT(); + + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_GetObjectSize(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ULONG_PTR pulSize) +{ + st_logf("GetObjectSize\n"); + INIT_CONTEXT(); + + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_GetAttributeValue(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount) +{ + struct session_state *state; + struct st_object *obj; + CK_ULONG i; + CK_RV ret; + int j; + + INIT_CONTEXT(); + + st_logf("GetAttributeValue: %lx\n", + (unsigned long)HANDLE_OBJECT_ID(hObject)); + VERIFY_SESSION_HANDLE(hSession, &state); + + if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { + st_logf("object not found: %lx\n", + (unsigned long)HANDLE_OBJECT_ID(hObject)); + return ret; + } + + for (i = 0; i < ulCount; i++) { + st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); + for (j = 0; j < obj->num_attributes; j++) { + if (obj->attrs[j].secret) { + pTemplate[i].ulValueLen = (CK_ULONG)-1; + break; + } + if (pTemplate[i].type == obj->attrs[j].attribute.type) { + if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { + if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) + memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, + obj->attrs[j].attribute.ulValueLen); + } + pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; + break; + } + } + if (j == obj->num_attributes) { + st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); + pTemplate[i].ulValueLen = (CK_ULONG)-1; + } + + } + return CKR_OK; +} + +CK_RV +C_FindObjectsInit(CK_SESSION_HANDLE hSession, + CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulCount) +{ + struct session_state *state; + + st_logf("FindObjectsInit\n"); + + INIT_CONTEXT(); + + VERIFY_SESSION_HANDLE(hSession, &state); + + if (state->find.next_object != -1) { + application_error("application didn't do C_FindObjectsFinal\n"); + find_object_final(state); + } + if (ulCount) { + CK_ULONG i; + + print_attributes(pTemplate, ulCount); + + state->find.attributes = + calloc(1, ulCount * sizeof(state->find.attributes[0])); + if (state->find.attributes == NULL) + return CKR_DEVICE_MEMORY; + for (i = 0; i < ulCount; i++) { + state->find.attributes[i].pValue = + malloc(pTemplate[i].ulValueLen); + if (state->find.attributes[i].pValue == NULL) { + find_object_final(state); + return CKR_DEVICE_MEMORY; + } + memcpy(state->find.attributes[i].pValue, + pTemplate[i].pValue, pTemplate[i].ulValueLen); + state->find.attributes[i].type = pTemplate[i].type; + state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; + } + state->find.num_attributes = ulCount; + state->find.next_object = 0; + } else { + st_logf("find all objects\n"); + state->find.attributes = NULL; + state->find.num_attributes = 0; + state->find.next_object = 0; + } + + return CKR_OK; +} + +CK_RV +C_FindObjects(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE_PTR phObject, + CK_ULONG ulMaxObjectCount, + CK_ULONG_PTR pulObjectCount) +{ + struct session_state *state; + int i; + + INIT_CONTEXT(); + + st_logf("FindObjects\n"); + + VERIFY_SESSION_HANDLE(hSession, &state); + + if (state->find.next_object == -1) { + application_error("application didn't do C_FindObjectsInit\n"); + return CKR_ARGUMENTS_BAD; + } + if (ulMaxObjectCount == 0) { + application_error("application asked for 0 objects\n"); + return CKR_ARGUMENTS_BAD; + } + *pulObjectCount = 0; + for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { + st_logf("FindObjects: %d\n", i); + state->find.next_object = i + 1; + if (attributes_match(soft_token.object.objs[i], + state->find.attributes, + state->find.num_attributes)) { + *phObject++ = soft_token.object.objs[i]->object_handle; + ulMaxObjectCount--; + (*pulObjectCount)++; + if (ulMaxObjectCount == 0) + break; + } + } + return CKR_OK; +} + +CK_RV +C_FindObjectsFinal(CK_SESSION_HANDLE hSession) +{ + struct session_state *state; + + INIT_CONTEXT(); + + st_logf("FindObjectsFinal\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + find_object_final(state); + return CKR_OK; +} + +static CK_RV +commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, + const CK_MECHANISM_TYPE *mechs, int mechs_len, + const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, + struct st_object **o) +{ + CK_RV ret; + int i; + + *o = NULL; + if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) + return ret; + + ret = attributes_match(*o, attr_match, attr_match_len); + if (!ret) { + application_error("called commonInit on key that doesn't " + "support required attr"); + return CKR_ARGUMENTS_BAD; + } + + for (i = 0; i < mechs_len; i++) + if (mechs[i] == pMechanism->mechanism) + break; + if (i == mechs_len) { + application_error("called mech (%08lx) not supported\n", + pMechanism->mechanism); + return CKR_ARGUMENTS_BAD; + } + return CKR_OK; +} + + +static CK_RV +dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism) +{ + CK_MECHANISM_PTR p; + + p = malloc(sizeof(*p)); + if (p == NULL) + return CKR_DEVICE_MEMORY; + + if (*dup) + free(*dup); + *dup = p; + memcpy(p, pMechanism, sizeof(*p)); + + return CKR_OK; +} + +#if 0 + +CK_RV +C_EncryptInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + struct session_state *state; + CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; + CK_BBOOL bool_true = CK_TRUE; + CK_ATTRIBUTE attr[] = { + { CKA_ENCRYPT, &bool_true, sizeof(bool_true) } + }; + struct st_object *o; + CK_RV ret; + + INIT_CONTEXT(); + + st_logf("EncryptInit\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), + mechs, sizeof(mechs)/sizeof(mechs[0]), + pMechanism, hKey, &o); + if (ret) + return ret; + + ret = dup_mechanism(&state->encrypt_mechanism, pMechanism); + if (ret == CKR_OK) + state->encrypt_object = OBJECT_ID(o); + + return ret; +} + +CK_RV +C_Encrypt(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pEncryptedData, + CK_ULONG_PTR pulEncryptedDataLen) +{ + CK_RV ret = CKR_FUNCTION_NOT_SUPPORTED; +#if 0 + struct session_state *state; + struct st_object *o; + void *buffer = NULL; + RSA *rsa; + int padding, len, buffer_len, padding_len; + + INIT_CONTEXT(); + + st_logf("Encrypt\n"); + + VERIFY_SESSION_HANDLE(hSession, &state); + + if (state->encrypt_object == -1) + return CKR_ARGUMENTS_BAD; + + o = soft_token.object.objs[state->encrypt_object]; + + if (o->u.public_key == NULL) { + st_logf("public key NULL\n"); + return CKR_ARGUMENTS_BAD; + } + + rsa = o->u.public_key->pkey.rsa; + + if (rsa == NULL) + return CKR_ARGUMENTS_BAD; + + RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ + + buffer_len = RSA_size(rsa); + + buffer = malloc(buffer_len); + if (buffer == NULL) { + ret = CKR_DEVICE_MEMORY; + goto out; + } + + ret = CKR_OK; + switch(state->encrypt_mechanism->mechanism) { + case CKM_RSA_PKCS: + padding = RSA_PKCS1_PADDING; + padding_len = RSA_PKCS1_PADDING_SIZE; + break; + default: + ret = CKR_FUNCTION_NOT_SUPPORTED; + goto out; + } + + if (buffer_len + padding_len < ulDataLen) { + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + if (pulEncryptedDataLen == NULL) { + st_logf("pulEncryptedDataLen NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + if (pData == NULL_PTR) { + st_logf("data NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding); + if (len <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } + if (len > buffer_len) + abort(); + + if (pEncryptedData != NULL_PTR) + memcpy(pEncryptedData, buffer, len); + *pulEncryptedDataLen = len; + + out: + if (buffer) { + memset(buffer, 0, buffer_len); + free(buffer); + } +#endif + return ret; +} + +CK_RV +C_EncryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + INIT_CONTEXT(); + st_logf("EncryptUpdate\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +CK_RV +C_EncryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) +{ + INIT_CONTEXT(); + st_logf("EncryptFinal\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +/* C_DecryptInit initializes a decryption operation. */ +CK_RV +C_DecryptInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + struct session_state *state; + CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; + CK_BBOOL bool_true = CK_TRUE; + CK_ATTRIBUTE attr[] = { + { CKA_DECRYPT, &bool_true, sizeof(bool_true) } + }; + struct st_object *o; + CK_RV ret; + + INIT_CONTEXT(); + st_logf("DecryptInit\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), + mechs, sizeof(mechs)/sizeof(mechs[0]), + pMechanism, hKey, &o); + if (ret) + return ret; + + ret = dup_mechanism(&state->decrypt_mechanism, pMechanism); + if (ret == CKR_OK) + state->decrypt_object = OBJECT_ID(o); + + return CKR_OK; +} + + +CK_RV +C_Decrypt(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData, + CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen) +{ + CK_RV ret = CKR_FUNCTION_NOT_SUPPORTED; +#if 0 + struct session_state *state; + struct st_object *o; + void *buffer = NULL; + RSA *rsa; + int padding, len, buffer_len, padding_len; + + INIT_CONTEXT(); + st_logf("Decrypt\n"); + + VERIFY_SESSION_HANDLE(hSession, &state); + + if (state->decrypt_object == -1) + return CKR_ARGUMENTS_BAD; + + o = soft_token.object.objs[state->decrypt_object]; + + if (o->u.private_key.key == NULL) { + st_logf("private key NULL\n"); + return CKR_ARGUMENTS_BAD; + } + + rsa = o->u.private_key.key->pkey.rsa; + + if (rsa == NULL) + return CKR_ARGUMENTS_BAD; + + RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ + + buffer_len = RSA_size(rsa); + + buffer = malloc(buffer_len); + if (buffer == NULL) { + ret = CKR_DEVICE_MEMORY; + goto out; + } + + ret = CKR_OK; + switch(state->decrypt_mechanism->mechanism) { + case CKM_RSA_PKCS: + padding = RSA_PKCS1_PADDING; + padding_len = RSA_PKCS1_PADDING_SIZE; + break; + default: + ret = CKR_FUNCTION_NOT_SUPPORTED; + goto out; + } + + if (buffer_len + padding_len < ulEncryptedDataLen) { + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + if (pulDataLen == NULL) { + st_logf("pulDataLen NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + if (pEncryptedData == NULL_PTR) { + st_logf("data NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer, + rsa, padding); + if (len <= 0) { + ret = CKR_DEVICE_ERROR; + goto out; + } + if (len > buffer_len) + abort(); + + if (pData != NULL_PTR) + memcpy(pData, buffer, len); + *pulDataLen = len; + + out: + if (buffer) { + memset(buffer, 0, buffer_len); + free(buffer); + } +#endif + return ret; +} + + +CK_RV +C_DecryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, + CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, + CK_ULONG_PTR pulPartLen) + +{ + st_logf("DecryptUpdate\n"); + INIT_CONTEXT(); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +CK_RV +C_DecryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastPart, + CK_ULONG_PTR pulLastPartLen) +{ + st_logf("DecryptFinal\n"); + INIT_CONTEXT(); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} +#endif + +CK_RV +C_DigestInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism) +{ + st_logf("DigestInit\n"); + INIT_CONTEXT(); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_SignInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + struct session_state *state; + CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; + CK_BBOOL bool_true = CK_TRUE; + CK_ATTRIBUTE attr[] = { + { CKA_SIGN, &bool_true, sizeof(bool_true) } + }; + struct st_object *o; + CK_RV ret; + + INIT_CONTEXT(); + st_logf("SignInit\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), + mechs, sizeof(mechs)/sizeof(mechs[0]), + pMechanism, hKey, &o); + if (ret) + return ret; + + ret = dup_mechanism(&state->sign_mechanism, pMechanism); + if (ret == CKR_OK) + state->sign_object = OBJECT_ID(o); + + return CKR_OK; +} + +CK_RV +C_Sign(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + struct session_state *state; + struct st_object *o; + CK_RV ret; + uint hret; + const AlgorithmIdentifier *alg; + heim_octet_string sig, data; + + INIT_CONTEXT(); + st_logf("Sign\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + sig.data = NULL; + sig.length = 0; + + if (state->sign_object == -1) + return CKR_ARGUMENTS_BAD; + + if (pulSignatureLen == NULL) { + st_logf("signature len NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + if (pData == NULL_PTR) { + st_logf("data NULL\n"); + ret = CKR_ARGUMENTS_BAD; + goto out; + } + + o = soft_token.object.objs[state->sign_object]; + + if (hx509_cert_have_private_key(o->cert) == 0) { + st_logf("private key NULL\n"); + return CKR_ARGUMENTS_BAD; + } + + switch(state->sign_mechanism->mechanism) { + case CKM_RSA_PKCS: + alg = hx509_signature_rsa_pkcs1_x509(); + break; + default: + ret = CKR_FUNCTION_NOT_SUPPORTED; + goto out; + } + + data.data = pData; + data.length = ulDataLen; + + hret = _hx509_create_signature(context, + _hx509_cert_private_key(o->cert), + alg, + &data, + NULL, + &sig); + if (hret) { + ret = CKR_DEVICE_ERROR; + goto out; + } + *pulSignatureLen = sig.length; + + if (pSignature != NULL_PTR) + memcpy(pSignature, sig.data, sig.length); + + ret = CKR_OK; + out: + if (sig.data) { + memset(sig.data, 0, sig.length); + der_free_octet_string(&sig); + } + return ret; +} + +CK_RV +C_SignUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + INIT_CONTEXT(); + st_logf("SignUpdate\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +CK_RV +C_SignFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + INIT_CONTEXT(); + st_logf("SignUpdate\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifyInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) +{ + struct session_state *state; + CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; + CK_BBOOL bool_true = CK_TRUE; + CK_ATTRIBUTE attr[] = { + { CKA_VERIFY, &bool_true, sizeof(bool_true) } + }; + struct st_object *o; + CK_RV ret; + + INIT_CONTEXT(); + st_logf("VerifyInit\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), + mechs, sizeof(mechs)/sizeof(mechs[0]), + pMechanism, hKey, &o); + if (ret) + return ret; + + ret = dup_mechanism(&state->verify_mechanism, pMechanism); + if (ret == CKR_OK) + state->verify_object = OBJECT_ID(o); + + return ret; +} + +CK_RV +C_Verify(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, + CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + struct session_state *state; + struct st_object *o; + const AlgorithmIdentifier *alg; + CK_RV ret; + int hret; + heim_octet_string data, sig; + + INIT_CONTEXT(); + st_logf("Verify\n"); + VERIFY_SESSION_HANDLE(hSession, &state); + + if (state->verify_object == -1) + return CKR_ARGUMENTS_BAD; + + o = soft_token.object.objs[state->verify_object]; + + switch(state->verify_mechanism->mechanism) { + case CKM_RSA_PKCS: + alg = hx509_signature_rsa_pkcs1_x509(); + break; + default: + ret = CKR_FUNCTION_NOT_SUPPORTED; + goto out; + } + + sig.data = pData; + sig.length = ulDataLen; + data.data = pSignature; + data.length = ulSignatureLen; + + hret = _hx509_verify_signature(context, + _hx509_get_cert(o->cert), + alg, + &data, + &sig); + if (hret) { + ret = CKR_GENERAL_ERROR; + goto out; + } + ret = CKR_OK; + + out: + return ret; +} + + +CK_RV +C_VerifyUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + INIT_CONTEXT(); + st_logf("VerifyUpdate\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_VerifyFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) +{ + INIT_CONTEXT(); + st_logf("VerifyFinal\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + +CK_RV +C_GenerateRandom(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR RandomData, + CK_ULONG ulRandomLen) +{ + INIT_CONTEXT(); + st_logf("GenerateRandom\n"); + VERIFY_SESSION_HANDLE(hSession, NULL); + return CKR_FUNCTION_NOT_SUPPORTED; +} + + +CK_FUNCTION_LIST funcs = { + { 2, 11 }, + C_Initialize, + C_Finalize, + C_GetInfo, + C_GetFunctionList, + C_GetSlotList, + C_GetSlotInfo, + C_GetTokenInfo, + C_GetMechanismList, + C_GetMechanismInfo, + C_InitToken, + (void *)func_not_supported, /* C_InitPIN */ + (void *)func_not_supported, /* C_SetPIN */ + C_OpenSession, + C_CloseSession, + C_CloseAllSessions, + C_GetSessionInfo, + (void *)func_not_supported, /* C_GetOperationState */ + (void *)func_not_supported, /* C_SetOperationState */ + C_Login, + C_Logout, + (void *)func_not_supported, /* C_CreateObject */ + (void *)func_not_supported, /* C_CopyObject */ + (void *)func_not_supported, /* C_DestroyObject */ + (void *)func_not_supported, /* C_GetObjectSize */ + C_GetAttributeValue, + (void *)func_not_supported, /* C_SetAttributeValue */ + C_FindObjectsInit, + C_FindObjects, + C_FindObjectsFinal, + (void *)func_not_supported, /* C_EncryptInit, */ + (void *)func_not_supported, /* C_Encrypt, */ + (void *)func_not_supported, /* C_EncryptUpdate, */ + (void *)func_not_supported, /* C_EncryptFinal, */ + (void *)func_not_supported, /* C_DecryptInit, */ + (void *)func_not_supported, /* C_Decrypt, */ + (void *)func_not_supported, /* C_DecryptUpdate, */ + (void *)func_not_supported, /* C_DecryptFinal, */ + C_DigestInit, + (void *)func_not_supported, /* C_Digest */ + (void *)func_not_supported, /* C_DigestUpdate */ + (void *)func_not_supported, /* C_DigestKey */ + (void *)func_not_supported, /* C_DigestFinal */ + C_SignInit, + C_Sign, + C_SignUpdate, + C_SignFinal, + (void *)func_not_supported, /* C_SignRecoverInit */ + (void *)func_not_supported, /* C_SignRecover */ + C_VerifyInit, + C_Verify, + C_VerifyUpdate, + C_VerifyFinal, + (void *)func_not_supported, /* C_VerifyRecoverInit */ + (void *)func_not_supported, /* C_VerifyRecover */ + (void *)func_not_supported, /* C_DigestEncryptUpdate */ + (void *)func_not_supported, /* C_DecryptDigestUpdate */ + (void *)func_not_supported, /* C_SignEncryptUpdate */ + (void *)func_not_supported, /* C_DecryptVerifyUpdate */ + (void *)func_not_supported, /* C_GenerateKey */ + (void *)func_not_supported, /* C_GenerateKeyPair */ + (void *)func_not_supported, /* C_WrapKey */ + (void *)func_not_supported, /* C_UnwrapKey */ + (void *)func_not_supported, /* C_DeriveKey */ + (void *)func_not_supported, /* C_SeedRandom */ + C_GenerateRandom, + (void *)func_not_supported, /* C_GetFunctionStatus */ + (void *)func_not_supported, /* C_CancelFunction */ + (void *)func_not_supported /* C_WaitForSlotEvent */ +}; diff --git a/lib/hx509/test_soft_pkcs11.c b/lib/hx509/test_soft_pkcs11.c new file mode 100644 index 000000000..98b69883a --- /dev/null +++ b/lib/hx509/test_soft_pkcs11.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "hx_locl.h" +#include "pkcs11.h" +#include + +static CK_RV +find_object(CK_SESSION_HANDLE session, + char *id, + CK_OBJECT_CLASS key_class, + CK_OBJECT_HANDLE_PTR object) +{ + CK_ULONG object_count; + CK_RV ret; + CK_ATTRIBUTE search_data[] = { + {CKA_ID, id, 0 }, + {CKA_CLASS, &key_class, sizeof(key_class)} + }; + CK_ULONG num_search_data = sizeof(search_data)/sizeof(search_data[0]); + + search_data[0].ulValueLen = strlen(id); + + ret = C_FindObjectsInit(session, search_data, num_search_data); + if (ret != CKR_OK) + return ret; + + ret = C_FindObjects(session, object, 1, &object_count); + if (ret != CKR_OK) + return ret; + if (object_count == 0) { + printf("found no object\n"); + return 1; + } + + ret = C_FindObjectsFinal(session); + if (ret != CKR_OK) + return ret; + + return CKR_OK; +} + +static char *sighash = "hej"; +static char signature[1024]; + + +int +main(int argc, char **argv) +{ + CK_SLOT_ID_PTR slot_ids; + CK_SLOT_ID slot; + CK_ULONG num_slots; + CK_RV ret; + CK_SLOT_INFO slot_info; + CK_TOKEN_INFO token_info; + CK_SESSION_HANDLE session; + CK_OBJECT_HANDLE public, private; + + C_Initialize(NULL_PTR); + + ret = C_GetSlotList(FALSE, NULL, &num_slots); + if (ret != CKR_OK) + errx(1, "C_GetSlotList1 failed: %d", (int)ret); + + if (num_slots == 0) + errx(1, "no slots"); + + if ((slot_ids = calloc(1, num_slots * sizeof(*slot_ids))) == NULL) + err(1, "alloc slots failed"); + + ret = C_GetSlotList(FALSE, slot_ids, &num_slots); + if (ret != CKR_OK) + errx(1, "C_GetSlotList2 failed: %d", (int)ret); + + slot = slot_ids[0]; + free(slot_ids); + + ret = C_GetSlotInfo(slot, &slot_info); + if (ret) + errx(1, "C_GetSlotInfo failed: %d", (int)ret); + + if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0) + errx(1, "no token present"); + + ret = C_OpenSession(slot, CKF_SERIAL_SESSION, NULL, NULL, &session); + if (ret != CKR_OK) + errx(1, "C_OpenSession failed: %d", (int)ret); + + ret = C_Login(session, CKU_USER, (unsigned char*)"foobar", 6); + if (ret != CKR_OK) + errx(1, "C_Login failed: %d", (int)ret); + + ret = C_GetTokenInfo(slot, &token_info); + if (ret) + errx(1, "C_GetTokenInfo failed: %d", (int)ret); + + if (token_info.flags & CKF_LOGIN_REQUIRED) + errx(1, "login required, even after C_Login"); + + ret = find_object(session, "cert", CKO_PUBLIC_KEY, &public); + if (ret != CKR_OK) + errx(1, "find cert failed: %d", (int)ret); + ret = find_object(session, "cert", CKO_PRIVATE_KEY, &private); + if (ret != CKR_OK) + errx(1, "find private key failed: %d", (int)ret); + + { + CK_ULONG ck_sigsize; + CK_MECHANISM mechanism; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_RSA_PKCS; + + ret = C_SignInit(session, &mechanism, private); + if (ret != CKR_OK) + return 1; + + ck_sigsize = sizeof(signature); + ret = C_Sign(session, (CK_BYTE *)sighash, strlen(sighash), + (CK_BYTE *)signature, &ck_sigsize); + if (ret != CKR_OK) { + printf("C_Sign failed with: %d\n", (int)ret); + return 1; + } + + ret = C_VerifyInit(session, &mechanism, public); + if (ret != CKR_OK) + return 1; + + ret = C_Verify(session, (CK_BYTE *)signature, ck_sigsize, + (CK_BYTE *)sighash, strlen(sighash)); + if (ret != CKR_OK) { + printf("message: %d\n", (int)ret); + return 1; + } + } + +#if 0 + { + CK_ULONG ck_sigsize, outsize; + CK_MECHANISM mechanism; + char outdata[1024]; + + memset(&mechanism, 0, sizeof(mechanism)); + mechanism.mechanism = CKM_RSA_PKCS; + + ret = C_EncryptInit(session, &mechanism, public); + if (ret != CKR_OK) + return 1; + + ck_sigsize = sizeof(signature); + ret = C_Encrypt(session, (CK_BYTE *)sighash, strlen(sighash), + (CK_BYTE *)signature, &ck_sigsize); + if (ret != CKR_OK) { + printf("message: %d\n", (int)ret); + return 1; + } + + ret = C_DecryptInit(session, &mechanism, private); + if (ret != CKR_OK) + return 1; + + outsize = sizeof(outdata); + ret = C_Decrypt(session, (CK_BYTE *)signature, ck_sigsize, + (CK_BYTE *)outdata, &outsize); + if (ret != CKR_OK) { + printf("message: %d\n", (int)ret); + return 1; + } + + if (memcmp(sighash, outdata, strlen(sighash)) != 0) + return 1; + } +#endif + + ret = C_CloseSession(session); + if (ret != CKR_OK) + return 1; + + C_Finalize(NULL_PTR); + + return 0; +} diff --git a/lib/hx509/tst-crypto-available1 b/lib/hx509/tst-crypto-available1 index 4e2082e01..71fa741d6 100644 --- a/lib/hx509/tst-crypto-available1 +++ b/lib/hx509/tst-crypto-available1 @@ -3,7 +3,7 @@ 1.2.840.113549.1.1.5 1.2.840.113549.1.1.4 1.2.840.113549.1.1.2 -1.2.840.113549.1.1.5 +1.2.752.43.16.1 2.16.840.1.101.3.4.2.1 1.3.14.3.2.26 1.2.840.113549.2.5 diff --git a/lib/hx509/tst-crypto-available3 b/lib/hx509/tst-crypto-available3 index ab14094ea..0b1a855ee 100644 --- a/lib/hx509/tst-crypto-available3 +++ b/lib/hx509/tst-crypto-available3 @@ -3,4 +3,4 @@ 1.2.840.113549.1.1.5 1.2.840.113549.1.1.4 1.2.840.113549.1.1.2 -1.2.840.113549.1.1.5 +1.2.752.43.16.1 diff --git a/lib/hx509/version-script.map b/lib/hx509/version-script.map index 1a85272d9..4e8331ebc 100644 --- a/lib/hx509/version-script.map +++ b/lib/hx509/version-script.map @@ -218,6 +218,8 @@ HEIMDAL_X509_1.0 { _hx509_cert_assign_key; _hx509_cert_private_key; _hx509_name_from_Name; + # pkcs11 symbols + C_*; local: *; };