aboutsummaryrefslogtreecommitdiffstats
path: root/tkd/pksign.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkd/pksign.c')
-rw-r--r--tkd/pksign.c707
1 files changed, 707 insertions, 0 deletions
diff --git a/tkd/pksign.c b/tkd/pksign.c
new file mode 100644
index 000000000..ade6bfe7d
--- /dev/null
+++ b/tkd/pksign.c
@@ -0,0 +1,707 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <gcrypt.h>
+
+#include "pkcs11.h"
+
+/*
+ * For now, we don't COMPUTE_KEYGRIP, assuming Scute.
+ * ... which used the ID as keygrip.
+ */
+
+#ifdef COMPUTE_KEYGRIP
+static void
+compute_keygrip_rsa (char *keygrip,
+ const char *modulus, unsigned long modulus_len,
+ const char *exponent, unsigned long exponent_len)
+{
+ ;
+}
+
+static void
+compute_keygrip_ec (char *keygrip,
+ const char *oid, unsigned long oid_len,
+ const char *ecpoint, unsigned long ecpoing_len)
+{
+ ;
+}
+#endif
+
+#define ck_function_list _CK_FUNCTION_LIST
+#define ck_token_info _CK_TOKEN_INFO
+#define ck_attribute _CK_ATTRIBUTE
+
+#define ck_mechanism _CK_MECHANISM
+#define parameter pParameter
+#define parameter_len ulParameterLen
+
+#define ck_slot_id_t CK_SLOT_ID
+#define ck_session_handle_t CK_SESSION_HANDLE
+#define ck_notification_t CK_NOTIFICATION
+#define ck_flags_t CK_FLAGS
+#define ck_object_handle_t CK_OBJECT_HANDLE
+#define ck_mechanism_type_t CK_MECHANISM_TYPE
+
+/*
+ * d_list -> dev
+ * session -> key_list -> key
+ *
+ */
+#define KEYGRIP_LEN (40+1)
+
+/*
+ * Major use cases:
+ * a few keys (two or three at maximum)
+ * with a single device, which only has one slot.
+ *
+ * So, static fixed allocation is better.
+ */
+#define MAX_KEYS 10
+#define MAX_SLOTS 10
+
+enum key_type {
+ KEY_RSA,
+ KEY_EC,
+ KEY_EDDSA,
+};
+
+struct key {
+ struct token *token; /* Back pointer. */
+ int valid;
+ ck_object_handle_t p11_keyid;
+ char keygrip[KEYGRIP_LEN];
+ int key_type;
+ /* Possibly, ID, LABEL, etc. */
+ /* Allowed mechanisms??? */
+};
+
+struct token {
+ struct cryptoki *ck; /* Back pointer. */
+ int valid;
+ ck_slot_id_t slot_id;
+ int login_required;
+ ck_session_handle_t session;
+ int num_keys;
+ struct key key_list[MAX_KEYS];
+};
+
+struct cryptoki {
+ struct ck_function_list *f;
+ int num_slots;
+ struct token token_list[MAX_SLOTS];
+};
+
+/* Possibly, we will extend this to support multiple PKCS#11 modules.
+ * For now, it's only one.
+ */
+static struct cryptoki ck_instance[1];
+
+
+static long
+get_function_list (struct cryptoki *ck, const char *libname)
+{
+ unsigned long err = 0;
+ unsigned long (*p_func) (struct ck_function_list **);
+ void *handle;
+
+ handle = dlopen (libname, RTLD_NOW);
+ if (handle == NULL)
+ {
+ return -1;
+ }
+
+ p_func = (CK_C_GetFunctionList)dlsym (handle, "C_GetFunctionList");
+ if (p_func == NULL)
+ {
+ return -1;
+ }
+ err = p_func (&ck->f);
+
+ if (err || ck->f == NULL)
+ {
+ return -1;
+ }
+
+ err = ck->f->C_Initialize (NULL);
+ if (err)
+ {
+ return -1;
+ }
+
+ /* For now, we never call dlclose to unload the LIBNAME */
+ return 0;
+}
+
+static long
+get_slot_list (struct cryptoki *ck,
+ unsigned long *num_slot_p,
+ ck_slot_id_t *slot_list)
+{
+ unsigned long err = 0;
+
+ /* Scute requires first call with NULL, to rescan. */
+ err = ck->f->C_GetSlotList (TRUE, NULL, num_slot_p);
+ if (err)
+ return err;
+
+ err = ck->f->C_GetSlotList (TRUE, slot_list, num_slot_p);
+ if (err)
+ {
+ return err;
+ }
+
+ return 0;
+}
+
+static long
+get_token_info (struct token *token,
+ struct ck_token_info *tk_info)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+ ck_slot_id_t slot_id = token->slot_id;
+
+ err = ck->f->C_GetTokenInfo (slot_id, tk_info);
+ if (err)
+ {
+ return err;
+ }
+
+ return 0;
+}
+
+/* XXX Implement some useful things to be notified... */
+struct p11dev {
+ int d;
+};
+
+static struct p11dev priv;
+
+static unsigned long
+notify_cb (ck_session_handle_t session,
+ ck_notification_t event, void *application)
+{
+ struct p11dev *priv = application;
+
+ (void)priv;
+ return 0;
+}
+
+static long
+open_session (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+ ck_slot_id_t slot_id = token->slot_id;
+ ck_session_handle_t session_handle;
+ ck_flags_t session_flags;
+ int option = 0;
+
+ session_flags = CKU_USER;
+ // session_flags = session_flags | CKF_RW_SESSION;
+ session_flags = session_flags | CKF_SERIAL_SESSION;
+
+ err = ck->f->C_OpenSession (slot_id, session_flags,
+ (void *)&priv, notify_cb, &session_handle);
+ if (err)
+ {
+ printf ("open_session: %d\n", err);
+ return -1;
+ }
+
+ token->session = session_handle;
+ token->valid = 1;
+ token->num_keys = 0;
+
+ return 0;
+}
+
+static long
+close_session (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ if (!token->valid)
+ return -1;
+
+ err = ck->f->C_CloseSession (token->session);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+login (struct token *token,
+ const unsigned char *pin, int pin_len)
+{
+ unsigned long err = 0;
+ unsigned long user_type = CKU_USER;
+ struct cryptoki *ck = token->ck;
+
+ err = ck->f->C_Login (token->session, user_type,
+ (unsigned char *)pin, pin_len);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+logout (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ err = ck->f->C_Logout (token->session);
+ if (err)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static long
+detect_key (int try_private, struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ struct ck_attribute templ[8];
+
+ unsigned char label[256];
+ unsigned long class;
+ unsigned long key_type;
+ unsigned long mechanisms[3];
+ unsigned char id[256];
+ unsigned char modulus[1024];
+ unsigned char exponent[8];
+ unsigned char ecparams[256];
+ unsigned char ecpoint[256];
+
+ unsigned long cnt = 0;
+ ck_object_handle_t obj;
+ int i;
+
+ if (try_private)
+ class = CKO_PRIVATE_KEY;
+ else
+ class = CKO_PUBLIC_KEY;
+ templ[0].type = CKA_CLASS;
+ templ[0].pValue = (void *)&class;
+ templ[0].ulValueLen = sizeof (class);
+
+ token->num_keys = 0;
+
+ err = ck->f->C_FindObjectsInit (token->session, templ, 1);
+ if (!err)
+ {
+ while (TRUE)
+ {
+ unsigned long any;
+ struct key *k = &token->key_list[cnt]; /* Allocate a key. */
+
+ k->token = token;
+ k->valid = 0;
+
+ /* Portable way to get objects... is get it one by one. */
+ err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
+ if (err || any == 0)
+ break;
+
+ templ[0].type = CKA_LABEL;
+ templ[0].pValue = (void *)label;
+ templ[0].ulValueLen = sizeof (label);
+
+ templ[1].type = CKA_KEY_TYPE;
+ templ[1].pValue = &key_type;
+ templ[1].ulValueLen = sizeof (key_type);
+
+ templ[2].type = CKA_ID;
+ templ[2].pValue = (void *)id;
+ templ[2].ulValueLen = sizeof (id);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
+ if (err)
+ {
+ continue;
+ }
+
+ label[templ[0].ulValueLen] = 0;
+ id[templ[2].ulValueLen] = 0;
+
+ printf ("handle: %ld label: %s key_type: %d id: %s\n",
+ obj, label, key_type, id);
+
+ templ[0].type = CKA_ALLOWED_MECHANISMS;
+ templ[0].pValue = (void *)mechanisms;
+ templ[0].ulValueLen = sizeof (mechanisms);
+
+ if (key_type == CKK_RSA)
+ {
+ templ[1].type = CKA_MODULUS;
+ templ[1].pValue = (void *)modulus;
+ templ[1].ulValueLen = sizeof (modulus);
+
+ templ[2].type = CKA_PUBLIC_EXPONENT;
+ templ[2].pValue = (void *)exponent;
+ templ[2].ulValueLen = sizeof (exponent);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
+ if (err)
+ {
+ continue;
+ }
+
+ /* Found a RSA key. */
+ printf ("RSA: %d %d %d\n",
+ templ[0].ulValueLen,
+ templ[1].ulValueLen,
+ templ[2].ulValueLen);
+ puts ("Public key:");
+ for (i = 0; i < templ[1].ulValueLen; i++)
+ {
+ printf ("%02x", modulus[i]);
+ if ((i % 16) == 15)
+ puts ("");
+ }
+ puts ("");
+
+ k->valid = 1;
+ k->p11_keyid = obj;
+ k->key_type = KEY_RSA;
+#ifdef COMPUTE_KEYGRIP
+ compute_keygrip_rsa (k->keygrip,
+ modulus, templ[0].ulValueLen,
+ exponent, templ[1].ulValueLen);
+#else
+ memcpy (k->keygrip, id, 40);
+ k->keygrip[40] = 0;
+#endif
+ cnt++;
+ }
+ else if (key_type == CKK_EC)
+ {
+ templ[1].type = CKA_EC_PARAMS;
+ templ[1].pValue = ecparams;
+ templ[1].ulValueLen = sizeof (ecparams);
+
+ templ[2].type = CKA_EC_POINT;
+ templ[2].pValue = (void *)ecpoint;
+ templ[2].ulValueLen = sizeof (ecpoint);
+
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 3);
+ if (err)
+ {
+ continue;
+ }
+
+ /* Found an ECC key. */
+ printf ("ECC: %d %d %d\n",
+ templ[0].ulValueLen,
+ templ[1].ulValueLen,
+ templ[2].ulValueLen);
+ puts ("Public key:");
+ for (i = 0; i < templ[2].ulValueLen; i++)
+ {
+ printf ("%02x", ecpoint[i]);
+ if ((i % 16) == 15)
+ puts ("");
+ }
+ puts ("");
+
+ k->valid = 1;
+ k->p11_keyid = obj;
+ k->key_type = KEY_EC;
+#ifdef COMPUTE_KEYGRIP
+ compute_keygrip_ec (k->keygrip,
+ ecparams, templ[1].ulValueLen,
+ ecpoint, templ[2].ulValueLen);
+#else
+ memcpy (k->keygrip, id, 40);
+ k->keygrip[40] = 0;
+#endif
+ cnt++;
+ }
+ }
+
+ token->num_keys = cnt;
+ err = ck->f->C_FindObjectsFinal (token->session);
+ if (err)
+ {
+ return -1;
+ }
+ }
+}
+
+static long
+get_certificate (struct token *token)
+{
+ unsigned long err = 0;
+ struct cryptoki *ck = token->ck;
+
+ struct ck_attribute templ[1];
+
+ unsigned long class;
+ unsigned char certificate[4096];
+ unsigned long cert_len;
+ int certificate_available;
+
+ ck_object_handle_t obj;
+ int i;
+
+ class = CKO_CERTIFICATE;
+ templ[0].type = CKA_CLASS;
+ templ[0].pValue = (void *)&class;
+ templ[0].ulValueLen = sizeof (class);
+
+ err = ck->f->C_FindObjectsInit (token->session, templ, 1);
+ if (!err)
+ {
+ while (TRUE)
+ {
+ unsigned long any;
+
+ /* Portable way to get objects... is get it one by one. */
+ err = ck->f->C_FindObjects (token->session, &obj, 1, &any);
+ if (err || any == 0)
+ break;
+
+ templ[0].type = CKA_VALUE;
+ templ[0].pValue = (void *)certificate;
+ templ[0].ulValueLen = sizeof (certificate);
+ err = ck->f->C_GetAttributeValue (token->session, obj, templ, 1);
+ if (err)
+ certificate_available = 0;
+ else
+ {
+ certificate_available = 1;
+ cert_len = templ[0].ulValueLen;
+
+ puts ("Certificate available:");
+ for (i = 0; i < cert_len; i++)
+ {
+ printf ("%02x", certificate[i]);
+ if ((i % 16) == 15)
+ puts ("");
+ }
+ puts ("");
+ }
+ }
+
+ err = ck->f->C_FindObjectsFinal (token->session);
+ if (err)
+ {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static long
+learn_keys (struct token *token)
+{
+ unsigned long err = 0;
+
+ detect_key (1, token);
+#if 0
+ if (token->num_keys == 0)
+ detect_key (0, token);
+#endif
+ get_certificate (token);
+
+ return 0;
+}
+
+
+static long
+find_key (struct cryptoki *ck, const char *keygrip, struct key **r_key)
+{
+ int i;
+ int j;
+
+ *r_key = NULL;
+ for (i = 0; i < ck->num_slots; i++)
+ {
+ struct token *token = &ck->token_list[i];
+
+ for (j = 0; j < token->num_keys; j++)
+ {
+ struct key *k = &token->key_list[j];
+
+ if (memcmp (k->keygrip, keygrip, 40) == 0)
+ {
+ *r_key = k;
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static long
+do_pksign (struct key *key,
+ const unsigned char *u_data, unsigned long u_data_len,
+ unsigned char *r_signature,
+ unsigned long *r_signature_len)
+{
+ unsigned long err = 0;
+ struct token *token = key->token;
+ struct cryptoki *ck = token->ck;
+ ck_mechanism_type_t mechanism;
+ struct ck_mechanism mechanism_struct;
+ unsigned char data[1024];
+ unsigned long data_len;
+
+ if (key->key_type == KEY_RSA)
+ {
+ size_t asnlen = sizeof (data);
+
+ gcry_md_get_asnoid (GCRY_MD_SHA256, data, &asnlen);
+ gcry_md_hash_buffer (GCRY_MD_SHA256, data+asnlen,
+ u_data, u_data_len);
+ data_len = asnlen+gcry_md_get_algo_dlen (GCRY_MD_SHA256);
+
+ mechanism = CKM_RSA_PKCS;
+ }
+ else if (key->key_type == KEY_EC)
+ mechanism = CKM_ECDSA_SHA256;
+ else if (key->key_type == KEY_EDDSA)
+ mechanism = CKM_EDDSA;
+
+ mechanism_struct.mechanism = mechanism;
+ mechanism_struct.parameter = NULL;
+ mechanism_struct.parameter_len = 0;
+
+ err = ck->f->C_SignInit (token->session, &mechanism_struct,
+ key->p11_keyid);
+
+ err = ck->f->C_Sign (token->session,
+ data, data_len,
+ r_signature, r_signature_len);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+int
+main (int argc, const char *argv[])
+{
+ long r;
+ struct cryptoki *ck = ck_instance;
+ unsigned long num_slots = MAX_SLOTS;
+ ck_slot_id_t slot_list[MAX_SLOTS];
+ int i;
+ const unsigned char *pin = NULL;
+ int pin_len = -1;
+ const char *keygrip = NULL;
+ int num_tokens = 0;
+
+ r = get_function_list (ck, argv[1]);
+ if (r)
+ {
+ return 1;
+ }
+
+ if (argc >= 3)
+ keygrip = argv[2];
+
+ if (argc >= 4)
+ {
+ pin = argv[3];
+ pin_len = strlen (argv[3]);
+ }
+
+ r = get_slot_list (ck, &num_slots, slot_list);
+ if (r)
+ {
+ return 1;
+ }
+
+ for (i = 0; i < num_slots; i++)
+ {
+ struct ck_token_info tk_info;
+ struct token *token = &ck->token_list[num_tokens]; /* Allocate one token in CK */
+
+ token->ck = ck;
+ token->valid = 0;
+ token->slot_id = slot_list[i];
+
+ if (get_token_info (token, &tk_info) == 0)
+ {
+ if ((tk_info.flags & CKF_TOKEN_INITIALIZED) == 0
+ || (tk_info.flags & CKF_USER_PIN_LOCKED) != 0)
+ continue;
+
+ token->login_required = (tk_info.flags & CKF_LOGIN_REQUIRED);
+
+ r = open_session (token);
+ if (r)
+ {
+ printf ("Error at open_session: %d\n", r);
+ continue;
+ }
+
+ /* XXX: Support each PIN for each token. */
+ if (pin)
+ login (token, pin, pin_len);
+
+ puts ("************");
+ num_tokens++;
+ r = learn_keys (token);
+ }
+ }
+
+ ck->num_slots = num_tokens;
+
+ if (keygrip)
+ {
+ struct key *k;
+
+ r = find_key (ck, keygrip, &k);
+ if (!r)
+ {
+ unsigned char sig[1024];
+ unsigned long siglen = sizeof (sig);
+
+ r = do_pksign (k, "test test", 9, sig, &siglen);
+ if (!r)
+ {
+ int i;
+
+ for (i = 0; i < siglen; i++)
+ printf ("%02x", sig[i]);
+ puts ("");
+ }
+ }
+ }
+
+ for (i = 0; i < num_slots; i++)
+ {
+ struct token *token = &ck->token_list[i];
+
+ close_session (token);
+ }
+
+ ck->f->C_Finalize (NULL);
+ return 0;
+}
+
+/*
+cc -g -o test_tk pksign.c -lgcrypt
+./test_tk /usr/lib/softhsm/libsofthsm2.so <KEYGRIP> 5678
+./test_tk /usr/local/lib/x86_64-linux-gnu/scute.so <KEYGRIP>
+ */