summaryrefslogtreecommitdiff
path: root/src/cms.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cms.c')
-rw-r--r--src/cms.c987
1 files changed, 713 insertions, 274 deletions
diff --git a/src/cms.c b/src/cms.c
index 57927a3..881443f 100644
--- a/src/cms.c
+++ b/src/cms.c
@@ -1,5 +1,5 @@
/* cms.c - cryptographic message syntax main functions
- * Copyright (C) 2001, 2003, 2004, 2008, 2012 g10 Code GmbH
+ * Copyright (C) 2001, 2003, 2004, 2008, 2012, 2020 g10 Code GmbH
*
* This file is part of KSBA.
*
@@ -28,6 +28,13 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
+/* References:
+ * RFC-5652 := Cryptographic Message Syntax (CMS) (aka STD0070)
+ * SPHINX := CMS profile developed by the German BSI.
+ * (see also https://lwn.net/2001/1011/a/german-smime.php3)
+ * PKCS#7 := Original specification of CMS
+ */
+
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@@ -43,7 +50,9 @@
#include "der-encoder.h"
#include "ber-help.h"
#include "sexp-parse.h"
-#include "cert.h" /* need to access cert->root and cert->image */
+#include "cert.h"
+#include "der-builder.h"
+
static gpg_error_t ct_parse_data (ksba_cms_t cms);
static gpg_error_t ct_parse_signed_data (ksba_cms_t cms);
@@ -73,6 +82,8 @@ static struct {
{ "1.2.840.113549.1.7.6", KSBA_CT_ENCRYPTED_DATA,
ct_parse_encrypted_data, ct_build_encrypted_data },
{ "1.2.840.113549.1.9.16.1.2", KSBA_CT_AUTH_DATA },
+ { "1.3.6.1.4.1.311.2.1.4", KSBA_CT_SPC_IND_DATA_CTX,
+ ct_parse_data , ct_build_data },
{ NULL }
};
@@ -87,7 +98,78 @@ static const char oid_signingTime[9] = "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x05";
static const char oidstr_smimeCapabilities[] = "1.2.840.113549.1.9.15";
+
+#if 0 /* Set to 1 to use this debug helper. */
+static void
+log_sexp (const char *text, ksba_const_sexp_t p)
+{
+ int level = 0;
+
+ gpgrt_log_debug ("%s: ", text);
+ if (!p)
+ gpgrt_log_printf ("[none]");
+ else
+ {
+ for (;;)
+ {
+ if (*p == '(')
+ {
+ gpgrt_log_printf ("%c", *p);
+ p++;
+ level++;
+ }
+ else if (*p == ')')
+ {
+ gpgrt_log_printf ("%c", *p);
+ p++;
+ if (--level <= 0 )
+ return;
+ }
+ else if (!digitp (p))
+ {
+ gpgrt_log_printf ("[invalid s-exp]");
+ return;
+ }
+ else
+ {
+ char *endp;
+ const unsigned char *s;
+ unsigned long len, n;
+
+ len = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p != ':')
+ {
+ gpgrt_log_printf ("[invalid s-exp]");
+ return;
+ }
+ p++;
+ for (s=p,n=0; n < len; n++, s++)
+ if ( !((*s >= 'a' && *s <= 'z')
+ || (*s >= 'A' && *s <= 'Z')
+ || (*s >= '0' && *s <= '9')
+ || *s == '-' || *s == '.'))
+ break;
+ if (n < len)
+ {
+ gpgrt_log_printf ("#");
+ for (n=0; n < len; n++, p++)
+ gpgrt_log_printf ("%02X", *p);
+ gpgrt_log_printf ("#");
+ }
+ else
+ {
+ for (n=0; n < len; n++, p++)
+ gpgrt_log_printf ("%c", *p);
+ }
+ }
+ }
+ }
+ gpgrt_log_printf ("\n");
+}
+#endif /* debug helper */
+
/* Helper for read_and_hash_cont(). */
static gpg_error_t
@@ -435,6 +517,7 @@ ksba_cms_identify (ksba_reader_t reader)
if (!strcmp (content_handlers[i].oid, oid))
break;
}
+ ksba_free(oid);
if (!content_handlers[i].oid)
return KSBA_CT_NONE; /* unknown */
if (maybe_p12 && (content_handlers[i].ct == KSBA_CT_DATA
@@ -500,6 +583,9 @@ ksba_cms_release (ksba_cms_t cms)
ksba_cert_release (cms->cert_list->cert);
xfree (cms->cert_list->enc_val.algo);
xfree (cms->cert_list->enc_val.value);
+ xfree (cms->cert_list->enc_val.ecdh.e);
+ xfree (cms->cert_list->enc_val.ecdh.wrap_algo);
+ xfree (cms->cert_list->enc_val.ecdh.encr_algo);
xfree (cms->cert_list);
cms->cert_list = cl;
}
@@ -531,6 +617,7 @@ ksba_cms_release (ksba_cms_t cms)
struct sig_val_s *tmp = cms->sig_val->next;
xfree (cms->sig_val->algo);
xfree (cms->sig_val->value);
+ xfree (cms->sig_val->ecc.r);
xfree (cms->sig_val);
cms->sig_val = tmp;
}
@@ -765,8 +852,6 @@ ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx,
if (!si)
return -1;
- issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer";
- serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber";
root = si->root;
image = si->image;
}
@@ -774,8 +859,6 @@ ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx,
{
struct value_tree_s *tmp;
- issuer_path = "KeyTransRecipientInfo.rid.issuerAndSerialNumber.issuer";
- serial_path = "KeyTransRecipientInfo.rid.issuerAndSerialNumber.serialNumber";
for (tmp=cms->recp_info; tmp && idx; tmp=tmp->next, idx-- )
;
if (!tmp)
@@ -786,6 +869,38 @@ ksba_cms_get_issuer_serial (ksba_cms_t cms, int idx,
else
return gpg_error (GPG_ERR_NO_DATA);
+
+ if (cms->signer_info)
+ {
+ issuer_path = "SignerInfo.sid.issuerAndSerialNumber.issuer";
+ serial_path = "SignerInfo.sid.issuerAndSerialNumber.serialNumber";
+ }
+ else if (cms->recp_info)
+ {
+ /* Find the choice to use. */
+ n = _ksba_asn_find_node (root, "RecipientInfo.+");
+ if (!n || !n->name)
+ return gpg_error (GPG_ERR_NO_VALUE);
+
+ if (!strcmp (n->name, "ktri"))
+ {
+ issuer_path = "ktri.rid.issuerAndSerialNumber.issuer";
+ serial_path = "ktri.rid.issuerAndSerialNumber.serialNumber";
+ }
+ else if (!strcmp (n->name, "kari"))
+ {
+ issuer_path = ("kari..recipientEncryptedKeys"
+ "..rid.issuerAndSerialNumber.issuer");
+ serial_path = ("kari..recipientEncryptedKeys"
+ "..rid.issuerAndSerialNumber.serialNumber");
+ }
+ else if (!strcmp (n->name, "kekri"))
+ return gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ);
+ else
+ return gpg_error (GPG_ERR_INV_CMS_OBJ);
+ root = n;
+ }
+
if (r_issuer)
{
n = _ksba_asn_find_node (root, issuer_path);
@@ -1175,6 +1290,81 @@ ksba_cms_get_sig_val (ksba_cms_t cms, int idx)
}
+/* Helper to dump a S-expression. */
+#if 0
+static void
+dbg_print_sexp (ksba_const_sexp_t p)
+{
+ int level = 0;
+
+ if (!p)
+ fputs ("[none]", stdout);
+ else
+ {
+ for (;;)
+ {
+ if (*p == '(')
+ {
+ putchar (*p);
+ p++;
+ level++;
+ }
+ else if (*p == ')')
+ {
+ putchar (*p);
+ p++;
+ if (--level <= 0 )
+ {
+ putchar ('\n');
+ return;
+ }
+ }
+ else if (!digitp (p))
+ {
+ fputs ("[invalid s-exp]\n", stdout);
+ return;
+ }
+ else
+ {
+ const unsigned char *s;
+ char *endp;
+ unsigned long len, n;
+
+ len = strtoul (p, &endp, 10);
+ p = endp;
+ if (*p != ':')
+ {
+ fputs ("[invalid s-exp]\n", stdout);
+ return;
+ }
+ p++;
+ for (s=p,n=0; n < len; n++, s++)
+ if ( !((*s >= 'a' && *s <= 'z')
+ || (*s >= 'A' && *s <= 'Z')
+ || (*s >= '0' && *s <= '9')
+ || *s == '-' || *s == '.'))
+ break;
+ if (n < len)
+ {
+ putchar('#');
+ for (n=0; n < len; n++, p++)
+ printf ("%02X", *p);
+ putchar('#');
+ }
+ else
+ {
+ for (n=0; n < len; n++, p++)
+ putchar (*p);
+ }
+ }
+ }
+ }
+ putchar ('\n');
+}
+#endif /* 0 */
+
+
+
/**
* ksba_cms_get_enc_val:
* @cms: CMS object
@@ -1189,10 +1379,17 @@ ksba_cms_get_sig_val (ksba_cms_t cms, int idx)
ksba_sexp_t
ksba_cms_get_enc_val (ksba_cms_t cms, int idx)
{
- AsnNode n, n2;
+ AsnNode root, n, n2;
gpg_error_t err;
ksba_sexp_t string;
struct value_tree_s *vt;
+ char *keyencralgo = NULL; /* Key encryption algo. */
+ char *parm = NULL; /* Helper to get the parms of kencralgo. */
+ size_t parmlen;
+ char *keywrapalgo = NULL; /* Key wrap algo. */
+ struct tag_info ti;
+ const unsigned char *der;
+ size_t derlen;
if (!cms)
return NULL;
@@ -1206,25 +1403,97 @@ ksba_cms_get_enc_val (ksba_cms_t cms, int idx)
if (!vt)
return NULL; /* No value at this IDX */
+ /* Find the choice to use. */
+ root = _ksba_asn_find_node (vt->root, "RecipientInfo.+");
+ if (!root || !root->name)
+ return NULL;
- n = _ksba_asn_find_node (vt->root,
- "KeyTransRecipientInfo.keyEncryptionAlgorithm");
- if (!n)
- return NULL;
- if (n->off == -1)
+ if (!strcmp (root->name, "ktri"))
{
-/* fputs ("ksba_cms_get_enc_val problem at node:\n", stderr); */
-/* _ksba_asn_node_dump_all (n, stderr); */
- return NULL;
+ n = _ksba_asn_find_node (root, "ktri.keyEncryptionAlgorithm");
+ if (!n || n->off == -1)
+ return NULL;
+ n2 = n->right; /* point to the actual value */
+ err = _ksba_encval_to_sexp
+ (vt->image + n->off,
+ n->nhdr + n->len + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
+ &string);
}
+ else if (!strcmp (root->name, "kari"))
+ {
+ /* _ksba_asn_node_dump_all (root, stderr); */
- n2 = n->right; /* point to the actual value */
- err = _ksba_encval_to_sexp (vt->image + n->off,
- n->nhdr + n->len
- + ((!n2||n2->off == -1)? 0:(n2->nhdr+n2->len)),
- &string);
+ /* Get the encrypted key. Result is in (DER,DERLEN) */
+ n = _ksba_asn_find_node (root, ("kari..recipientEncryptedKeys"
+ "..encryptedKey"));
+ if (!n || n->off == -1)
+ {
+ err = gpg_error (GPG_ERR_INV_KEYINFO);
+ goto leave;
+ }
+
+ der = vt->image + n->off;
+ derlen = n->nhdr + n->len;
+ err = parse_octet_string (&der, &derlen, &ti);
+ if (err)
+ goto leave;
+ derlen = ti.length;
+ /* gpgrt_log_printhex (der, derlen, "%s: encryptedKey", __func__); */
+
+ /* Get the KEK algos. */
+ n = _ksba_asn_find_node (root, "kari..keyEncryptionAlgorithm");
+ if (!n || n->off == -1)
+ {
+ err = gpg_error (GPG_ERR_INV_KEYINFO);
+ goto leave;
+ }
+ err = _ksba_parse_algorithm_identifier2 (vt->image + n->off,
+ n->nhdr + n->len, NULL,
+ &keyencralgo, &parm, &parmlen);
+ if (err)
+ goto leave;
+ if (!parm)
+ {
+ err = gpg_error (GPG_ERR_INV_KEYINFO);
+ goto leave;
+ }
+ err = _ksba_parse_algorithm_identifier (parm, parmlen,NULL, &keywrapalgo);
+ if (err)
+ goto leave;
+
+ /* gpgrt_log_debug ("%s: keyencralgo='%s'\n", __func__, keyencralgo); */
+ /* gpgrt_log_debug ("%s: keywrapalgo='%s'\n", __func__, keywrapalgo); */
+
+ /* Get the ephemeral public key. */
+ n = _ksba_asn_find_node (root, "kari..originator..originatorKey");
+ if (!n || n->off == -1)
+ {
+ err = gpg_error (GPG_ERR_INV_KEYINFO);
+ goto leave;
+ }
+ err = _ksba_encval_kari_to_sexp (vt->image + n->off, n->nhdr + n->len,
+ keyencralgo, keywrapalgo, der, derlen,
+ &string);
+ if (err)
+ goto leave;
+
+ /* gpgrt_log_debug ("%s: encryptedKey:\n", __func__); */
+ /* dbg_print_sexp (string); */
+ }
+ else if (!strcmp (n->name, "kekri"))
+ return NULL; /*GPG_ERR_UNSUPPORTED_CMS_OBJ*/
+ else
+ return NULL; /*GPG_ERR_INV_CMS_OBJ*/
+
+ leave:
+ xfree (keyencralgo);
+ xfree (keywrapalgo);
+ xfree (parm);
if (err)
+ {
+ /* gpgrt_log_debug ("%s: error: %s\n", __func__, gpg_strerror (err)); */
return NULL;
+ }
return string;
}
@@ -1288,7 +1557,7 @@ ksba_cms_hash_signed_attrs (ksba_cms_t cms, int idx)
* ksba_cms_set_content_type:
* @cms: A CMS object
* @what: 0 for content type, 1 for inner content type
- * @type: Tyep constant
+ * @type: Type constant
*
* Set the content type used for build operations. This should be the
* first operation before starting to create a CMS message.
@@ -1568,25 +1837,29 @@ ksba_cms_set_signing_time (ksba_cms_t cms, int idx, const ksba_isotime_t sigtime
}
-/*
- r_sig = (sig-val
- (<algo>
- (<param_name1> <mpi>)
- ...
- (<param_namen> <mpi>)
- ))
- The sexp must be in canonical form.
- Note the <algo> must be given as a stringified OID or the special
- string "rsa".
-
- Note that IDX is only used for consistency checks.
+/* Set the signature value as a canonical encoded s-expression.
+ *
+ * r_sig = (sig-val
+ * (<algo>
+ * (<param_name1> <mpi>)
+ * ...
+ * (<param_namen> <mpi>)
+ * ))
+ *
+ * <algo> must be given as a stringified OID or the special string
+ * "rsa". For ECC <algo> must either be "ecdsa" or the OID matching the used
+ * hash algorithm; the expected parameters are "r" and "s".
+ *
+ * Note that IDX is only used for consistency checks.
*/
gpg_error_t
ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
{
- const unsigned char *s;
- unsigned long n;
+ gpg_error_t err;
+ unsigned long n, namelen;
struct sig_val_s *sv, **sv_tail;
+ const unsigned char *s, *endp, *name;
+ int ecc; /* True for ECC algos. */
int i;
if (!cms)
@@ -1594,6 +1867,7 @@ ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
if (idx < 0)
return gpg_error (GPG_ERR_INV_INDEX); /* only one signer for now */
+ /* log_sexp ("sigval:", sigval); */
s = sigval;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
@@ -1619,9 +1893,20 @@ ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
sv = xtrycalloc (1, sizeof *sv);
if (!sv)
return gpg_error (GPG_ERR_ENOMEM);
+
if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a')
- { /* kludge to allow "rsa" to be passed as algorithm name */
- sv->algo = xtrystrdup ("1.2.840.113549.1.1.1");
+ {
+ sv->algo = xtrystrdup ("1.2.840.113549.1.1.1"); /* rsa */
+ if (!sv->algo)
+ {
+ xfree (sv);
+ return gpg_error (GPG_ERR_ENOMEM);
+ }
+ }
+ else if (n==5 && !memcmp (s, "ecdsa", 5))
+ {
+ /* Use a placeholder for later fixup. */
+ sv->algo = xtrystrdup ("ecdsa");
if (!sv->algo)
{
xfree (sv);
@@ -1641,76 +1926,114 @@ ksba_cms_set_sig_val (ksba_cms_t cms, int idx, ksba_const_sexp_t sigval)
}
s += n;
- /* And now the values - FIXME: For now we only support one */
- /* fixme: start loop */
- if (*s != '(')
- {
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
- }
- s++;
+ ecc = (!strcmp (sv->algo, "ecdsa") /* placeholder */
+ || !strcmp (sv->algo, "1.2.840.10045.4.3.2") /* ecdsa-with-SHA256 */
+ || !strcmp (sv->algo, "1.2.840.10045.4.3.3") /* ecdsa-with-SHA384 */
+ || !strcmp (sv->algo, "1.2.840.10045.4.3.4") /* ecdsa-with-SHA512 */
+ );
- if (!(n = snext (&s)))
- {
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (GPG_ERR_INV_SEXP);
- }
- s += n; /* ignore the name of the parameter */
+ xfree (sv->value); sv->value = NULL;
+ xfree (sv->ecc.r); sv->ecc.r = NULL;
- if (!digitp(s))
+ while (*s == '(')
{
- xfree (sv->algo);
- xfree (sv);
- /* May also be an invalid S-EXP. */
- return gpg_error (GPG_ERR_UNKNOWN_SEXP);
- }
+ s++;
+ n = strtoul (s, (char**)&endp, 10);
+ s = endp;
+ if (!n || *s != ':')
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto leave;
+ }
+ s++;
+ name = s;
+ namelen = n;
+ s += n;
- if (!(n = snext (&s)))
- {
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (GPG_ERR_INV_SEXP);
- }
+ if (!digitp(s))
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
+ goto leave;
+ }
+ n = strtoul (s, (char**)&endp, 10);
+ s = endp;
+ if (!n || *s != ':')
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto leave;
+ }
+ s++;
+
+ if (namelen == 1 && *name == 's')
+ {
+ /* Store the "main" parameter into value. */
+ xfree (sv->value);
+ sv->value = xtrymalloc (n);
+ if (!sv->value)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (sv->value, s, n);
+ sv->valuelen = n;
+ }
+ else if (ecc && namelen == 1 && *name == 'r')
+ {
+ xfree (sv->ecc.r);
+ sv->ecc.r = xtrymalloc (n);
+ if (!sv->ecc.r)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ memcpy (sv->ecc.r, s, n);
+ sv->ecc.rlen = n;
+ }
+ /* (We ignore all other parameter of the (key value) form.) */
- if (n > 1 && !*s)
- { /* We might have a leading zero due to the way we encode
- MPIs - this zero should not go into the OCTECT STRING. */
+ s += n;
+ if ( *s != ')')
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
+ goto leave;
+ }
s++;
- n--;
}
- sv->value = xtrymalloc (n);
- if (!sv->value)
+
+ /* Expect two closing parenthesis. */
+ if (*s != ')')
{
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (GPG_ERR_ENOMEM);
+ err = gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
+ goto leave;
}
- memcpy (sv->value, s, n);
- sv->valuelen = n;
- s += n;
+ s++;
if ( *s != ')')
{
- xfree (sv->value);
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto leave;
}
- s++;
- /* fixme: end loop over parameters */
- /* we need 2 closing parenthesis */
- if ( *s != ')' || s[1] != ')')
+ /* Check that we have all required data. */
+ if (!sv->value)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto leave;
+ }
+ if (ecc && (!sv->ecc.r || !sv->ecc.rlen))
{
- xfree (sv->value);
- xfree (sv->algo);
- xfree (sv);
- return gpg_error (GPG_ERR_INV_SEXP);
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto leave;
}
*sv_tail = sv;
- return 0;
+ return 0; /* Success. */
+
+ leave: /* Note: This is an error-only label. */
+ xfree (sv->value);
+ xfree (sv->algo);
+ xfree (sv->ecc.r);
+ xfree (sv);
+ return err;
}
@@ -1751,17 +2074,24 @@ ksba_cms_set_content_enc_algo (ksba_cms_t cms,
* (<param_name1> <mpi>)
* ...
* (<param_namen> <mpi>)
+ * (encr-algo <oid>)
+ * (wrap-algo <oid>)
* ))
*
* Note the <algo> must be given as a stringified OID or the special
- * string "rsa" */
+ * string "rsa". For RSA there is just one parameter named "a";
+ * encr-algo and wrap-algo are also not used. For ECC <algo> must be
+ * "ecdh", the parameter "s" gives the encrypted key, "e" specified
+ * the ephemeral public key, and wrap-algo algo and encr-algo are the
+ * stringified OIDs for the ECDH algorithm parameters. */
gpg_error_t
ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval)
{
/*FIXME: This shares most code with ...set_sig_val */
struct certlist_s *cl;
- const char *s, *endp;
- unsigned long n;
+ const char *s, *endp, *name;
+ unsigned long n, namelen;
+ int ecdh = 0; /* We expect ECC parameters. */
if (!cms)
return gpg_error (GPG_ERR_INV_VALUE);
@@ -1770,8 +2100,9 @@ ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval)
for (cl=cms->cert_list; cl && idx; cl = cl->next, idx--)
;
if (!cl)
- return gpg_error (GPG_ERR_INV_INDEX); /* no certificate to store the value */
+ return gpg_error (GPG_ERR_INV_INDEX); /* No cert to store the value. */
+ /* log_sexp ("encval", encval); */
s = encval;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
@@ -1796,12 +2127,18 @@ ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval)
return gpg_error (GPG_ERR_INV_SEXP); /* we don't allow empty lengths */
s++;
xfree (cl->enc_val.algo);
- if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a')
+ if (n==3 && !memcmp (s, "rsa", 3))
{ /* kludge to allow "rsa" to be passed as algorithm name */
cl->enc_val.algo = xtrystrdup ("1.2.840.113549.1.1.1");
if (!cl->enc_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
}
+ else if (n==4 && !memcmp (s, "ecdh", 4))
+ {
+ cl->enc_val.algo = xtrystrdup ("1.2.840.10045.2.1"); /* ecPublicKey */
+ if (!cl->enc_val.algo)
+ return gpg_error (GPG_ERR_ENOMEM);
+ }
else
{
cl->enc_val.algo = xtrymalloc (n+1);
@@ -1812,47 +2149,96 @@ ksba_cms_set_enc_val (ksba_cms_t cms, int idx, ksba_const_sexp_t encval)
}
s += n;
- /* And now the values - FIXME: For now we only support one */
- /* fixme: start loop */
- if (*s != '(')
- return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
- s++;
- n = strtoul (s, (char**)&endp, 10);
- s = endp;
- if (!n || *s != ':')
- return gpg_error (GPG_ERR_INV_SEXP);
- s++;
- s += n; /* ignore the name of the parameter */
+ ecdh = !strcmp (cl->enc_val.algo, "1.2.840.10045.2.1");
- if (!digitp(s))
- return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
- n = strtoul (s, (char**)&endp, 10);
- s = endp;
- if (!n || *s != ':')
- return gpg_error (GPG_ERR_INV_SEXP);
- s++;
- if (n > 1 && !*s)
- { /* We might have a leading zero due to the way we encode
- MPIs - this zero should not go into the OCTECT STRING. */
+ xfree (cl->enc_val.value); cl->enc_val.value = NULL;
+ xfree (cl->enc_val.ecdh.e); cl->enc_val.ecdh.e = NULL;
+ xfree (cl->enc_val.ecdh.encr_algo); cl->enc_val.ecdh.encr_algo = NULL;
+ xfree (cl->enc_val.ecdh.wrap_algo); cl->enc_val.ecdh.wrap_algo = NULL;
+
+ while (*s == '(')
+ {
+ s++;
+ n = strtoul (s, (char**)&endp, 10);
+ s = endp;
+ if (!n || *s != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++;
+ name = s;
+ namelen = n;
+ s += n;
+
+ if (!digitp(s))
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
+ n = strtoul (s, (char**)&endp, 10);
+ s = endp;
+ if (!n || *s != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++;
+
+ if (namelen == 1 && ((!ecdh && *name == 'a') || (ecdh && *name == 's')))
+ {
+ /* Store the "main" parameter into value. */
+ xfree (cl->enc_val.value);
+ cl->enc_val.value = xtrymalloc (n);
+ if (!cl->enc_val.value)
+ return gpg_error (GPG_ERR_ENOMEM);
+ memcpy (cl->enc_val.value, s, n);
+ cl->enc_val.valuelen = n;
+ }
+ else if (!ecdh)
+ ; /* Ignore all other parameters for RSA. */
+ else if (namelen == 1 && *name == 'e')
+ {
+ xfree (cl->enc_val.ecdh.e);
+ cl->enc_val.ecdh.e = xtrymalloc (n);
+ if (!cl->enc_val.ecdh.e)
+ return gpg_error (GPG_ERR_ENOMEM);
+ memcpy (cl->enc_val.ecdh.e, s, n);
+ cl->enc_val.ecdh.elen = n;
+ }
+ else if (namelen == 9 && !memcmp (name, "encr-algo", 9))
+ {
+ xfree (cl->enc_val.ecdh.encr_algo);
+ cl->enc_val.ecdh.encr_algo = xtrymalloc (n+1);
+ if (!cl->enc_val.ecdh.encr_algo)
+ return gpg_error (GPG_ERR_ENOMEM);
+ memcpy (cl->enc_val.ecdh.encr_algo, s, n);
+ cl->enc_val.ecdh.encr_algo[n] = 0;
+ }
+ else if (namelen == 9 && !memcmp (name, "wrap-algo", 9))
+ {
+ xfree (cl->enc_val.ecdh.wrap_algo);
+ cl->enc_val.ecdh.wrap_algo = xtrymalloc (n+1);
+ if (!cl->enc_val.ecdh.wrap_algo)
+ return gpg_error (GPG_ERR_ENOMEM);
+ memcpy (cl->enc_val.ecdh.wrap_algo, s, n);
+ cl->enc_val.ecdh.wrap_algo[n] = 0;
+ }
+ /* (We ignore all other parameter of the (key value) form.) */
+
+ s += n;
+ if ( *s != ')')
+ return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* or invalid sexp */
s++;
- n--;
}
- xfree (cl->enc_val.value);
- cl->enc_val.value = xtrymalloc (n);
- if (!cl->enc_val.value)
- return gpg_error (GPG_ERR_ENOMEM);
- memcpy (cl->enc_val.value, s, n);
- cl->enc_val.valuelen = n;
- s += n;
- if ( *s != ')')
- return gpg_error (GPG_ERR_UNKNOWN_SEXP); /* but may also be an invalid one */
+ /* Expect two closing parenthesis. */
+ if (*s != ')')
+ return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
- /* fixme: end loop over parameters */
+ if ( *s != ')')
+ return gpg_error (GPG_ERR_INV_SEXP);
- /* we need 2 closing parenthesis */
- if ( *s != ')' || s[1] != ')')
+ /* Check that we have all required data. */
+ if (!cl->enc_val.value)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ if (ecdh && (!cl->enc_val.ecdh.e
+ || !cl->enc_val.ecdh.elen
+ || !cl->enc_val.ecdh.encr_algo
+ || !cl->enc_val.ecdh.wrap_algo))
return gpg_error (GPG_ERR_INV_SEXP);
+
return 0;
}
@@ -2509,7 +2895,7 @@ build_signed_data_attributes (ksba_cms_t cms)
attridx++;
/* Include the signing time */
- if (certlist->signing_time)
+ if (*certlist->signing_time)
{
attr = _ksba_asn_expand_tree (cms_tree->parse_tree,
"CryptographicMessageSyntax.Attribute");
@@ -2673,6 +3059,7 @@ build_signed_data_rest (ksba_cms_t cms)
struct sig_val_s *sv;
ksba_writer_t tmpwrt = NULL;
AsnNode root = NULL;
+ ksba_der_t dbld = NULL;
/* Now we can really write the signer info */
err = ksba_asn_create_tree ("cms", &cms_tree);
@@ -2708,6 +3095,7 @@ build_signed_data_rest (ksba_cms_t cms)
AsnNode n, n2;
unsigned char *image;
size_t imagelen;
+ const char *oid;
if (!digestlist || !si || !sv)
{
@@ -2776,7 +3164,7 @@ build_signed_data_rest (ksba_cms_t cms)
assert (si->root);
assert (si->image);
n2 = _ksba_asn_find_node (si->root, "SignerInfo.signedAttrs");
- if (!n2 || !n->down)
+ if (!n2 || !n2->down)
{
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
@@ -2799,7 +3187,26 @@ build_signed_data_rest (ksba_cms_t cms)
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
- err = _ksba_der_store_oid (n, sv->algo);
+
+ if (!strcmp (sv->algo, "ecdsa"))
+ {
+ /* Look at the digest algorithm and replace accordingly. */
+ if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.1"))
+ oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-SHA256 */
+ else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.2"))
+ oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-SHA384 */
+ else if (!strcmp (digestlist->oid, "2.16.840.1.101.3.4.2.3"))
+ oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-SHA512 */
+ else
+ {
+ err = gpg_error (GPG_ERR_DIGEST_ALGO);
+ goto leave;
+ }
+ }
+ else
+ oid = sv->algo;
+
+ err = _ksba_der_store_oid (n, oid);
if (err)
goto leave;
n = _ksba_asn_find_node (root,
@@ -2825,9 +3232,38 @@ build_signed_data_rest (ksba_cms_t cms)
err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
goto leave;
}
- err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen);
- if (err)
- goto leave;
+
+ if (sv->ecc.r) /* ECDSA */
+ {
+ unsigned char *tmpder;
+ size_t tmpderlen;
+
+ _ksba_der_release (dbld);
+ dbld = _ksba_der_builder_new (0);
+ if (!dbld)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ _ksba_der_add_int (dbld, sv->ecc.r, sv->ecc.rlen, 1);
+ _ksba_der_add_int (dbld, sv->value, sv->valuelen, 1);
+ _ksba_der_add_end (dbld);
+
+ err = _ksba_der_builder_get (dbld, &tmpder, &tmpderlen);
+ if (err)
+ goto leave;
+ err = _ksba_der_store_octet_string (n, tmpder, tmpderlen);
+ xfree (tmpder);
+ if (err)
+ goto leave;
+ }
+ else /* RSA */
+ {
+ err = _ksba_der_store_octet_string (n, sv->value, sv->valuelen);
+ if (err)
+ goto leave;
+ }
/* Make the DER encoding and write it out. */
err = _ksba_der_encode_tree (root, &image, &imagelen);
@@ -2871,7 +3307,7 @@ build_signed_data_rest (ksba_cms_t cms)
ksba_asn_tree_release (cms_tree);
_ksba_asn_release_nodes (root);
ksba_writer_release (tmpwrt);
-
+ _ksba_der_release (dbld);
return err;
}
@@ -2969,12 +3405,20 @@ build_enveloped_data_header (ksba_cms_t cms)
{
gpg_error_t err;
int recpno;
- ksba_asn_tree_t cms_tree = NULL;
struct certlist_s *certlist;
unsigned char *buf;
const char *s;
size_t len;
- ksba_writer_t tmpwrt = NULL;
+ ksba_der_t dbld = NULL;
+ int any_ecdh = 0;
+
+ /* See whether we have any ECDH recipients. */
+ for (certlist = cms->cert_list; certlist; certlist = certlist->next)
+ if (certlist->enc_val.ecdh.e)
+ {
+ any_ecdh = 1;
+ break;
+ }
/* Write the outer contentInfo */
/* fixme: code is shared with signed_data_header */
@@ -3012,7 +3456,9 @@ build_enveloped_data_header (ksba_cms_t cms)
For SPHINX the version number must be 0.
*/
- s = "\x00";
+
+
+ s = any_ecdh? "\x02" :"\x00";
err = _ksba_ber_write_tl (cms->writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1);
if (err)
return err;
@@ -3023,11 +3469,6 @@ build_enveloped_data_header (ksba_cms_t cms)
/* Note: originatorInfo is not yet implemented and must not be used
for SPHINX */
- /* Now we write the recipientInfo */
- err = ksba_asn_create_tree ("cms", &cms_tree);
- if (err)
- return err;
-
certlist = cms->cert_list;
if (!certlist)
{
@@ -3035,19 +3476,19 @@ build_enveloped_data_header (ksba_cms_t cms)
goto leave;
}
- /* To construct the set we use a temporary writer object */
- err = ksba_writer_new (&tmpwrt);
- if (err)
- goto leave;
- err = ksba_writer_set_mem (tmpwrt, 2048);
- if (err)
- goto leave;
+ dbld = _ksba_der_builder_new (0);
+ if (!dbld)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ _ksba_der_add_tag (dbld, 0, TYPE_SET);
for (recpno=0; certlist; recpno++, certlist = certlist->next)
{
- AsnNode root, n;
- unsigned char *image;
- size_t imagelen;
+ const unsigned char *der;
+ size_t derlen;
if (!certlist->cert)
{
@@ -3055,141 +3496,140 @@ build_enveloped_data_header (ksba_cms_t cms)
goto leave;
}
- root = _ksba_asn_expand_tree (cms_tree->parse_tree,
- "CryptographicMessageSyntax.RecipientInfo");
-
- /* We store a version of 0 because we are only allowed to use
- the issuerAndSerialNumber for SPHINX */
- n = _ksba_asn_find_node (root, "RecipientInfo.ktri.version");
- if (!n)
- {
- err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
- goto leave;
- }
- err = _ksba_der_store_integer (n, "\x00\x00\x00\x01\x00");
- if (err)
- goto leave;
-
- /* Store the rid */
- n = _ksba_asn_find_node (root, "RecipientInfo.ktri.rid");
- if (!n)
+ if (!certlist->enc_val.ecdh.e) /* RSA (ktri) */
{
- err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
- goto leave;
- }
-
- err = set_issuer_serial (n, certlist->cert, 1);
- if (err)
- goto leave;
-
- /* store the keyEncryptionAlgorithm */
- if (!certlist->enc_val.algo || !certlist->enc_val.value)
- return gpg_error (GPG_ERR_MISSING_VALUE);
- n = _ksba_asn_find_node (root,
- "RecipientInfo.ktri.keyEncryptionAlgorithm.algorithm");
- if (!n)
- {
- err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
- goto leave;
- }
- err = _ksba_der_store_oid (n, certlist->enc_val.algo);
- if (err)
- goto leave;
- n = _ksba_asn_find_node (root,
- "RecipientInfo.ktri.keyEncryptionAlgorithm.parameters");
- if (!n)
- {
- err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
- goto leave;
- }
-
- /* Now store NULL for the optional parameters. From Peter
- * Gutmann's X.509 style guide:
- *
- * Another pitfall to be aware of is that algorithms which
- * have no parameters have this specified as a NULL value
- * rather than omitting the parameters field entirely. The
- * reason for this is that when the 1988 syntax for
- * AlgorithmIdentifier was translated into the 1997 syntax,
- * the OPTIONAL associated with the AlgorithmIdentifier
- * parameters got lost. Later it was recovered via a defect
- * report, but by then everyone thought that algorithm
- * parameters were mandatory. Because of this the algorithm
- * parameters should be specified as NULL, regardless of what
- * you read elsewhere.
- *
- * The trouble is that things *never* get better, they just
- * stay the same, only more so
- * -- Terry Pratchett, "Eric"
- *
- * Although this is about signing, we always do it. Versions of
- * Libksba before 1.0.6 had a bug writing out the NULL tag here,
- * thus in reality we used to be correct according to the
- * standards despite we didn't intended so.
- */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ /* We store a version of 0 because we are only allowed to
+ * use the issuerAndSerialNumber for SPHINX */
+ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "", 1);
+ /* rid.issuerAndSerialNumber */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ /* rid.issuerAndSerialNumber.issuer */
+ err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen);
+ if (err)
+ goto leave;
+ _ksba_der_add_der (dbld, der, derlen);
+ /* rid.issuerAndSerialNumber.serialNumber */
+ err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen);
+ if (err)
+ goto leave;
+ _ksba_der_add_der (dbld, der, derlen);
+ _ksba_der_add_end (dbld);
- err = _ksba_der_store_null (n);
- if (err)
- goto leave;
+ /* Store the keyEncryptionAlgorithm */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ if (!certlist->enc_val.algo || !certlist->enc_val.value)
+ {
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+ _ksba_der_add_oid (dbld, certlist->enc_val.algo);
+ /* Now store NULL for the optional parameters. From Peter
+ * Gutmann's X.509 style guide:
+ *
+ * Another pitfall to be aware of is that algorithms which
+ * have no parameters have this specified as a NULL value
+ * rather than omitting the parameters field entirely. The
+ * reason for this is that when the 1988 syntax for
+ * AlgorithmIdentifier was translated into the 1997 syntax,
+ * the OPTIONAL associated with the AlgorithmIdentifier
+ * parameters got lost. Later it was recovered via a defect
+ * report, but by then everyone thought that algorithm
+ * parameters were mandatory. Because of this the algorithm
+ * parameters should be specified as NULL, regardless of what
+ * you read elsewhere.
+ *
+ * The trouble is that things *never* get better, they just
+ * stay the same, only more so
+ * -- Terry Pratchett, "Eric"
+ *
+ * Although this is about signing, we always do it. Versions of
+ * Libksba before 1.0.6 had a bug writing out the NULL tag here,
+ * thus in reality we used to be correct according to the
+ * standards despite we didn't intended so.
+ */
+ _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0);
+ _ksba_der_add_end (dbld);
+
+ /* Store the encryptedKey */
+ if (!certlist->enc_val.value)
+ {
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+ _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING,
+ certlist->enc_val.value,
+ certlist->enc_val.valuelen);
- /* store the encryptedKey */
- if (!certlist->enc_val.value)
- {
- err = gpg_error (GPG_ERR_MISSING_VALUE);
- goto leave;
}
- n = _ksba_asn_find_node (root, "RecipientInfo.ktri.encryptedKey");
- if (!n)
+ else /* ECDH */
{
- err = gpg_error (GPG_ERR_ELEMENT_NOT_FOUND);
- goto leave;
- }
- err = _ksba_der_store_octet_string (n,
- certlist->enc_val.value,
- certlist->enc_val.valuelen);
- if (err)
- goto leave;
-
+ _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* kari */
+ _ksba_der_add_ptr (dbld, 0, TYPE_INTEGER, "\x03", 1);
+
+ _ksba_der_add_tag (dbld, CLASS_CONTEXT, 0); /* originator */
+ _ksba_der_add_tag (dbld, CLASS_CONTEXT, 1); /* originatorKey */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* algorithm */
+ _ksba_der_add_oid (dbld, certlist->enc_val.algo);
+ _ksba_der_add_end (dbld);
+ _ksba_der_add_bts (dbld, certlist->enc_val.ecdh.e,
+ certlist->enc_val.ecdh.elen, 0);
+ _ksba_der_add_end (dbld); /* end originatorKey */
+ _ksba_der_add_end (dbld); /* end originator */
+
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* keyEncrAlgo */
+ _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.encr_algo);
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ _ksba_der_add_oid (dbld, certlist->enc_val.ecdh.wrap_algo);
+ _ksba_der_add_end (dbld);
+ _ksba_der_add_end (dbld); /* end keyEncrAlgo */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKeys */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE); /* recpEncrKey */
+
+ /* rid.issuerAndSerialNumber */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ err = _ksba_cert_get_issuer_dn_ptr (certlist->cert, &der, &derlen);
+ if (err)
+ goto leave;
+ _ksba_der_add_der (dbld, der, derlen);
+ err = _ksba_cert_get_serial_ptr (certlist->cert, &der, &derlen);
+ if (err)
+ goto leave;
+ _ksba_der_add_der (dbld, der, derlen);
+ _ksba_der_add_end (dbld);
- /* Make the DER encoding and write it out */
- err = _ksba_der_encode_tree (root, &image, &imagelen);
- if (err)
- goto leave;
+ /* encryptedKey */
+ if (!certlist->enc_val.value)
+ {
+ err = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+ _ksba_der_add_ptr (dbld, 0, TYPE_OCTET_STRING,
+ certlist->enc_val.value,
+ certlist->enc_val.valuelen);
- err = ksba_writer_write (tmpwrt, image, imagelen);
- if (err)
- goto leave;
+ _ksba_der_add_end (dbld); /* end recpEncrKey */
+ _ksba_der_add_end (dbld); /* end recpEncrKeys */
+ }
- xfree (image);
- _ksba_asn_release_nodes (root);
+ _ksba_der_add_end (dbld); /* End SEQUENCE (ktri or kari) */
}
-
- ksba_asn_tree_release (cms_tree);
- cms_tree = NULL;
+ _ksba_der_add_end (dbld); /* End SET */
/* Write out the SET filled with all recipient infos */
{
- unsigned char *value;
- size_t valuelen;
+ unsigned char *image;
+ size_t imagelen;
- value = ksba_writer_snatch_mem (tmpwrt, &valuelen);
- if (!value)
- {
- err = gpg_error (GPG_ERR_ENOMEM);
- goto leave;
- }
- ksba_writer_release (tmpwrt);
- tmpwrt = NULL;
- err = _ksba_ber_write_tl (cms->writer, TYPE_SET, CLASS_UNIVERSAL,
- 1, valuelen);
- if (!err)
- err = ksba_writer_write (cms->writer, value, valuelen);
- xfree (value);
+ err = _ksba_der_builder_get (dbld, &image, &imagelen);
+ if (err)
+ goto leave;
+ err = ksba_writer_write (cms->writer, image, imagelen);
+ xfree (image);
if (err)
goto leave;
}
-
/* Write the (inner) encryptedContentInfo */
err = _ksba_ber_write_tl (cms->writer, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, 0);
if (err)
@@ -3222,8 +3662,7 @@ build_enveloped_data_header (ksba_cms_t cms)
/* Now the encrypted data should be written */
leave:
- ksba_writer_release (tmpwrt);
- ksba_asn_tree_release (cms_tree);
+ _ksba_der_release (dbld);
return err;
}