krb5 commit: Add soft-pkcs11 source code

Greg Hudson ghudson at mit.edu
Wed Jun 26 01:36:32 EDT 2019


https://github.com/krb5/krb5/commit/a4bc3e513a58b0d1292f3506ac3b35be8c178086
commit a4bc3e513a58b0d1292f3506ac3b35be8c178086
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Jun 20 10:45:18 2019 -0400

    Add soft-pkcs11 source code
    
    soft-pkcs11 is no longer available upstream and is not generally
    packaged in distributions, making it inconvenient to use for tests.
    Import the 1.8 source code, detabified and with trailing whitespace
    removed but otherwise unmodified.

 src/tests/softpkcs11/main.c | 2049 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2049 insertions(+), 0 deletions(-)

diff --git a/src/tests/softpkcs11/main.c b/src/tests/softpkcs11/main.c
new file mode 100644
index 0000000..2acec51
--- /dev/null
+++ b/src/tests/softpkcs11/main.c
@@ -0,0 +1,2049 @@
+/*
+ * Copyright (c) 2004-2006, Stockholms universitet
+ * (Stockholm University, 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 university 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "locl.h"
+
+/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */
+
+#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;
+    union {
+        X509 *cert;
+        EVP_PKEY *public_key;
+        struct {
+            const char *file;
+            EVP_PKEY *key;
+            X509 *cert;
+        } private_key;
+    } u;
+};
+
+static struct soft_token {
+    CK_VOID_PTR application;
+    CK_NOTIFY notify;
+    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 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 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(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key)
+{
+    switch (key_type) {
+    case CKK_RSA: {
+        CK_BYTE *modulus = NULL;
+        size_t modulus_len = 0;
+        CK_ULONG modulus_bits = 0;
+        CK_BYTE *exponent = NULL;
+        size_t exponent_len = 0;
+
+        modulus_bits = BN_num_bits(key->pkey.rsa->n);
+
+        modulus_len = BN_num_bytes(key->pkey.rsa->n);
+        modulus = malloc(modulus_len);
+        BN_bn2bin(key->pkey.rsa->n, modulus);
+
+        exponent_len = BN_num_bytes(key->pkey.rsa->e);
+        exponent = malloc(exponent_len);
+        BN_bn2bin(key->pkey.rsa->e, exponent);
+
+        add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len);
+        add_object_attribute(o, 0, CKA_MODULUS_BITS,
+                             &modulus_bits, sizeof(modulus_bits));
+        add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT,
+                             exponent, exponent_len);
+
+        RSA_set_method(key->pkey.rsa, RSA_PKCS1_SSLeay());
+
+        free(modulus);
+        free(exponent);
+    }
+    default:
+        /* XXX */
+        break;
+    }
+    return CKR_OK;
+}
+
+
+static int
+pem_callback(char *buf, int num, int w, void *key)
+{
+    return -1;
+}
+
+
+static CK_RV
+add_certificate(char *label,
+                const char *cert_file,
+                const char *private_key_file,
+                char *id,
+                int anchor)
+{
+    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;
+    void *cert_data = NULL;
+    size_t cert_length;
+    void *subject_data = NULL;
+    size_t subject_length;
+    void *issuer_data = NULL;
+    size_t issuer_length;
+    void *serial_data = NULL;
+    size_t serial_length;
+    CK_RV ret = CKR_GENERAL_ERROR;
+    X509 *cert;
+    EVP_PKEY *public_key;
+
+    size_t id_len = strlen(id);
+
+    {
+        FILE *f;
+
+        f = fopen(cert_file, "r");
+        if (f == NULL) {
+            st_logf("failed to open file %s\n", cert_file);
+            return CKR_GENERAL_ERROR;
+        }
+
+        cert = PEM_read_X509(f, NULL, NULL, NULL);
+        fclose(f);
+        if (cert == NULL) {
+            st_logf("failed reading PEM cert\n");
+            return CKR_GENERAL_ERROR;
+        }
+
+        OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret);
+        if (ret)
+            goto out;
+
+        OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length,
+                                   X509_get_issuer_name(cert), ret);
+        if (ret)
+            goto out;
+
+        OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length,
+                                   X509_get_subject_name(cert), ret);
+        if (ret)
+            goto out;
+
+        OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length,
+                                   X509_get_serialNumber(cert), ret);
+        if (ret)
+            goto out;
+
+    }
+
+    st_logf("done parsing, adding to internal structure\n");
+
+    o = add_st_object();
+    if (o == NULL) {
+        ret = CKR_DEVICE_MEMORY;
+        goto out;
+    }
+    o->type = STO_T_CERTIFICATE;
+    o->u.cert = cert;
+    public_key = X509_get_pubkey(o->u.cert);
+
+    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;
+    }
+
+    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, label, strlen(label));
+
+    add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type));
+    add_object_attribute(o, 0, CKA_ID, id, id_len);
+
+    add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length);
+    add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length);
+    add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length);
+    add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length);
+    if (anchor)
+        add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true));
+    else
+        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->u.public_key = public_key;
+
+    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, label, strlen(label));
+
+    add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
+    add_object_attribute(o, 0, CKA_ID, id, id_len);
+    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, subject_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(o, key_type, public_key);
+
+    st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o));
+
+    if (private_key_file) {
+        CK_FLAGS flags;
+        FILE *f;
+
+        o = add_st_object();
+        if (o == NULL) {
+            ret = CKR_DEVICE_MEMORY;
+            goto out;
+        }
+        o->type = STO_T_PRIVATE_KEY;
+        o->u.private_key.file = strdup(private_key_file);
+        o->u.private_key.key = NULL;
+
+        o->u.private_key.cert = 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, label, strlen(label));
+
+        add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type));
+        add_object_attribute(o, 0, CKA_ID, id, id_len);
+        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, subject_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(o, key_type, public_key);
+
+        f = fopen(private_key_file, "r");
+        if (f == NULL) {
+            st_logf("failed to open private key\n");
+            return CKR_GENERAL_ERROR;
+        }
+
+        o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL);
+        fclose(f);
+        if (o->u.private_key.key == NULL) {
+            st_logf("failed to read private key a startup\n");
+            /* don't bother with this failure for now,
+               fix it at C_Login time */;
+        } else {
+            /* XXX verify keytype */
+
+            if (key_type == CKK_RSA)
+                RSA_set_method(o->u.private_key.key->pkey.rsa,
+                               RSA_PKCS1_SSLeay());
+
+            if (X509_check_private_key(cert, o->u.private_key.key) != 1) {
+                EVP_PKEY_free(o->u.private_key.key);
+                o->u.private_key.key = NULL;
+                st_logf("private key doesn't verify\n");
+            } else {
+                st_logf("private key usable\n");
+                soft_token.flags.login_done = 1;
+            }
+        }
+    }
+
+    ret = CKR_OK;
+ out:
+    if (ret != CKR_OK) {
+        st_logf("something went wrong when adding cert!\n");
+
+        /* XXX wack o */;
+    }
+    free(cert_data);
+    free(serial_data);
+    free(issuer_data);
+    free(subject_data);
+
+    return ret;
+}
+
+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 void
+read_conf_file(const char *fn)
+{
+    char buf[1024], *cert, *key, *id, *label, *s, *p;
+    int anchor;
+    FILE *f;
+
+    f = fopen(fn, "r");
+    if (f == NULL) {
+        st_logf("can't open configuration file %s\n", fn);
+        return;
+    }
+
+    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;
+        id = strtok_r(p, "\t", &s);
+        if (id == NULL)
+            continue;
+        label = strtok_r(NULL, "\t", &s);
+        if (label == NULL)
+            continue;
+        cert = strtok_r(NULL, "\t", &s);
+        if (cert == NULL)
+            continue;
+        key = strtok_r(NULL, "\t", &s);
+
+        /* XXX */
+        if (strcmp(id, "anchor") == 0) {
+            id = "\x00\x00";
+            anchor = 1;
+        }
+
+        st_logf("adding: %s\n", label);
+
+        add_certificate(label, cert, key, id, anchor);
+    }
+}
+
+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;
+    st_logf("Initialize\n");
+    int i;
+
+    OpenSSL_add_all_algorithms();
+    ERR_load_crypto_strings();
+
+    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");
+        }
+
+        read_conf_file(fn);
+        free(fn);
+    }
+
+    return CKR_OK;
+}
+
+CK_RV
+C_Finalize(CK_VOID_PTR args)
+{
+    int i;
+
+    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)
+{
+    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)
+{
+    *ppFunctionList = &funcs;
+    return CKR_OK;
+}
+
+CK_RV
+C_GetSlotList(CK_BBOOL tokenPresent,
+              CK_SLOT_ID_PTR pSlotList,
+              CK_ULONG_PTR   pulCount)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+    st_logf("GetMechanismList\n");
+
+    *pulCount = 2;
+    if (pMechanismList == NULL_PTR)
+        return CKR_OK;
+    pMechanismList[0] = CKM_RSA_X_509;
+    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)
+{
+    st_logf("GetMechanismInfo: slot %d type: %d\n",
+            (int)slotID, (int)type);
+    return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_InitToken(CK_SLOT_ID slotID,
+            CK_UTF8CHAR_PTR pPin,
+            CK_ULONG ulPinLen,
+            CK_UTF8CHAR_PTR pLabel)
+{
+    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;
+
+    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;
+    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;
+
+    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");
+
+    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;
+    int i;
+
+    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);
+    }
+
+    for (i = 0; i < soft_token.object.num_objs; i++) {
+        struct st_object *o = soft_token.object.objs[i];
+        FILE *f;
+
+        if (o->type != STO_T_PRIVATE_KEY)
+            continue;
+
+        if (o->u.private_key.key)
+            continue;
+
+        f = fopen(o->u.private_key.file, "r");
+        if (f == NULL) {
+            st_logf("can't open private file: %s\n", o->u.private_key.file);
+            continue;
+        }
+
+        o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin);
+        fclose(f);
+        if (o->u.private_key.key == NULL) {
+            st_logf("failed to read key: %s error: %s\n",
+                    o->u.private_key.file,
+                    ERR_error_string(ERR_get_error(), NULL));
+            /* just ignore failure */;
+            continue;
+        }
+
+        /* XXX check keytype */
+        RSA_set_method(o->u.private_key.key->pkey.rsa, RSA_PKCS1_SSLeay());
+
+        if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) {
+            EVP_PKEY_free(o->u.private_key.key);
+            o->u.private_key.key = NULL;
+            st_logf("private key %s doesn't verify\n", o->u.private_key.file);
+            continue;
+        }
+
+        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");
+    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");
+    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;
+
+    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");
+
+    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;
+        size_t len;
+
+        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;
+
+    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;
+
+    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;
+}
+
+
+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, CKM_RSA_X_509 };
+    CK_BBOOL bool_true = CK_TRUE;
+    CK_ATTRIBUTE attr[] = {
+        { CKA_ENCRYPT, &bool_true, sizeof(bool_true) }
+    };
+    struct st_object *o;
+    CK_RV ret;
+
+    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)
+{
+    struct session_state *state;
+    struct st_object *o;
+    void *buffer = NULL;
+    CK_RV ret;
+    RSA *rsa;
+    int padding, len, buffer_len, padding_len;
+
+    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;
+    case CKM_RSA_X_509:
+        padding = RSA_NO_PADDING;
+        padding_len = 0;
+        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);
+    }
+    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)
+{
+    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)
+{
+    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, CKM_RSA_X_509 };
+    CK_BBOOL bool_true = CK_TRUE;
+    CK_ATTRIBUTE attr[] = {
+        { CKA_DECRYPT, &bool_true, sizeof(bool_true) }
+    };
+    struct st_object *o;
+    CK_RV ret;
+
+    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)
+{
+    struct session_state *state;
+    struct st_object *o;
+    void *buffer = NULL;
+    CK_RV ret;
+    RSA *rsa;
+    int padding, len, buffer_len, padding_len;
+
+    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;
+    case CKM_RSA_X_509:
+        padding = RSA_NO_PADDING;
+        padding_len = 0;
+        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);
+    }
+    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");
+    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");
+    VERIFY_SESSION_HANDLE(hSession, NULL);
+    return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+C_DigestInit(CK_SESSION_HANDLE hSession,
+             CK_MECHANISM_PTR pMechanism)
+{
+    st_logf("DigestInit\n");
+    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, CKM_RSA_X_509 };
+    CK_BBOOL bool_true = CK_TRUE;
+    CK_ATTRIBUTE attr[] = {
+        { CKA_SIGN, &bool_true, sizeof(bool_true) }
+    };
+    struct st_object *o;
+    CK_RV ret;
+
+    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;
+    void *buffer = NULL;
+    CK_RV ret;
+    RSA *rsa;
+    int padding, len, buffer_len, padding_len;
+
+    st_logf("Sign\n");
+    VERIFY_SESSION_HANDLE(hSession, &state);
+
+    if (state->sign_object == -1)
+        return CKR_ARGUMENTS_BAD;
+
+    o = soft_token.object.objs[state->sign_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;
+    }
+
+    switch(state->sign_mechanism->mechanism) {
+    case CKM_RSA_PKCS:
+        padding = RSA_PKCS1_PADDING;
+        padding_len = RSA_PKCS1_PADDING_SIZE;
+        break;
+    case CKM_RSA_X_509:
+        padding = RSA_NO_PADDING;
+        padding_len = 0;
+        break;
+    default:
+        ret = CKR_FUNCTION_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (buffer_len < ulDataLen + padding_len) {
+        ret = CKR_ARGUMENTS_BAD;
+        goto out;
+    }
+
+    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;
+    }
+
+    len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding);
+    st_logf("private encrypt done\n");
+    if (len <= 0) {
+        ret = CKR_DEVICE_ERROR;
+        goto out;
+    }
+    if (len > buffer_len)
+        abort();
+
+    if (pSignature != NULL_PTR)
+        memcpy(pSignature, buffer, len);
+    *pulSignatureLen = len;
+
+    ret = CKR_OK;
+
+ out:
+    if (buffer) {
+        memset(buffer, 0, buffer_len);
+        free(buffer);
+    }
+    return ret;
+}
+
+CK_RV
+C_SignUpdate(CK_SESSION_HANDLE hSession,
+             CK_BYTE_PTR pPart,
+             CK_ULONG ulPartLen)
+{
+    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)
+{
+    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, CKM_RSA_X_509 };
+    CK_BBOOL bool_true = CK_TRUE;
+    CK_ATTRIBUTE attr[] = {
+        { CKA_VERIFY, &bool_true, sizeof(bool_true) }
+    };
+    struct st_object *o;
+    CK_RV ret;
+
+    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;
+    void *buffer = NULL;
+    CK_RV ret;
+    RSA *rsa;
+    int padding, len, buffer_len;
+
+    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];
+
+    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->verify_mechanism->mechanism) {
+    case CKM_RSA_PKCS:
+        padding = RSA_PKCS1_PADDING;
+        break;
+    case CKM_RSA_X_509:
+        padding = RSA_NO_PADDING;
+        break;
+    default:
+        ret = CKR_FUNCTION_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (buffer_len < ulDataLen) {
+        ret = CKR_ARGUMENTS_BAD;
+        goto out;
+    }
+
+    if (pSignature == NULL) {
+        st_logf("signature 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_decrypt(ulDataLen, pData, buffer, rsa, padding);
+    st_logf("private encrypt done\n");
+    if (len <= 0) {
+        ret = CKR_DEVICE_ERROR;
+        goto out;
+    }
+    if (len > buffer_len)
+        abort();
+
+    if (len != ulSignatureLen) {
+        ret = CKR_GENERAL_ERROR;
+        goto out;
+    }
+
+    if (memcmp(pSignature, buffer, len) != 0) {
+        ret = CKR_GENERAL_ERROR;
+        goto out;
+    }
+
+ out:
+    if (buffer) {
+        memset(buffer, 0, buffer_len);
+        free(buffer);
+    }
+    return ret;
+}
+
+
+CK_RV
+C_VerifyUpdate(CK_SESSION_HANDLE hSession,
+               CK_BYTE_PTR pPart,
+               CK_ULONG ulPartLen)
+{
+    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)
+{
+    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)
+{
+    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,
+    C_EncryptInit,
+    C_Encrypt,
+    C_EncryptUpdate,
+    C_EncryptFinal,
+    C_DecryptInit,
+    C_Decrypt,
+    C_DecryptUpdate,
+    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 */
+};


More information about the cvs-krb5 mailing list