[git] Scute - branch, master, updated. scute-1.5.0-21-g4fc9da0
by Werner Koch
cvs at cvs.gnupg.org
Mon Feb 25 08:26:45 CET 2019
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PKCS#11 token on top of gpg-agent".
The branch, master has been updated
via 4fc9da05d490b8d4d0d0714d51781d7e2521bbc6 (commit)
from a3dc060e32a52e3a094c3af84e432c832b511da9 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 4fc9da05d490b8d4d0d0714d51781d7e2521bbc6
Author: Werner Koch <wk at gnupg.org>
Date: Mon Feb 25 08:23:34 2019 +0100
Implement decryption.
* src/agent.h (struct key_info_s, key_info_t): Move to ...
* src/cert.h (struct key_info_s): here.
* src/agent.c (learn_status_cb): Set usage info.
(scute_agent_learn): Use "SCD LEARN" instead of "LEARN"
(struct pkdecrypt_parm_s): New.
(pkdecrypt_data_cb): New.
(pkdecrypt_inq_cb): New.
(pkdecrypt_parse_result): New.
(scute_agent_decrypt): New.
* src/cert-object.c (scute_attr_prv): Set decrypt flag. Replace arg
'grip' by 'kinfo'. Change callers.
* src/gpgsm.c (struct search_cb_parm): Replace 'grip' by 'kinfo'.
(scute_gpgsm_get_cert): Replace args 'grip' and 'certref' by 'kinfo'.
* src/slots.c (struct session): Add 'decryption_key'. Change callers.
(slot_create_session): Init decryption_key.
(session_sign): Allow NULL for PSIGNATURE but still return the
required length.
(session_init_decrypt): New.
(session_decrypt): New.
--
Tested with a Yubikey PIV card. Requires recent fix for gpg-agent to
allow for the flags parameter in the decrypt input. That parameter is
actually suggested by Libgcrypt.
Signed-off-by: Werner Koch <wk at gnupg.org>
diff --git a/src/agent.c b/src/agent.c
index 6228af9..c168a2e 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -1,5 +1,5 @@
/* agent.c - Talking to gpg-agent.
- * Copyright (C) 2006, 2007, 2008, 2015 g10 Code GmbH
+ * Copyright (C) 2006, 2007, 2008, 2015, 2019 g10 Code GmbH
*
* This file is part of Scute.
*
@@ -65,7 +65,7 @@ gnupg_allow_set_foregound_window (pid_t pid)
return;
#ifdef HAVE_W32_SYSTEM
else if (!AllowSetForegroundWindow (pid))
- DEBUG (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %u\n",
+ DEBUG (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %\n",
(unsigned long)pid, (unsigned int)GetLastError ());
#endif
}
@@ -642,15 +642,35 @@ learn_status_cb (void *opaque, const char *line)
else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
{
/* The format of such a line is:
- * KEYPARINFO <hexgrip> <keyref>
+ * KEYPAIRINFO <hexgrip> <keyref> [<usage>]
*/
const char *hexgrip = line;
+ char *line_buffer, *p;
+ const char *usage;
while (*line && !spacep (line))
line++;
while (spacep (line))
line++;
- keyref = line;
+
+ p = line_buffer = strdup (line);
+ if (!line_buffer)
+ goto no_core;
+ keyref = line_buffer;
+ while (*p && !spacep (p))
+ p++;
+ if (*p)
+ {
+ *p++ = 0;
+ while (spacep (p))
+ p++;
+ usage = p;
+ while (*p && !spacep (p))
+ p++;
+ *p = 0;
+ }
+ else
+ usage = "";
if (hexgrip_valid_p (hexgrip))
{
@@ -662,12 +682,26 @@ learn_status_cb (void *opaque, const char *line)
if (!kinfo)
goto no_core;
}
- else /* Existing entry - clear the grip. */
- *kinfo->grip = 0;
+ else /* Existing entry - clear grip and usage. */
+ {
+ *kinfo->grip = 0;
+ memset (&kinfo->usage, 0, sizeof kinfo->usage);
+ }
strncpy (kinfo->grip, hexgrip, sizeof kinfo->grip);
kinfo->grip[sizeof kinfo->grip -1] = 0;
+ for (; *usage; usage++)
+ {
+ switch (*usage)
+ {
+ case 's': kinfo->usage.sign = 1; break;
+ case 'c': kinfo->usage.cert = 1; break;
+ case 'a': kinfo->usage.auth = 1; break;
+ case 'e': kinfo->usage.encr = 1; break;
+ }
+ }
}
+ free (line_buffer);
}
else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen))
{
@@ -707,7 +741,7 @@ scute_agent_learn (struct agent_card_info_s *info)
gpg_error_t err;
memset (info, 0, sizeof (*info));
- err = assuan_transact (agent_ctx, "LEARN --sendinfo",
+ err = assuan_transact (agent_ctx, "SCD LEARN --force",
NULL, NULL,
default_inq_cb, NULL,
learn_status_cb, info);
@@ -720,7 +754,7 @@ scute_agent_learn (struct agent_card_info_s *info)
if (!err)
{
memset (info, 0, sizeof (*info));
- err = assuan_transact (agent_ctx, "LEARN --sendinfo",
+ err = assuan_transact (agent_ctx, "SCD LEARN --force",
NULL, NULL,
default_inq_cb, NULL,
learn_status_cb, info);
@@ -998,7 +1032,7 @@ decode_hash (const unsigned char *data, int len,
/* Call the agent to sign (DATA,LEN) using the key described by
- * HEXGRIP. Stores the signature in SIG_RESULT and its lengtn at
+ * HEXGRIP. Stores the signature in SIG_RESULT and its length at
* SIG_LEN; SIGLEN must initially point to the allocated size of
* SIG_RESULT. */
gpg_error_t
@@ -1060,6 +1094,187 @@ scute_agent_sign (const char *hexgrip, unsigned char *data, int len,
}
+
+struct pkdecrypt_parm_s
+{
+ unsigned int len;
+ unsigned char data[512];
+ assuan_context_t ctx;
+ const unsigned char *ciphertext;
+ size_t ciphertextlen;
+};
+
+
+static gpg_error_t
+pkdecrypt_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ struct pkdecrypt_parm_s *parm = opaque;
+
+ if (parm->len + length > sizeof parm->data)
+ {
+ DEBUG (DBG_INFO, "maximum decryption result length exceeded");
+ return gpg_error (GPG_ERR_BAD_DATA);
+ }
+
+ memcpy (parm->data + parm->len, buffer, length);
+ parm->len += length;
+
+ return 0;
+}
+
+
+/* Handle the inquiries from pkdecrypt. Note, we only send the data,
+ * assuan_transact takes care of flushing and writing the "END". */
+static gpg_error_t
+pkdecrypt_inq_cb (void *opaque, const char *line)
+{
+ struct pkdecrypt_parm_s *parm = opaque;
+ gpg_error_t err;
+ const char *keyword = line;
+ int keywordlen;
+
+ for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 10 && !memcmp (keyword, "CIPHERTEXT", 10))
+ err = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
+ else
+ err = default_inq_cb (NULL, line);
+
+ return err;
+}
+
+
+
+/* Parse the result of a pkdecrypt operation which is an s-expression
+ * in canonical form that looks like
+ * (5:value<NDATA>:<DATA>).
+ *
+ * The raw result is stored in RESULT which has a size of *R_LEN, and
+ * *R_LEN is adjusted to the actual size. */
+static gpg_error_t
+pkdecrypt_parse_result (struct pkdecrypt_parm_s *ctx,
+ unsigned char *result, unsigned int *r_len)
+{
+ char *buf = ctx->data;
+ size_t len = ctx->len;
+ char *endp, *raw;
+ size_t n, rawlen;
+
+ if (len < 13 || memcmp (buf, "(5:value", 8) )
+ return gpg_error (GPG_ERR_INV_SEXP);
+ len -= 8;
+ buf += 8;
+
+ n = strtoul (buf, &endp, 10);
+ if (!n || *endp != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ endp++;
+ if ((endp-buf)+n > len)
+ return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */
+
+ /* Let (RAW,RAWLEN) describe the pkcs#1 block and remove that padding. */
+ raw = endp;
+ rawlen = n;
+
+ if (rawlen < 10) /* 0x00 + 0x02 + <1_random> + 0x00 + <16-session> */
+ return gpg_error (GPG_ERR_INV_SESSION_KEY);
+
+ if (raw[0] || raw[1] != 2 ) /* Wrong block type version. */
+ return gpg_error (GPG_ERR_INV_SESSION_KEY);
+
+ for (n=2; n < rawlen && raw[n]; n++) /* Skip the random bytes. */
+ ;
+ if (n+1 >= rawlen || raw[n] )
+ return gpg_error (GPG_ERR_INV_SESSION_KEY);
+ n++; /* Skip the zero byte */
+
+ if (*r_len < (rawlen - n))
+ return gpg_error (GPG_ERR_TOO_LARGE);
+ memcpy (result, raw + n, rawlen - n);
+ *r_len = rawlen - n;
+ return 0;
+}
+
+
+/* Call the agent to decrypt (ENCDATA,ENCDATALEN) using the key
+ * described by HEXGRIP. Stores the plaintext at R_PLAINDATA and its
+ * length at R_PLAINDATALEN; R_PLAINDATALEN must initially point to
+ * the allocated size of R_PLAINDATA and is updated to the actual used
+ * size on return. */
+gpg_error_t
+scute_agent_decrypt (const char *hexgrip,
+ unsigned char *encdata, int encdatalen,
+ unsigned char *r_plaindata, unsigned int *r_plaindatalen)
+{
+ char cmd[150];
+ gpg_error_t err;
+ struct pkdecrypt_parm_s pkdecrypt;
+ char *s_data;
+ size_t s_datalen;
+
+ if (!hexgrip || !encdata || !encdatalen || !r_plaindatalen)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ if (!r_plaindata)
+ {
+ /* Fixme: We do not return the minimal required length but our
+ * internal buffer size. */
+ pkdecrypt.len = *r_plaindatalen;
+ *r_plaindatalen = sizeof pkdecrypt.data - 1;
+ if (pkdecrypt.len > sizeof pkdecrypt.data - 1)
+ return gpg_error (GPG_ERR_INV_LENGTH);
+ return 0;
+ }
+
+ snprintf (cmd, sizeof (cmd), "SETKEY %s", hexgrip);
+ err = assuan_transact (agent_ctx, cmd,
+ NULL, NULL,
+ default_inq_cb, NULL,
+ NULL, NULL);
+ if (err)
+ return err;
+
+ /* Convert the input into an appropriate s-expression as expected by
+ * gpg-agent which is:
+ *
+ * (enc-val
+ * (flags pkcs1)
+ * (rsa
+ * (a VALUE)))
+ *
+ * Out of convenience we append a non-counted extra nul to the
+ * created canonical s-expression.
+ */
+ s_data = malloc (100 + encdatalen);
+ if (!s_data)
+ return gpg_error_from_syserror ();
+ snprintf (s_data, 50, "(7:enc-val(5:flags5:pkcs1)(3:rsa(1:a%d:",
+ encdatalen);
+ s_datalen = strlen (s_data);
+ memcpy (s_data + s_datalen, encdata, encdatalen);
+ s_datalen += encdatalen;
+ memcpy (s_data + s_datalen, ")))", 4);
+ s_datalen += 3;
+
+ pkdecrypt.len = 0;
+ pkdecrypt.ctx = agent_ctx;
+ pkdecrypt.ciphertext = s_data;
+ pkdecrypt.ciphertextlen = s_datalen;
+ err = assuan_transact (agent_ctx, "PKDECRYPT",
+ pkdecrypt_data_cb, &pkdecrypt,
+ pkdecrypt_inq_cb, &pkdecrypt,
+ NULL, NULL);
+ if (!err)
+ err = pkdecrypt_parse_result (&pkdecrypt, r_plaindata, r_plaindatalen);
+
+ free (s_data);
+ return err;
+}
+
+
/* Determine if FPR is trusted. */
gpg_error_t
scute_agent_is_trusted (const char *fpr, bool *is_trusted)
diff --git a/src/agent.h b/src/agent.h
index d14ebb7..0dfce0e 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -35,27 +35,6 @@
#include "cert.h"
-
-/* An object to store information pertaining to a keypair as stored on
- * a card. This is commonly used as a linked list of all keys known
- * for a card. */
-struct key_info_s
-{
- struct key_info_s *next;
-
- char grip[41];/* The keygrip as hex encoded string. */
-
- unsigned char xflag; /* Temporary flag to help processing a list. */
-
- /* The three next items are mostly useful for OpenPGP cards. */
- unsigned char fprlen; /* Use length of the next item. */
- unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN. */
- unsigned long created; /* The time the key was created. */
-
- char keyref[1]; /* String with the keyref (e.g. OPENPGP.1). */
-};
-typedef struct key_info_s *key_info_t;
-
/* The information structure for a smart card. */
struct agent_card_info_s
@@ -132,6 +111,12 @@ gpg_error_t scute_agent_sign (const char *hexgrip,
unsigned char *data, int len,
unsigned char *sig_result, unsigned int *sig_len);
+/* Decrypt data. */
+gpg_error_t scute_agent_decrypt (const char *hexgrip,
+ unsigned char *encdata, int encdatalen,
+ unsigned char *r_plaindata,
+ unsigned int *r_plaindatalen);
+
/* Determine if FPR is trusted. */
gpg_error_t scute_agent_is_trusted (const char *fpr, bool *is_trusted);
diff --git a/src/cert-object.c b/src/cert-object.c
index d3a594d..0184bad 100644
--- a/src/cert-object.c
+++ b/src/cert-object.c
@@ -590,7 +590,7 @@ scute_attr_cert (struct cert *cert, const char *grip,
gpg_error_t
-scute_attr_prv (struct cert *cert, const char *grip,
+scute_attr_prv (struct cert *cert, key_info_t kinfo,
CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp)
{
CK_RV err = 0;
@@ -617,8 +617,8 @@ scute_attr_prv (struct cert *cert, const char *grip,
CK_MECHANISM_TYPE obj_mechanisms[] = { CKM_RSA_PKCS };
CK_BBOOL obj_sensitive = CK_TRUE;
- CK_BBOOL obj_decrypt = CK_FALSE; /* Authentication only for now. */
- CK_BBOOL obj_sign = CK_TRUE;
+ CK_BBOOL obj_decrypt = CK_FALSE; /* Updated below. */
+ CK_BBOOL obj_sign = CK_FALSE; /* Updated below. */
CK_BBOOL obj_sign_recover = CK_FALSE;
CK_BBOOL obj_unwrap = CK_FALSE;
CK_BBOOL obj_extractable = CK_FALSE;
@@ -627,6 +627,13 @@ scute_attr_prv (struct cert *cert, const char *grip,
CK_BBOOL obj_wrap_with_trusted = CK_FALSE;
CK_BBOOL obj_always_authenticate = CK_FALSE;
+ if (kinfo->usage.sign || kinfo->usage.cert || kinfo->usage.auth)
+ obj_sign = CK_TRUE;
+
+ if (kinfo->usage.encr)
+ obj_decrypt = CK_TRUE;
+
+
err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
&subject_start, &subject_len);
if (err)
@@ -695,7 +702,7 @@ scute_attr_prv (struct cert *cert, const char *grip,
snprintf (cka_id_buffer, sizeof cka_id_buffer, "%s %s",
*cert->certref ? cert->certref:"-",
- grip && *grip? grip : "?" );
+ kinfo->grip && *kinfo->grip? kinfo->grip : "?" );
err = attr_one (attr, &attr_count, CKA_ID,
cka_id_buffer, strlen (cka_id_buffer));
}
diff --git a/src/cert.h b/src/cert.h
index d1a6cb1..0ba2dfb 100644
--- a/src/cert.h
+++ b/src/cert.h
@@ -38,7 +38,35 @@
#include "cryptoki.h"
+
+/* An object to store information pertaining to a keypair as stored on
+ * a card. This is commonly used as a linked list of all keys known
+ * for a card. */
+struct key_info_s
+{
+ struct key_info_s *next;
+
+ char grip[41];/* The keygrip as hex encoded string. */
+
+ unsigned char xflag; /* Temporary flag to help processing a list. */
+
+ /* The three next items are mostly useful for OpenPGP cards. */
+ unsigned char fprlen; /* Use length of the next item. */
+ unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN. */
+ unsigned long created; /* The time the key was created. */
+ struct {
+ unsigned int sign:1;
+ unsigned int cert:1;
+ unsigned int auth:1;
+ unsigned int encr:1;
+ } usage;
+
+ char keyref[1]; /* String with the keyref (e.g. OPENPGP.1). */
+};
+typedef struct key_info_s *key_info_t;
+
+
/* A certificate structure holds all information of a certificate
during a certificate search. */
struct cert
@@ -47,7 +75,9 @@ struct cert
bool valid;
/* The certifciate reference if retrieved from a card or an empty
- * string if not known. Example value: "OPENPGP.3". */
+ * string if not known. Example value: "OPENPGP.3". This is
+ * required because we do not always have access to a corresponding
+ * key_info_t object. */
char certref[25];
#if 1
@@ -133,7 +163,7 @@ gpg_error_t scute_gpgsm_search_certs (enum keylist_modes mode,
gpg_error_t scute_attr_cert (struct cert *cert, const char *grip,
CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp);
-gpg_error_t scute_attr_prv (struct cert *cert, const char *grip,
+gpg_error_t scute_attr_prv (struct cert *cert, key_info_t kinfo,
CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp);
void scute_attr_free (CK_ATTRIBUTE_PTR attr, CK_ULONG attr_count);
diff --git a/src/gpgsm.c b/src/gpgsm.c
index 5c3e220..dead6e1 100644
--- a/src/gpgsm.c
+++ b/src/gpgsm.c
@@ -47,7 +47,7 @@ struct search_cb_parm
cert_get_cb_t cert_get_cb;
void *hook;
bool with_chain;
- const char *grip;
+ key_info_t kinfo;
};
@@ -63,7 +63,7 @@ search_cb (void *hook, struct cert *cert)
/* Add the private key object only once. */
if (!ctx->found)
{
- err = scute_attr_prv (cert, ctx->grip, &attrp, &attr_countp);
+ err = scute_attr_prv (cert, ctx->kinfo, &attrp, &attr_countp);
if (err)
return err;
@@ -85,7 +85,7 @@ search_cb (void *hook, struct cert *cert)
scute_gpgsm_search_certs (KEYLIST_BY_FPR, cert->chain_id, search_cb, ctx);
/* Turn this certificate into a certificate object. */
- err = scute_attr_cert (cert, ctx->grip, &attrp, &attr_countp);
+ err = scute_attr_cert (cert, ctx->kinfo->grip, &attrp, &attr_countp);
if (err)
return err;
@@ -101,9 +101,10 @@ search_cb (void *hook, struct cert *cert)
/* Create the attributes required for a new certificate object. If
- * CERTREF is not NULL it is used to locate the cert directly from the
- * card; if CERTREF is NULL or a cert was not found on the card, GRIP
- * is used to find the certificate in the local key store of gpgsm.
+ * KINFO->KEYREF is not NULL it is used to locate the cert directly
+ * from the card; if KINFO->KEYREF is NULL or a cert was not found on
+ * the card, KINFO->GRIP is used to find the certificate in the local
+ * key store of gpgsm.
*
* FIXME: This is all pretty questionable because our input data
* always comes from the card.
@@ -112,8 +113,7 @@ search_cb (void *hook, struct cert *cert)
* and ATTR_COUNTP, and for the private key object in PRV_ATTRP and
* PRV_ATTR_COUNTP. */
gpg_error_t
-scute_gpgsm_get_cert (char *grip, const char *certref,
- cert_get_cb_t cert_get_cb, void *hook)
+scute_gpgsm_get_cert (key_info_t kinfo, cert_get_cb_t cert_get_cb, void *hook)
{
gpg_error_t err;
struct search_cb_parm search;
@@ -122,36 +122,25 @@ scute_gpgsm_get_cert (char *grip, const char *certref,
search.cert_get_cb = cert_get_cb;
search.hook = hook;
search.with_chain = false;
- search.grip = grip;
+ search.kinfo = kinfo;
- DEBUG (DBG_INFO, "scute_gpgsm_get_cert: certref='%s'", certref);
+ DEBUG (DBG_INFO, "scute_gpgsm_get_cert: keyref='%s'", kinfo->keyref);
/* If the cert is requested from the card, we try to get it from
* the card as well. */
- if (certref)
+ if (kinfo->keyref)
{
struct cert cert;
memset (&cert, '\0', sizeof (cert));
- err = scute_agent_get_cert (certref, &cert);
- if (! err)
- {
-#if 0
- /* For now, we don't need no stinking chain. */
-
- /* As we only have the DER certificate from the card, we need to
- parse that and fill out the missing info and try to get the
- certificate chain from gpgsm. */
- err = scute_cert_from_der (&cert);
-#endif
- if (! err)
- err = search_cb (&search, &cert);
- return err;
- }
+ err = scute_agent_get_cert (kinfo->keyref, &cert);
+ if (!err)
+ return search_cb (&search, &cert);
}
DEBUG (DBG_INFO, "scute_gpgsm_get_cert: falling back to gpgsm");
search.with_chain = true;
- err = scute_gpgsm_search_certs (KEYLIST_BY_GRIP, grip, search_cb, &search);
+ err = scute_gpgsm_search_certs (KEYLIST_BY_GRIP, kinfo->grip,
+ search_cb, &search);
return err;
}
diff --git a/src/gpgsm.h b/src/gpgsm.h
index a68a38e..515d2f8 100644
--- a/src/gpgsm.h
+++ b/src/gpgsm.h
@@ -43,11 +43,8 @@ typedef gpg_error_t (*cert_get_cb_t) (void *hook,
CK_ATTRIBUTE_PTR attrp,
CK_ULONG attr_countp);
-/* Create the attributes required for a new certificate object.
- Returns allocated attributes for the certificate object in ATTRP
- and ATTR_COUNTP, and for the private key object in PRV_ATTRP
- and PRV_ATTR_COUNTP. */
-gpg_error_t scute_gpgsm_get_cert (char *grip, const char *certref,
- cert_get_cb_t cert_get_cb, void *hook);
+/* Create the attributes required for a new certificate object. */
+gpg_error_t scute_gpgsm_get_cert (key_info_t kinfo,
+ cert_get_cb_t cert_get_cb, void *hook);
#endif /* GPGSM_H */
diff --git a/src/p11-decrypt.c b/src/p11-decrypt.c
index 8530720..485af6d 100644
--- a/src/p11-decrypt.c
+++ b/src/p11-decrypt.c
@@ -1,5 +1,5 @@
/* p11-decrypt.c - Cryptoki implementation.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
*
* This file is part of Scute.
*
@@ -23,18 +23,47 @@
#endif
#include "cryptoki.h"
+#include "locking.h"
+#include "slots.h"
-
+/* Decrypt the data (ENCDATA,ENCDATALEN) using the information
+ * recorded in HSESSION by C_DecryptInit. R_DATA is a buffer to
+ * receive the decrypted data. The length of that buffer must be
+ * stored in a variable to which R_DATALEN points to; on success that
+ * length is updated to the actual length of the decrypted data at
+ * R_DATA. In-place decryption is supported; that is ENCDATA and
+ * R_DATA may be the same buffer.
+ *
+ * If the function returns CKR_BUFFER_TOO_SMALL no further
+ * C_DecryptInit is required, instead the function can be called again
+ * with a larger buffer. On all other return codes a new
+ * C_DecryptInit is required. However, in contrast to the specs the
+ * return code CKR_ARGUMENTS_BAD may not require a new C_DecryptInit
+ * because this can be considered a bug in the caller's code. In case
+ * the input cannot be decrypted because it has an inappropriate
+ * length, then either CKR_ENCRYPTED_DATA_INVALID or
+ * CKR_ENCRYPTED_DATA_LEN_RANGE may be returned.
+ */
CK_RV CK_SPEC
-C_Decrypt (CK_SESSION_HANDLE hSession,
- CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
- CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+C_Decrypt (CK_SESSION_HANDLE hsession,
+ CK_BYTE *encdata, CK_ULONG encdatalen,
+ CK_BYTE *r_data, CK_ULONG *r_datalen)
{
- /* FIXME: Implement this. */
- (void) hSession;
- (void) pEncryptedData;
- (void) ulEncryptedDataLen;
- (void) pData;
- (void) pulDataLen;
- return CKR_FUNCTION_NOT_SUPPORTED;
+ CK_RV rv;
+ slot_iterator_t slot;
+ session_iterator_t sid;
+
+ if (!hsession || !encdata || !r_datalen)
+ return CKR_ARGUMENTS_BAD;
+
+ rv = scute_global_lock ();
+ if (rv)
+ return rv;
+
+ rv = slots_lookup_session (hsession, &slot, &sid);
+ if (!rv)
+ rv = session_decrypt (slot, sid, encdata, encdatalen, r_data, r_datalen);
+
+ scute_global_unlock ();
+ return rv;
}
diff --git a/src/p11-decryptinit.c b/src/p11-decryptinit.c
index dce1e00..71131be 100644
--- a/src/p11-decryptinit.c
+++ b/src/p11-decryptinit.c
@@ -1,5 +1,5 @@
/* p11-decryptinit.c - Cryptoki implementation.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
*
* This file is part of Scute.
*
@@ -23,15 +23,37 @@
#endif
#include "cryptoki.h"
+#include "locking.h"
+#include "slots.h"
+
+
+/* Prepare a decryption operation. HSESSION is the session's handle,
+ * MECHANISM points to an object describing the mechanism to be used,
+ * and HKEY is a handle to the decryption key. After calling this
+ * function either C_Decrypt or (C_DecryptUpdate, C_DecryptFinal) can
+ * be used to actually decrypt the data. The preparation is valid
+ * until a C_Decrypt or C_DecryptFinal.
+ */
-
CK_RV CK_SPEC
-C_DecryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
- CK_OBJECT_HANDLE hKey)
+C_DecryptInit (CK_SESSION_HANDLE hsession, CK_MECHANISM *mechanism,
+ CK_OBJECT_HANDLE hkey)
{
- /* FIXME: Implement this. */
- (void) hSession;
- (void) pMechanism;
- (void) hKey;
- return CKR_FUNCTION_NOT_SUPPORTED;
+ CK_RV rv;
+ slot_iterator_t slot;
+ session_iterator_t sid;
+
+ if (!hsession || !mechanism || hkey == CK_INVALID_HANDLE)
+ return CKR_ARGUMENTS_BAD;
+
+ rv = scute_global_lock ();
+ if (rv)
+ return rv;
+
+ rv = slots_lookup_session (hsession, &slot, &sid);
+ if (!rv)
+ rv = session_init_decrypt (slot, sid, mechanism, hkey);
+
+ scute_global_unlock ();
+ return rv;
}
diff --git a/src/p11-encryptinit.c b/src/p11-encryptinit.c
index d769a91..a34be0b 100644
--- a/src/p11-encryptinit.c
+++ b/src/p11-encryptinit.c
@@ -31,5 +31,9 @@ C_EncryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
(void) hSession;
(void) pMechanism;
(void) hKey;
+
+
+
+
return CKR_FUNCTION_NOT_SUPPORTED;
}
diff --git a/src/p11-sign.c b/src/p11-sign.c
index 4f2ae7d..ae1e5e5 100644
--- a/src/p11-sign.c
+++ b/src/p11-sign.c
@@ -29,7 +29,7 @@
/* Sign the data (PDATA,ULDATALEN) using the information recorded in
- * the HSESSION by C_SignInit. PSIGNAURE is a buffer to receive the
+ * the HSESSION by C_SignInit. PSIGNATURE is a buffer to receive the
* signature. The length of that buffer must be stored in a variable
* to which PULSIGNATURELEN points to; on success that length is
* updated to the actual length of the signature in PULSIGNATURE.
diff --git a/src/slots.c b/src/slots.c
index ca6a015..5ac8486 100644
--- a/src/slots.c
+++ b/src/slots.c
@@ -1,5 +1,5 @@
/* slots.c - Slot management.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
*
* This file is part of Scute.
*
@@ -97,8 +97,12 @@ struct session
/* The signing key. */
CK_OBJECT_HANDLE signing_key;
+
+ /* The decryption key. */
+ CK_OBJECT_HANDLE decryption_key;
};
+
/* The slot status. */
typedef enum
{
@@ -132,6 +136,7 @@ struct slot
/* The slot table. */
+/* FIXME: That symbol name is pretty short for a global. */
static scute_table_t slots;
@@ -379,7 +384,7 @@ slot_init (slot_iterator_t id)
for (ki = slot->info.kinfo; ki; ki = ki->next)
{
- err = scute_gpgsm_get_cert (ki->grip, ki->keyref, add_object, slot);
+ err = scute_gpgsm_get_cert (ki, add_object, slot);
if (err)
goto leave;
}
@@ -799,6 +804,7 @@ slot_create_session (slot_iterator_t id, session_iterator_t *session,
session_p->search_result = NULL;
session_p->search_result_len = 0;
session_p->signing_key = CK_INVALID_HANDLE;
+ session_p->decryption_key = CK_INVALID_HANDLE;
*session = SESSION_BUILD_ID (id, tsid);
@@ -1046,9 +1052,6 @@ session_sign (slot_iterator_t id, session_iterator_t sid,
int i;
const char *keyref;
- if (!pSignature)
- return CKR_ARGUMENTS_BAD;
-
if (!session->signing_key)
return CKR_OPERATION_NOT_INITIALIZED;
@@ -1099,9 +1102,139 @@ session_sign (slot_iterator_t id, session_iterator_t sid,
rc = CKR_ARGUMENTS_BAD;
else
rc = scute_gpg_err_to_ck (err);
+ /* Return the length. */
+ if (rc == CKR_OK || rc == CKR_BUFFER_TOO_SMALL)
+ *pulSignatureLen = sig_len;
leave:
if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
session->signing_key = 0;
return rc;
}
+
+
+/* Prepare a decryption for slot with SLOTID and the session SID using
+ * MECHANISM and KEY. This is the core of C_DecryptInit. */
+CK_RV
+session_init_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+ CK_MECHANISM *mechanism, object_iterator_t key)
+{
+ struct slot *slot;
+ struct session *session;
+ CK_RV rv;
+ CK_ATTRIBUTE_PTR attr;
+ CK_ULONG attr_count;
+ CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+
+ if (mechanism->mechanism != CKM_RSA_PKCS)
+ return CKR_MECHANISM_INVALID;
+
+ slot = scute_table_data (slots, slotid);
+ session = scute_table_data (slot->sessions, sid);
+
+ rv = slot_get_object (slotid, key, &attr, &attr_count);
+ if (rv)
+ return rv;
+
+ /* FIXME: What kind of strange loop is this? */
+ while (attr_count-- > 0)
+ if (attr->type == CKA_CLASS)
+ break;
+
+ if (attr_count == (CK_ULONG) -1)
+ return CKR_KEY_HANDLE_INVALID;
+
+ if (attr->ulValueLen != sizeof (key_class)
+ || memcmp (attr->pValue, &key_class, sizeof (key_class)))
+ return CKR_KEY_HANDLE_INVALID;
+
+ /* It's the private RSA key object. */
+ session->decryption_key = key;
+
+ return 0;
+}
+
+
+/* The core of C_Decrypt - see there for a description. */
+CK_RV
+session_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+ CK_BYTE *encdata, CK_ULONG encdatalen,
+ CK_BYTE *r_plaindata, CK_ULONG *r_plaindatalen)
+{
+ CK_RV rv;
+ gpg_error_t err;
+ struct slot *slot;
+ struct session *session;
+ CK_ATTRIBUTE_PTR attr;
+ CK_ULONG attr_count;
+ CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+ CK_BYTE key_id[100];
+ int i;
+ const char *keyref;
+ unsigned int plaindatalen;
+
+ slot = scute_table_data (slots, slotid);
+ session = scute_table_data (slot->sessions, sid);
+
+ if (!session->decryption_key)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ rv = slot_get_object (slotid, session->decryption_key, &attr, &attr_count);
+ if (rv)
+ goto leave;
+ if (attr_count == (CK_ULONG) -1)
+ {
+ rv = CKR_KEY_HANDLE_INVALID;
+ goto leave;
+ }
+ if (attr->ulValueLen != sizeof (key_class)
+ || memcmp (attr->pValue, &key_class, sizeof (key_class)))
+ {
+ rv = CKR_KEY_HANDLE_INVALID;
+ goto leave;
+ }
+
+ /* Find the CKA_ID */
+ for (i = 0; i < attr_count; i++)
+ if (attr[i].type == CKA_ID)
+ break;
+ if (i == attr_count)
+ {
+ rv = CKR_GENERAL_ERROR;
+ goto leave;
+ }
+
+ if (attr[i].ulValueLen >= sizeof key_id - 1)
+ {
+ rv = CKR_GENERAL_ERROR;
+ goto leave;
+ }
+ strncpy (key_id, attr[i].pValue, attr[i].ulValueLen);
+ key_id[attr[i].ulValueLen] = 0;
+ DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id);
+ for (keyref=key_id; *keyref && *keyref != ' '; keyref++)
+ ;
+ if (*keyref)
+ keyref++; /* Point to the grip. */
+
+ plaindatalen = *r_plaindatalen;
+ err = scute_agent_decrypt (keyref, encdata, encdatalen,
+ r_plaindata, &plaindatalen);
+ DEBUG (DBG_INFO, "agent returned gpg error %d datalen=%u", err, plaindatalen);
+ /* Take care of error codes which are not mapped by default. */
+ if (gpg_err_code (err) == GPG_ERR_INV_LENGTH)
+ rv = CKR_BUFFER_TOO_SMALL;
+ else if (gpg_err_code (err) == GPG_ERR_INV_ARG)
+ rv = CKR_ARGUMENTS_BAD;
+ else
+ rv = scute_gpg_err_to_ck (err);
+ /* Return the length. */
+ if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL)
+ *r_plaindatalen = plaindatalen;
+
+ leave:
+ if (rv != CKR_BUFFER_TOO_SMALL)
+ session->decryption_key = 0;
+ DEBUG (DBG_INFO, "leaving decrypt with rv=%lu", rv);
+ return rv;
+}
diff --git a/src/slots.h b/src/slots.h
index 6783219..b2fa224 100644
--- a/src/slots.h
+++ b/src/slots.h
@@ -199,12 +199,23 @@ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid,
object_iterator_t **search_result,
int *search_result_len);
-/* Set the signing key for session SID in slot ID to KEY. */
+/* The core of C_SignInit. */
CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid,
object_iterator_t key);
+/* The core of C_Sign. */
CK_RV session_sign (slot_iterator_t id, session_iterator_t sid,
CK_BYTE_PTR pData, CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen);
+/* The core of C_DecryptInit. */
+CK_RV session_init_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+ CK_MECHANISM *mechanism, object_iterator_t key);
+
+/* The core of C_Decrypt. */
+CK_RV session_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+ CK_BYTE *encdata, CK_ULONG encdatalen,
+ CK_BYTE *r_plaindata, CK_ULONG *r_plaindatalen);
+
+
#endif /* !SLOTS_H */
-----------------------------------------------------------------------
Summary of changes:
src/agent.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++--
src/agent.h | 27 ++----
src/cert-object.c | 15 +++-
src/cert.h | 34 +++++++-
src/gpgsm.c | 43 ++++------
src/gpgsm.h | 9 +-
src/p11-decrypt.c | 53 +++++++++---
src/p11-decryptinit.c | 40 +++++++--
src/p11-encryptinit.c | 4 +
src/p11-sign.c | 2 +-
src/slots.c | 143 +++++++++++++++++++++++++++++--
src/slots.h | 13 ++-
12 files changed, 519 insertions(+), 97 deletions(-)
hooks/post-receive
--
PKCS#11 token on top of gpg-agent
http://git.gnupg.org
More information about the Gnupg-commits
mailing list