diff --git a/src/certreq.c b/src/certreq.c
index e0869e9..9fba0c8 100644
--- a/src/certreq.c
+++ b/src/certreq.c
@@ -1,1129 +1,1192 @@
/* certreq.c - create pkcs-10 messages
* Copyright (C) 2002, 2011, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* KSBA is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "cms.h"
#include "convert.h"
#include "keyinfo.h"
#include "der-encoder.h"
#include "ber-help.h"
#include "sexp-parse.h"
#include "certreq.h"
static const char oidstr_subjectAltName[] = "2.5.29.17";
static const char oidstr_extensionReq[] = "1.2.840.113549.1.9.14";
+#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 */
/**
* ksba_cms_new:
*
* Create a new and empty CMS object
*
* Return value: A CMS object or an error code.
**/
gpg_error_t
ksba_certreq_new (ksba_certreq_t *r_cr)
{
*r_cr = xtrycalloc (1, sizeof **r_cr);
if (!*r_cr)
return gpg_error_from_errno (errno);
return 0;
}
/**
* ksba_certreq_release:
* @cms: A Certreq object
*
* Release a Certreq object.
**/
void
ksba_certreq_release (ksba_certreq_t cr)
{
if (!cr)
return;
xfree (cr->x509.serial.der);
xfree (cr->x509.issuer.der);
xfree (cr->x509.siginfo.der);
xfree (cr->subject.der);
xfree (cr->key.der);
xfree (cr->cri.der);
xfree (cr->sig_val.algo);
xfree (cr->sig_val.value);
while (cr->subject_alt_names)
{
struct general_names_s *tmp = cr->subject_alt_names->next;
xfree (cr->subject_alt_names);
cr->subject_alt_names = tmp;
}
while (cr->extn_list)
{
struct extn_list_s *e = cr->extn_list->next;
xfree (cr->extn_list);
cr->extn_list = e;
}
xfree (cr);
}
gpg_error_t
ksba_certreq_set_writer (ksba_certreq_t cr, ksba_writer_t w)
{
if (!cr || !w)
return gpg_error (GPG_ERR_INV_VALUE);
cr->writer = w;
return 0;
}
/* Provide a hash function so that we are able to hash the data */
void
ksba_certreq_set_hash_function (ksba_certreq_t cr,
void (*hash_fnc)(void *, const void *, size_t),
void *hash_fnc_arg)
{
if (cr)
{
cr->hash_fnc = hash_fnc;
cr->hash_fnc_arg = hash_fnc_arg;
}
}
/* Store the serial number. If this function is used, a real X.509
certificate will be built instead of a pkcs#10 certificate signing
request. SN must be a simple canonical encoded s-expression with
the serial number as its only item. Note that this function allows
to set a negative serial number, which is not forbidden but
probably not a good idea. */
gpg_error_t
ksba_certreq_set_serial (ksba_certreq_t cr, ksba_const_sexp_t sn)
{
const char *p = (const char *)sn;
unsigned long n;
char *endp;
if (!cr || !sn || !p || *p != '(')
return gpg_error (GPG_ERR_INV_VALUE);
p++;
n = strtoul (p, &endp, 10);
p = endp;
if (*p++ != ':' || !n)
return gpg_error (GPG_ERR_INV_VALUE);
/* Remove invalid leading zero bytes. */
for (; n > 1 && !*p && !(p[1] & 0x80); n--, p++)
;
if (cr->x509.serial.der)
return gpg_error (GPG_ERR_CONFLICT); /* Already set */
cr->x509.serial.der = xtrymalloc (n);
if (!cr->x509.serial.der)
return gpg_error_from_syserror ();
memcpy (cr->x509.serial.der, p, n);
cr->x509.serial.derlen = n;
return 0;
}
/* Store the issuer's name. NAME must be a valid RFC-2253 encoded DN
name. Only used for building an X.509 certificate. */
gpg_error_t
ksba_certreq_set_issuer (ksba_certreq_t cr, const char *name)
{
if (!cr || !name)
return gpg_error (GPG_ERR_INV_VALUE);
if (cr->x509.issuer.der)
return gpg_error (GPG_ERR_CONFLICT); /* Already set */
return _ksba_dn_from_str (name, &cr->x509.issuer.der,
&cr->x509.issuer.derlen);
}
/* Store validity information. The time is in TIMEBUF. A value of 0
for WHAT stores the notBefore time, a value of 1 stores the
notAfter time. Only used for building an X.509 certificate. */
gpg_error_t
ksba_certreq_set_validity (ksba_certreq_t cr, int what,
const ksba_isotime_t timebuf)
{
if (!cr || what < 0 || what > 1
|| !timebuf || _ksba_assert_time_format (timebuf))
return gpg_error (GPG_ERR_INV_VALUE);
_ksba_copy_time (what?cr->x509.not_after:cr->x509.not_before, timebuf);
return 0;
}
/* Store the signing key info. This is used to extract the signing
algorithm; the signing itself needs to be done by the caller as
response to a stop code. The expression SIGINFO is similar to a
sig-val one, however most parameters are not required. The
expected structure of this canonical encoded s-expression is:
(sig-val
(
( )
...
( )))
*/
gpg_error_t
ksba_certreq_set_siginfo (ksba_certreq_t cr, ksba_const_sexp_t siginfo)
{
if (!cr || !siginfo)
return gpg_error (GPG_ERR_INV_VALUE);
xfree (cr->x509.siginfo.der);
cr->x509.siginfo.der = NULL;
return _ksba_keyinfo_from_sexp (siginfo, 1, &cr->x509.siginfo.der,
&cr->x509.siginfo.derlen);
}
/* Store the subject's name. Does perform some syntactic checks on
the name. The first added subject is the real one, all subsequent
calls add subjectAltNames.
NAME must be a valid RFC-2253 encoded DN name for the first one or an
email address enclosed in angle brackets for all further calls.
*/
gpg_error_t
ksba_certreq_add_subject (ksba_certreq_t cr, const char *name)
{
unsigned long namelen;
size_t n, n1;
struct general_names_s *gn;
unsigned char *der;
int tag;
const char *endp;
if (!cr || !name)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cr->subject.der)
return _ksba_dn_from_str (name, &cr->subject.der, &cr->subject.derlen);
/* This is assumed to be an subjectAltName. */
/* Note that the way we pass the name should match what
ksba_cert_get_subject() returns. In particular we expect that it
is a real string and thus a canonical S-expression is
additionally terminated by a 0. */
namelen = strlen (name);
if (*name == '<' && name[namelen-1] == '>'
&& namelen >= 4 && strchr (name, '@'))
{
name++;
namelen -= 2;
tag = 1; /* rfc822Name */
}
else if (!strncmp (name, "(8:dns-name", 11))
{
tag = 2; /* dNSName */
namelen = strtoul (name+11, (char**)&endp, 10);
name = endp;
if (!namelen || *name != ':')
return gpg_error (GPG_ERR_INV_SEXP);
name++;
}
else if (!strncmp (name, "(3:uri", 6))
{
tag = 6; /* uRI */
namelen = strtoul (name+6, (char**)&endp, 10);
name = endp;
if (!namelen || *name != ':')
return gpg_error (GPG_ERR_INV_SEXP);
name++;
}
else
return gpg_error (GPG_ERR_INV_VALUE);
n1 = _ksba_ber_count_tl (tag, CLASS_CONTEXT, 0, namelen);
n1 += namelen;
gn = xtrymalloc (sizeof *gn + n1 - 1);
if (!gn)
return gpg_error_from_errno (errno);
gn->tag = tag;
gn->datalen = n1;
der = (unsigned char *)gn->data;
n = _ksba_ber_encode_tl (der, tag, CLASS_CONTEXT, 0, namelen);
if (!n)
return gpg_error (GPG_ERR_BUG);
der += n;
memcpy (der, name, namelen);
assert (der + namelen - (unsigned char*)gn->data == n1);
gn->next = cr->subject_alt_names;
cr->subject_alt_names = gn;
return 0;
}
/* Add the GeneralNames object GNAMES to the list of extensions in CR.
Use OID as object identifier for the extensions. */
static gpg_error_t
add_general_names_to_extn (ksba_certreq_t cr, struct general_names_s *gnames,
const char *oid)
{
struct general_names_s *g;
size_t n, n1, n2;
struct extn_list_s *e;
unsigned char *der;
/* Calculate the required size. */
n1 = 0;
for (g=gnames; g; g = g->next)
n1 += g->datalen;
n2 = _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1);
n2 += n1;
/* Allocate memory and encode all. */
e = xtrymalloc (sizeof *e + n2 - 1);
if (!e)
return gpg_error_from_errno (errno);
e->oid = oid;
e->critical = 0;
e->derlen = n2;
der = e->der;
n = _ksba_ber_encode_tl (der, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1);
if (!n)
return gpg_error (GPG_ERR_BUG); /* (no need to cleanup after a bug) */
der += n;
for (g=gnames; g; g = g->next)
{
memcpy (der, g->data, g->datalen);
der += g->datalen;
}
assert (der - e->der == n2);
e->next = cr->extn_list;
cr->extn_list = e;
return 0;
}
/* Store the subject's publickey. */
gpg_error_t
ksba_certreq_set_public_key (ksba_certreq_t cr, ksba_const_sexp_t key)
{
if (!cr)
return gpg_error (GPG_ERR_INV_VALUE);
+
xfree (cr->key.der);
cr->key.der = NULL;
return _ksba_keyinfo_from_sexp (key, 0, &cr->key.der, &cr->key.derlen);
}
/* Generic function to add an extension to a certificate request. The
extension must be provided readily encoded in the buffer DER of
length DERLEN bytes; the OID is to be given in OID and IS_CRIT
should be set to true if that extension shall be marked
critical. */
gpg_error_t
ksba_certreq_add_extension (ksba_certreq_t cr,
const char *oid, int is_crit,
const void *der, size_t derlen)
{
size_t oidlen;
struct extn_list_s *e;
if (!cr || !oid|| !*oid || !der || !derlen)
return gpg_error (GPG_ERR_INV_VALUE);
oidlen = strlen (oid);
e = xtrymalloc (sizeof *e + derlen + oidlen);
if (!e)
return gpg_error_from_errno (errno);
e->critical = is_crit;
e->derlen = derlen;
memcpy (e->der, der, derlen);
strcpy (e->der+derlen, oid);
e->oid = e->der + derlen;
e->next = cr->extn_list;
cr->extn_list = e;
return 0;
}
/*
* r_sig = (sig-val
* (
* ( )
* ...
* ( )
* ))
* The sexp must be in canonical form.
* Fixme: The code is mostly duplicated from cms.c
* Note, that must be given as a stringified OID or the special
* string "rsa" which is translated to sha1WithRSAEncryption
*/
gpg_error_t
ksba_certreq_set_sig_val (ksba_certreq_t cr, ksba_const_sexp_t sigval)
{
const unsigned char *s, *saved;
char *buf = NULL;
unsigned long n, len;
int pass, nparam;
- int is_EdDSA = 0;
if (!cr)
return gpg_error (GPG_ERR_INV_VALUE);
s = sigval;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
if (!smatch (&s, 7, "sig-val"))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (*s != '(')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
/* break out the algorithm ID */
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
xfree (cr->sig_val.algo);
if (n==3 && s[0] == 'r' && s[1] == 's' && s[2] == 'a')
{ /* kludge to allow "rsa" to be passed as algorithm name */
cr->sig_val.algo = xtrystrdup ("1.2.840.113549.1.1.5");
if (!cr->sig_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
}
else
{
cr->sig_val.algo = xtrymalloc (n+1);
if (!cr->sig_val.algo)
return gpg_error (GPG_ERR_ENOMEM);
memcpy (cr->sig_val.algo, s, n);
cr->sig_val.algo[n] = 0;
if (!memcmp (s, "eddsa", 5))
- is_EdDSA = 1;
+ cr->sig_val.is_ecc = 2;
}
s += n;
+ if (cr->sig_val.is_ecc == 2
+ || !strcmp (cr->sig_val.algo, "1.3.101.112") /* Ed25519 */
+ || !strcmp (cr->sig_val.algo, "1.3.101.113")) /* Ed448 */
+ cr->sig_val.is_ecc = 2;
+ else if (!strcmp (cr->sig_val.algo, "1.2.840.10045.4.1") /* with-sha1 */
+ || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.1") /* with-sha224 */
+ || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.2") /* with-sha256 */
+ || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.3") /* with-sha384 */
+ || !strcmp (cr->sig_val.algo, "1.2.840.10045.4.3.4"))/* with-sha512 */
+ cr->sig_val.is_ecc = 1;
+ else
+ cr->sig_val.is_ecc = 0;
+
/* And now the values.
*
* If there is only one value, the signature is simply
* that value. Otherwise, the signature is a DER-encoded
* SEQUENCE of INTEGERs representing the different values.
*
* We need three passes over the values:
* - first pass is to get the number of values (nparam);
* - second pass is to compute the total length (len);
* - third pass is to build the final signature. */
for (pass = 1, nparam = len = 0, saved = s; pass < 4; pass++)
{
s = saved;
if (pass == 3)
{
size_t needed = len;
- if (!is_EdDSA && nparam > 1)
- needed += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, len);
+ if (cr->sig_val.is_ecc != 2 && nparam > 1)
+ needed += _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL,
+ 1, len);
xfree (cr->sig_val.value);
cr->sig_val.value = xtrymalloc (needed);
if (!cr->sig_val.value)
return gpg_error (GPG_ERR_ENOMEM);
cr->sig_val.valuelen = needed;
buf = cr->sig_val.value;
- if (!is_EdDSA && nparam > 1)
+ if (cr->sig_val.is_ecc != 2 && nparam > 1)
buf += _ksba_ber_encode_tl (buf, TYPE_SEQUENCE,
CLASS_UNIVERSAL, 1, len);
}
while (*s != ')')
{
if (*s != '(')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
s += n; /* Ignore the name of the parameter. */
if (!digitp (s))
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (!(n = snext (&s)))
return gpg_error (GPG_ERR_INV_SEXP);
if (pass == 1)
nparam++;
else if (pass == 2)
{
- if (is_EdDSA || nparam == 1)
+ if (cr->sig_val.is_ecc == 2 || nparam == 1)
len += n;
else
len += _ksba_ber_count_tl (TYPE_INTEGER, CLASS_UNIVERSAL, 0,
*s >= 0x80? n + 1 : n)
+ (*s >= 0x80? n + 1 : n);
}
else if (pass == 3)
{
- if (is_EdDSA || nparam == 1)
+ if (cr->sig_val.is_ecc == 2 || nparam == 1)
{
memcpy (buf, s, n);
buf += n;
}
else
{
if (*s >= 0x80)
{ /* Add leading zero byte. */
buf += _ksba_ber_encode_tl (buf, TYPE_INTEGER,
CLASS_UNIVERSAL, 0, n + 1);
*buf++ = 0;
}
else
buf += _ksba_ber_encode_tl (buf, TYPE_INTEGER,
CLASS_UNIVERSAL, 0, n);
memcpy (buf, s, n);
buf += n;
}
}
s += n;
if (*s != ')')
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
s++;
}
}
/* we need 2 closing parenthesis */
if ( *s != ')' || s[1] != ')')
return gpg_error (GPG_ERR_INV_SEXP);
return 0;
}
/* Build the extension block and return it in R_DER and R_DERLEN. IF
CERTMODE is true build X.509 certificate extension instead. */
static gpg_error_t
build_extensions (ksba_certreq_t cr, int certmode,
void **r_der, size_t *r_derlen)
{
gpg_error_t err;
ksba_writer_t writer, w=NULL;
struct extn_list_s *e;
unsigned char *value = NULL;
size_t valuelen;
unsigned char *p;
size_t n;
*r_der = NULL;
*r_derlen = 0;
err = ksba_writer_new (&writer);
if (err)
goto leave;
err = ksba_writer_set_mem (writer, 2048);
if (err)
goto leave;
err = ksba_writer_new (&w);
if (err)
goto leave;
for (e=cr->extn_list; e; e = e->next)
{
err = ksba_writer_set_mem (w, e->derlen + 100);
if (err)
goto leave;
err = ksba_oid_from_str (e->oid, &p, &n);
if(err)
goto leave;
err = _ksba_ber_write_tl (w, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n);
if (!err)
err = ksba_writer_write (w, p, n);
xfree (p);
if (e->critical)
{
err = _ksba_ber_write_tl (w, TYPE_BOOLEAN, CLASS_UNIVERSAL, 0, 1);
if (!err)
err = ksba_writer_write (w, "\xff", 1);
if(err)
goto leave;
}
err = _ksba_ber_write_tl (w, TYPE_OCTET_STRING, CLASS_UNIVERSAL,
0, e->derlen);
if (!err)
err = ksba_writer_write (w, e->der, e->derlen);
if(err)
goto leave;
p = ksba_writer_snatch_mem (w, &n);
if (!p)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL,
1, n);
if (!err)
err = ksba_writer_write (writer, p, n);
xfree (p); p = NULL;
if (err)
goto leave;
}
/* Embed all the sequences into another sequence */
value = ksba_writer_snatch_mem (writer, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
err = ksba_writer_set_mem (writer, valuelen+10);
if (err)
goto leave;
err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL,
1, valuelen);
if (!err)
err = ksba_writer_write (writer, value, valuelen);
if (err)
goto leave;
xfree (value);
value = ksba_writer_snatch_mem (writer, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
if (!certmode)
{
/* Now create the extension request sequence content */
err = ksba_writer_set_mem (writer, valuelen+100);
if (err)
goto leave;
err = ksba_oid_from_str (oidstr_extensionReq, &p, &n);
if(err)
goto leave;
err = _ksba_ber_write_tl (writer, TYPE_OBJECT_ID, CLASS_UNIVERSAL, 0, n);
if (!err)
err = ksba_writer_write (writer, p, n);
xfree (p); p = NULL;
if (err)
return err;
err = _ksba_ber_write_tl (writer, TYPE_SET, CLASS_UNIVERSAL, 1, valuelen);
if (!err)
err = ksba_writer_write (writer, value, valuelen);
/* Put this all into a SEQUENCE */
xfree (value);
value = ksba_writer_snatch_mem (writer, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
err = ksba_writer_set_mem (writer, valuelen+10);
if (err)
goto leave;
err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL,
1, valuelen);
if (!err)
err = ksba_writer_write (writer, value, valuelen);
if (err)
goto leave;
xfree (value);
value = ksba_writer_snatch_mem (writer, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
}
*r_der = value;
*r_derlen = valuelen;
value = NULL;
leave:
ksba_writer_release (writer);
ksba_writer_release (w);
xfree (value);
return err;
}
/* Build a value tree from the already stored values. */
static gpg_error_t
build_cri (ksba_certreq_t cr)
{
gpg_error_t err;
ksba_writer_t writer;
void *value = NULL;
size_t valuelen;
int certmode;
/* If a serial number has been set, we don't create a CSR but a
proper certificate. */
certmode = !!cr->x509.serial.der;
err = ksba_writer_new (&writer);
if (err)
goto leave;
err = ksba_writer_set_mem (writer, 2048);
if (err)
goto leave;
if (!cr->key.der)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
/* We write all stuff out to a temporary writer object, then use
this object to create the cri and store the cri image */
if (certmode)
{
/* Store the version structure; version is 3 (encoded as 2):
[0] { INTEGER 2 } */
err = ksba_writer_write (writer, "\xa0\x03\x02\x01\x02", 5);
}
else
{
/* Store version v1 (which is a 0). */
err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0, 1);
if (!err)
err = ksba_writer_write (writer, "", 1);
}
if (err)
goto leave;
/* For a certificate we need to store the s/n, the signature
algorithm identifier, the issuer DN and the validity. */
if (certmode)
{
/* Store the serial number. */
err = _ksba_ber_write_tl (writer, TYPE_INTEGER, CLASS_UNIVERSAL, 0,
cr->x509.serial.derlen);
if (!err)
err = ksba_writer_write (writer,
cr->x509.serial.der, cr->x509.serial.derlen);
if (err)
goto leave;
/* Store the signature algorithm identifier. */
if (!cr->x509.siginfo.der)
err = gpg_error (GPG_ERR_MISSING_VALUE);
else
err = ksba_writer_write (writer,
cr->x509.siginfo.der, cr->x509.siginfo.derlen);
if (err)
goto leave;
/* Store the issuer DN. If no issuer DN has been set we use the
subject DN. */
if (cr->x509.issuer.der)
err = ksba_writer_write (writer,
cr->x509.issuer.der, cr->x509.issuer.derlen);
else if (cr->subject.der)
err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen);
else
err = gpg_error (GPG_ERR_MISSING_VALUE);
if (err)
goto leave;
/* Store the Validity. */
{
unsigned char templ[36];
unsigned char *tp;
tp = templ;
*tp++ = 0x30;
*tp++ = 0x22;
*tp++ = TYPE_GENERALIZED_TIME;
*tp++ = 15;
if (cr->x509.not_before[0])
{
if (_ksba_cmp_time (cr->x509.not_before, "20500101T000000") >= 0)
{
memcpy (tp, cr->x509.not_before, 8);
tp += 8;
memcpy (tp, cr->x509.not_before+9, 6);
tp += 6;
}
else
{
tp[-2] = TYPE_UTC_TIME;
tp[-1] = 13;
memcpy (tp, cr->x509.not_before+2, 6);
tp += 6;
memcpy (tp, cr->x509.not_before+9, 6);
tp += 6;
}
}
else
{
tp[-2] = TYPE_UTC_TIME;
tp[-1] = 13;
memcpy (tp, "110101000000", 12);
tp += 12;
}
*tp++ = 'Z';
*tp++ = TYPE_GENERALIZED_TIME;
*tp++ = 15;
if (cr->x509.not_after[0])
{
if (_ksba_cmp_time (cr->x509.not_after, "20500101T000000") >= 0)
{
memcpy (tp, cr->x509.not_after, 8);
tp += 8;
memcpy (tp, cr->x509.not_after+9, 6);
tp += 6;
}
else
{
tp[-2] = TYPE_UTC_TIME;
tp[-1] = 13;
memcpy (tp, cr->x509.not_after+2, 6);
tp += 6;
memcpy (tp, cr->x509.not_after+9, 6);
tp += 6;
}
}
else
{
memcpy (tp,"20630405170000", 14);
tp += 14;
}
*tp++ = 'Z';
assert (tp - templ <= 36);
templ[1] = tp - templ - 2; /* Fixup the sequence length. */
err = ksba_writer_write (writer, templ, tp - templ);
if (err)
goto leave;
}
}
/* store the subject */
if (!cr->subject.der)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
err = ksba_writer_write (writer, cr->subject.der, cr->subject.derlen);
if (err)
goto leave;
/* store the public key info */
err = ksba_writer_write (writer, cr->key.der, cr->key.derlen);
if (err)
goto leave;
/* Copy generalNames objects to the extension list. */
if (cr->subject_alt_names)
{
err = add_general_names_to_extn (cr, cr->subject_alt_names,
oidstr_subjectAltName);
if (err)
goto leave;
while (cr->subject_alt_names)
{
struct general_names_s *tmp = cr->subject_alt_names->next;
xfree (cr->subject_alt_names);
cr->subject_alt_names = tmp;
}
cr->subject_alt_names = NULL;
}
/* Write the extensions. Note that the implicit SET OF is REQUIRED */
xfree (value); value = NULL;
valuelen = 0;
if (cr->extn_list)
{
err = build_extensions (cr, certmode, &value, &valuelen);
if (err)
goto leave;
err = _ksba_ber_write_tl (writer, certmode? 3:0,
CLASS_CONTEXT, 1, valuelen);
if (!err)
err = ksba_writer_write (writer, value, valuelen);
if (err)
goto leave;
}
else
{ /* We can't write an object of length zero using our ber_write
function. So we must open encode it. */
err = ksba_writer_write (writer,
certmode? "\xa3\x02\x30":"\xa0\x02\x30", 4);
if (err)
goto leave;
}
/* pack it into the sequence */
xfree (value);
value = ksba_writer_snatch_mem (writer, &valuelen);
if (!value)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto leave;
}
/* reinitialize the buffer to create the outer sequence */
err = ksba_writer_set_mem (writer, valuelen+10);
if (err)
goto leave;
/* write outer sequence */
err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL,
1, valuelen);
if (!err)
err = ksba_writer_write (writer, value, valuelen);
if (err)
goto leave;
/* and store the final result */
cr->cri.der = ksba_writer_snatch_mem (writer, &cr->cri.derlen);
if (!cr->cri.der)
err = gpg_error (GPG_ERR_ENOMEM);
leave:
ksba_writer_release (writer);
xfree (value);
return err;
}
static gpg_error_t
hash_cri (ksba_certreq_t cr)
{
if (!cr->hash_fnc)
return gpg_error (GPG_ERR_MISSING_ACTION);
if (!cr->cri.der)
return gpg_error (GPG_ERR_INV_STATE);
cr->hash_fnc (cr->hash_fnc_arg, cr->cri.der, cr->cri.derlen);
return 0;
}
/* The user has calculated the signatures and we can now write
the signature */
static gpg_error_t
sign_and_write (ksba_certreq_t cr)
{
gpg_error_t err;
- ksba_writer_t writer;
- void *value = NULL;
+ ksba_der_t dbld;
+ unsigned char *value = NULL;
size_t valuelen;
- err = ksba_writer_new (&writer);
- if (err)
- goto leave;
- err = ksba_writer_set_mem (writer, 2048);
- if (err)
- goto leave;
+ dbld = _ksba_der_builder_new (0);
+ if (!dbld)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Start outer sequence. */
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
- /* store the cri */
+ /* Store the cri */
if (!cr->cri.der)
{
err = gpg_error (GPG_ERR_MISSING_VALUE);
goto leave;
}
- err = ksba_writer_write (writer, cr->cri.der, cr->cri.derlen);
- if (err)
- goto leave;
+ _ksba_der_add_der (dbld, cr->cri.der, cr->cri.derlen);
- /* store the signatureAlgorithm */
+ /* Store the signatureAlgorithm */
if (!cr->sig_val.algo)
return gpg_error (GPG_ERR_MISSING_VALUE);
- err = _ksba_der_write_algorithm_identifier (writer,
- cr->sig_val.algo, NULL, 0);
- if (err)
- goto leave;
+ _ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
+ _ksba_der_add_oid (dbld, cr->sig_val.algo);
+ if (!cr->sig_val.is_ecc)
+ _ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0);
+ _ksba_der_add_end (dbld);
- /* write the signature */
- err = _ksba_ber_write_tl (writer, TYPE_BIT_STRING, CLASS_UNIVERSAL, 0,
- 1 + cr->sig_val.valuelen);
- if (!err)
- err = ksba_writer_write (writer, "", 1);
- if (!err)
- err = ksba_writer_write (writer, cr->sig_val.value, cr->sig_val.valuelen);
- if (err)
- goto leave;
+ /* Write the signature */
+ _ksba_der_add_bts (dbld, cr->sig_val.value, cr->sig_val.valuelen, 0);
- /* pack it into the outer sequence */
- value = ksba_writer_snatch_mem (writer, &valuelen);
- if (!value)
- {
- err = gpg_error (GPG_ERR_ENOMEM);
- goto leave;
- }
- err = ksba_writer_set_mem (writer, valuelen+10);
- if (err)
- goto leave;
- /* write outer sequence */
- err = _ksba_ber_write_tl (writer, TYPE_SEQUENCE, CLASS_UNIVERSAL,
- 1, valuelen);
- if (!err)
- err = ksba_writer_write (writer, value, valuelen);
+ /* End outer sequence. */
+ _ksba_der_add_end (dbld);
+
+ /* and finally write the result */
+ err = _ksba_der_builder_get (dbld, &value, &valuelen);
if (err)
goto leave;
- /* and finally write the result */
- xfree (value);
- value = ksba_writer_snatch_mem (writer, &valuelen);
- if (!value)
- err = gpg_error (GPG_ERR_ENOMEM);
- else if (!cr->writer)
+ if (!cr->writer)
err = gpg_error (GPG_ERR_MISSING_ACTION);
else
err = ksba_writer_write (cr->writer, value, valuelen);
leave:
- ksba_writer_release (writer);
+ ksba_der_release (dbld);
xfree (value);
return err;
}
-/* The main function to build a certificate request. It used used in
- a loop so allow for interaction between the function and the caller */
+/* The main function to build a certificate request. It is used in a
+ * loop to allow for interaction between the function and the caller */
gpg_error_t
ksba_certreq_build (ksba_certreq_t cr, ksba_stop_reason_t *r_stopreason)
{
enum {
sSTART,
sHASHING,
sGOTSIG,
sERROR
} state = sERROR;
gpg_error_t err = 0;
ksba_stop_reason_t stop_reason;
if (!cr || !r_stopreason)
return gpg_error (GPG_ERR_INV_VALUE);
if (!cr->any_build_done)
{ /* first time initialization of the stop reason */
*r_stopreason = 0;
cr->any_build_done = 1;
}
/* Calculate state from last reason */
stop_reason = *r_stopreason;
*r_stopreason = KSBA_SR_RUNNING;
switch (stop_reason)
{
case 0:
state = sSTART;
break;
case KSBA_SR_NEED_HASH:
state = sHASHING;
break;
case KSBA_SR_NEED_SIG:
if (!cr->sig_val.algo)
err = gpg_error (GPG_ERR_MISSING_ACTION);
else
state = sGOTSIG;
break;
case KSBA_SR_RUNNING:
err = gpg_error (GPG_ERR_INV_STATE);
break;
default:
err = gpg_error (GPG_ERR_BUG);
break;
}
if (err)
return err;
/* Do the action */
switch (state)
{
case sSTART:
err = build_cri (cr);
break;
case sHASHING:
err = hash_cri (cr);
break;
case sGOTSIG:
err = sign_and_write (cr);
break;
default:
err = gpg_error (GPG_ERR_INV_STATE);
break;
}
if (err)
return err;
/* Calculate new stop reason */
switch (state)
{
case sSTART:
stop_reason = KSBA_SR_NEED_HASH; /* caller should set the hash function*/
break;
case sHASHING:
stop_reason = KSBA_SR_NEED_SIG;
break;
case sGOTSIG:
stop_reason = KSBA_SR_READY;
break;
default:
break;
}
*r_stopreason = stop_reason;
return 0;
}
diff --git a/src/certreq.h b/src/certreq.h
index 43388b6..2a9503c 100644
--- a/src/certreq.h
+++ b/src/certreq.h
@@ -1,124 +1,125 @@
/* certreq.h - Internal definitions for pkcs-10 objects
* Copyright (C) 2002, 2005, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* KSBA is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#ifndef CERTREQ_H
#define CERTREQ_H 1
#include "ksba.h"
#ifndef HAVE_TYPEDEFD_ASNNODE
typedef struct asn_node_struct *AsnNode; /* FIXME: should not go here */
#define HAVE_TYPEDEFD_ASNNODE
#endif
struct extn_list_s
{
struct extn_list_s *next;
const char *oid;
int critical;
int derlen;
unsigned char der[1];
};
/* Object to collect information for building a GeneralNames. */
struct general_names_s
{
struct general_names_s *next;
int tag; /* The GeneralName CHOICE. Only certain values are
supported. This is not strictly required because DATA
below has already been prefixed with the DER encoded
tag. */
size_t datalen; /* Length of the data. */
char data[1]; /* The actual data: encoded tag, llength and value. */
};
struct ksba_certreq_s
{
gpg_error_t last_error;
ksba_writer_t writer;
void (*hash_fnc)(void *, const void *, size_t);
void *hash_fnc_arg;
int any_build_done;
struct {
struct {
char *der; /* Malloced serialno; if this is set we want to
build a real X.509 certificate. */
size_t derlen;
} serial;
struct {
char *der;
size_t derlen;
} issuer;
ksba_isotime_t not_before;
ksba_isotime_t not_after;
struct {
unsigned char *der;
size_t derlen;
} siginfo;
} x509;
struct {
char *der;
size_t derlen;
} subject;
struct {
unsigned char *der;
size_t derlen;
} key;
struct general_names_s *subject_alt_names;
struct extn_list_s *extn_list;
struct {
unsigned char *der;
size_t derlen;
} cri;
struct {
char *algo;
+ int is_ecc; /* 1 = plain ecc, 2 = EdDSA */
unsigned char *value;
size_t valuelen;
} sig_val;
};
#endif /*CERTREQ_H*/
diff --git a/src/keyinfo.c b/src/keyinfo.c
index 48f396c..666726f 100644
--- a/src/keyinfo.c
+++ b/src/keyinfo.c
@@ -1,1596 +1,1633 @@
/* keyinfo.c - Parse and build a keyInfo structure
* Copyright (C) 2001, 2002, 2007, 2008, 2012, 2020 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* KSBA is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
/* Instead of using the ASN parser - which is easily possible - we use
a simple handcoded one to speed up the operation and to make it
more robust. */
#include
#include
#include
#include
#include
#include "util.h"
#include "asn1-func.h"
#include "keyinfo.h"
#include "shared.h"
#include "convert.h"
#include "ber-help.h"
#include "sexp-parse.h"
#include "stringbuf.h"
#include "der-builder.h"
/* Constants used for the public key algorithms. */
typedef enum
{
+ PKALGO_NONE,
PKALGO_RSA,
PKALGO_DSA,
PKALGO_ECC,
PKALGO_X25519,
PKALGO_X448,
PKALGO_ED25519,
PKALGO_ED448
}
pkalgo_t;
struct algo_table_s {
const char *oidstring;
const unsigned char *oid; /* NULL indicattes end of table */
int oidlen;
int supported; /* Values > 1 are also used to indicate hacks. */
pkalgo_t pkalgo;
const char *algo_string;
const char *elem_string; /* parameter name or '-' */
const char *ctrl_string; /* expected tag values (value > 127 are raw data)*/
const char *parmelem_string; /* parameter name or '-'. */
const char *parmctrl_string; /* expected tag values. */
const char *digest_string; /* The digest algo if included in the OID. */
};
/* Special values for the supported field. */
#define SUPPORTED_RSAPSS 2
static const struct algo_table_s pk_algo_table[] = {
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */
"1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9,
1, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02" },
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.7 */
"1.2.840.113549.1.1.7", /* RSAES-OAEP */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x07", 9,
0, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"},
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */
"1.2.840.113549.1.1.10", /* rsaPSS */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9,
SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02"},
{ /* */
"2.5.8.1.1", /* rsa (ambiguous due to missing padding rules)*/
"\x55\x08\x01\x01", 4,
1, PKALGO_RSA, "ambiguous-rsa", "-ne", "\x30\x02\x02" },
{ /* iso.member-body.us.x9-57.x9cm.1 */
"1.2.840.10040.4.1", /* dsa */
"\x2a\x86\x48\xce\x38\x04\x01", 7,
1, PKALGO_DSA, "dsa", "y", "\x02", "-pqg", "\x30\x02\x02\x02" },
{ /* iso.member-body.us.ansi-x9-62.2.1 */
"1.2.840.10045.2.1", /* ecPublicKey */
"\x2a\x86\x48\xce\x3d\x02\x01", 7,
1, PKALGO_ECC, "ecc", "q", "\x80" },
{ /* iso.identified-organization.thawte.110 */
"1.3.101.110", /* X25519 */
"\x2b\x65\x6e", 3,
1, PKALGO_X25519, "ecc", "q", "\x80" },
{ /* iso.identified-organization.thawte.111 */
"1.3.101.111", /* X448 */
"\x2b\x65\x6f", 3,
1, PKALGO_X448, "ecc", "q", "\x80" },
{ /* iso.identified-organization.thawte.112 */
"1.3.101.112", /* Ed25519 */
"\x2b\x65\x70", 3,
1, PKALGO_ED25519, "ecc", "q", "\x80" },
{ /* iso.identified-organization.thawte.113 */
"1.3.101.113", /* Ed448 */
"\x2b\x65\x71", 3,
1, PKALGO_ED448, "ecc", "q", "\x80" },
{NULL}
};
static const struct algo_table_s sig_algo_table[] = {
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.5 */
"1.2.840.113549.1.1.5", /* sha1WithRSAEncryption */
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" },
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.4 */
"1.2.840.113549.1.1.4", /* md5WithRSAEncryption */
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x04", 9,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md5" },
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.2 */
"1.2.840.113549.1.1.2", /* md2WithRSAEncryption */
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x02", 9,
0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "md2" },
{ /* iso.member-body.us.x9-57.x9cm.1 */
"1.2.840.10040.4.3", /* dsa */
"\x2a\x86\x48\xce\x38\x04\x01", 7,
1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02" },
{ /* iso.member-body.us.x9-57.x9cm.3 */
"1.2.840.10040.4.3", /* dsaWithSha1 */
"\x2a\x86\x48\xce\x38\x04\x03", 7,
1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" },
{ /* Teletrust signature algorithm. */
"1.3.36.8.5.1.2.2", /* dsaWithRIPEMD160 */
"\x2b\x24\x08\x05\x01\x02\x02", 7,
1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "rmd160" },
{ /* NIST Algorithm */
"2.16.840.1.101.3.4.3.1", /* dsaWithSha224 */
"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11,
1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" },
{ /* NIST Algorithm (the draft also used .1 but we better use .2) */
"2.16.840.1.101.3.4.3.2", /* dsaWithSha256 */
"\x06\x09\x60\x86\x48\x01\x65\x03\x04\x03\x01", 11,
1, PKALGO_DSA, "dsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" },
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha1 */
"1.2.840.10045.4.1", /* ecdsa */
"\x2a\x86\x48\xce\x3d\x04\x01", 7,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha1" },
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-specified */
"1.2.840.10045.4.3",
"\x2a\x86\x48\xce\x3d\x04\x03", 7,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, NULL },
/* The digest algorithm is given by the parameter. */
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha224 */
"1.2.840.10045.4.3.1",
"\x2a\x86\x48\xce\x3d\x04\x03\x01", 8,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha224" },
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha256 */
"1.2.840.10045.4.3.2",
"\x2a\x86\x48\xce\x3d\x04\x03\x02", 8,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha256" },
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha384 */
"1.2.840.10045.4.3.3",
"\x2a\x86\x48\xce\x3d\x04\x03\x03", 8,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha384" },
{ /* iso.member-body.us.ansi-x9-62.signatures.ecdsa-with-sha512 */
"1.2.840.10045.4.3.4",
"\x2a\x86\x48\xce\x3d\x04\x03\x04", 8,
1, PKALGO_ECC, "ecdsa", "-rs", "\x30\x02\x02", NULL, NULL, "sha512" },
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */
"1.2.840.113549.1.1.1", /* rsaEncryption used without hash algo*/
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9,
1, PKALGO_RSA, "rsa", "s", "\x82" },
{ /* from NIST's OIW - actually belongs in a pure hash table */
"1.3.14.3.2.26", /* sha1 */
"\x2B\x0E\x03\x02\x1A", 5,
0, PKALGO_RSA, "sha-1", "", "", NULL, NULL, "sha1" },
{ /* As used by telesec cards */
"1.3.36.3.3.1.2", /* rsaSignatureWithripemd160 */
"\x2b\x24\x03\x03\x01\x02", 6,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" },
{ /* from NIST's OIW - used by TU Darmstadt */
"1.3.14.3.2.29", /* sha-1WithRSAEncryption */
"\x2B\x0E\x03\x02\x1D", 5,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" },
{ /* from PKCS#1 */
"1.2.840.113549.1.1.11", /* sha256WithRSAEncryption */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b", 9,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha256" },
{ /* from PKCS#1 */
"1.2.840.113549.1.1.12", /* sha384WithRSAEncryption */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c", 9,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha384" },
{ /* from PKCS#1 */
"1.2.840.113549.1.1.13", /* sha512WithRSAEncryption */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d", 9,
1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha512" },
{ /* iso.member-body.us.rsadsi.pkcs.pkcs-1.10 */
"1.2.840.113549.1.1.10", /* rsaPSS */
"\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0a", 9,
SUPPORTED_RSAPSS, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, NULL},
{ /* TeleTrust signature scheme with RSA signature and DSI according
to ISO/IEC 9796-2 with random number and RIPEMD-160. I am not
sure for what this is good; thus disabled. */
"1.3.36.3.4.3.2.2", /* sigS_ISO9796-2rndWithrsa_ripemd160 */
"\x2B\x24\x03\x04\x03\x02\x02", 7,
0, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "rmd160" },
{ /* iso.identified-organization.thawte.112 */
"1.3.101.112", /* Ed25519 */
"\x2b\x65\x70", 3,
1, PKALGO_ED25519, "eddsa", "", "", NULL, NULL, NULL },
{ /* iso.identified-organization.thawte.113 */
"1.3.101.113", /* Ed448 */
"\x2b\x65\x71", 3,
1, PKALGO_ED448, "eddsa", "", "", NULL, NULL, NULL },
{NULL}
};
static const struct algo_table_s enc_algo_table[] = {
{/* iso.member-body.us.rsadsi.pkcs.pkcs-1.1 */
"1.2.840.113549.1.1.1", /* rsaEncryption (RSAES-PKCA1-v1.5) */
"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9,
1, PKALGO_RSA, "rsa", "a", "\x82" },
{/* iso.member-body.us.ansi-x9-62.2.1 */
"1.2.840.10045.2.1", /* ecPublicKey */
"\x2a\x86\x48\xce\x3d\x02\x01", 7,
1, PKALGO_ECC, "ecdh", "e", "\x80" },
{NULL}
};
/* This tables maps names of ECC curves names to OIDs. A similar
table is used by Libgcrypt. */
static const struct
{
const char *oid;
const char *name;
+ unsigned char pkalgo; /* If not 0 force the use of ALGO. */
} curve_names[] =
{
- /* For backward compatibility we keep the two original OIDs from
- * OpenPGP and do not replace them by those from RFC-8410. It is
- * anyway better to use the dotted decimal form. */
- { "1.3.6.1.4.1.11591.15.1", "Ed25519" },
- { "1.3.6.1.4.1.3029.1.5.1", "Curve25519" },
- { "1.3.101.110", "X25519" },
+ { "1.3.101.112", "Ed25519", PKALGO_ED25519},
+ { "1.3.101.110", "Curve25519", PKALGO_X25519},
+ { "1.3.101.110", "X25519", PKALGO_X25519},
- { "1.3.101.113", "Ed448" },
- { "1.3.101.111", "X448" },
+ { "1.3.101.113", "Ed448", PKALGO_ED448 },
+ { "1.3.101.111", "X448", PKALGO_X448 },
{ "1.2.840.10045.3.1.1", "NIST P-192" },
{ "1.2.840.10045.3.1.1", "nistp192" },
{ "1.2.840.10045.3.1.1", "prime192v1" },
{ "1.2.840.10045.3.1.1", "secp192r1" },
{ "1.3.132.0.33", "NIST P-224" },
{ "1.3.132.0.33", "nistp224" },
{ "1.3.132.0.33", "secp224r1" },
{ "1.2.840.10045.3.1.7", "NIST P-256" },
{ "1.2.840.10045.3.1.7", "nistp256" },
{ "1.2.840.10045.3.1.7", "prime256v1" },
{ "1.2.840.10045.3.1.7", "secp256r1" },
{ "1.3.132.0.34", "NIST P-384" },
{ "1.3.132.0.34", "nistp384" },
{ "1.3.132.0.34", "secp384r1" },
{ "1.3.132.0.35", "NIST P-521" },
{ "1.3.132.0.35", "nistp521" },
{ "1.3.132.0.35", "secp521r1" },
{ "1.3.36.3.3.2.8.1.1.1" , "brainpoolP160r1" },
{ "1.3.36.3.3.2.8.1.1.3" , "brainpoolP192r1" },
{ "1.3.36.3.3.2.8.1.1.5" , "brainpoolP224r1" },
{ "1.3.36.3.3.2.8.1.1.7" , "brainpoolP256r1" },
{ "1.3.36.3.3.2.8.1.1.9" , "brainpoolP320r1" },
{ "1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1" },
{ "1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1" },
{ "1.2.643.2.2.35.1", "GOST2001-CryptoPro-A" },
{ "1.2.643.2.2.35.2", "GOST2001-CryptoPro-B" },
{ "1.2.643.2.2.35.3", "GOST2001-CryptoPro-C" },
{ "1.2.643.7.1.2.1.2.1", "GOST2012-tc26-A" },
{ "1.2.643.7.1.2.1.2.2", "GOST2012-tc26-B" },
{ "1.3.132.0.10", "secp256k1" },
{ NULL, NULL}
};
#define TLV_LENGTH(prefix) do { \
if (!prefix ## len) \
return gpg_error (GPG_ERR_INV_KEYINFO); \
c = *(prefix)++; prefix ## len--; \
if (c == 0x80) \
return gpg_error (GPG_ERR_NOT_DER_ENCODED); \
if (c == 0xff) \
return gpg_error (GPG_ERR_BAD_BER); \
\
if ( !(c & 0x80) ) \
len = c; \
else \
{ \
int count = c & 0x7f; \
\
for (len=0; count; count--) \
{ \
len <<= 8; \
if (!prefix ## len) \
return gpg_error (GPG_ERR_BAD_BER);\
c = *(prefix)++; prefix ## len--; \
len |= c & 0xff; \
} \
} \
if (len > prefix ## len) \
return gpg_error (GPG_ERR_INV_KEYINFO); \
} while (0)
/* Given a string BUF of length BUFLEN with either a curve name or its
* OID in dotted form return a string in dotted form of the name. The
- * caller must free the result. On error NULL is returned. */
+ * caller must free the result. On error NULL is returned. If a
+ * curve requires the use of a certain algorithm, that algorithm is
+ * stored at R_PKALGO. */
static char *
-get_ecc_curve_oid (const unsigned char *buf, size_t buflen)
+get_ecc_curve_oid (const unsigned char *buf, size_t buflen, pkalgo_t *r_pkalgo)
{
unsigned char *result;
+ int i, find_pkalgo;
/* Skip an optional "oid." prefix. */
if (buflen > 4 && buf[3] == '.' && digitp (buf+4)
&& ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd')
||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D')))
{
buf += 4;
buflen -= 4;
}
/* If it does not look like an OID - map it through the table. */
if (buflen && !digitp (buf))
{
- int i;
-
for (i=0; curve_names[i].oid; i++)
if (buflen == strlen (curve_names[i].name)
&& !memcmp (buf, curve_names[i].name, buflen))
break;
if (!curve_names[i].oid)
return NULL; /* Not found. */
buf = curve_names[i].oid;
buflen = strlen (curve_names[i].oid);
+ *r_pkalgo = curve_names[i].pkalgo;
+ find_pkalgo = 0;
}
+ else
+ find_pkalgo = 1;
result = xtrymalloc (buflen + 1);
if (!result)
return NULL; /* Ooops */
memcpy (result, buf, buflen);
result[buflen] = 0;
+
+ if (find_pkalgo)
+ {
+ /* We still need to check whether the OID requires a certain ALGO. */
+ for (i=0; curve_names[i].oid; i++)
+ if (!strcmp (curve_names[i].oid, result))
+ {
+ *r_pkalgo = curve_names[i].pkalgo;
+ break;
+ }
+ }
+
return result;
}
/* Return the OFF and the LEN of algorithm within DER. Do some checks
and return the number of bytes read in r_nread, adding this to der
does point into the BIT STRING.
mode 0: just get the algorithm identifier. FIXME: should be able to
handle BER Encoding.
mode 1: as described.
*/
static gpg_error_t
get_algorithm (int mode, const unsigned char *der, size_t derlen,
size_t *r_nread, size_t *r_pos, size_t *r_len, int *r_bitstr,
size_t *r_parm_pos, size_t *r_parm_len, int *r_parm_type)
{
int c;
const unsigned char *start = der;
const unsigned char *startseq;
unsigned long seqlen, len;
*r_bitstr = 0;
if (r_parm_pos)
*r_parm_pos = 0;
if (r_parm_len)
*r_parm_len = 0;
if (r_parm_type)
*r_parm_type = 0;
/* get the inner sequence */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if ( c != 0x30 )
return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */
TLV_LENGTH(der);
seqlen = len;
startseq = der;
/* get the object identifier */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if ( c != 0x06 )
return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not an OBJECT IDENTIFIER */
TLV_LENGTH(der);
/* der does now point to an oid of length LEN */
*r_pos = der - start;
*r_len = len;
der += len;
derlen -= len;
seqlen -= der - startseq;;
/* Parse the parameter. */
if (seqlen)
{
const unsigned char *startparm = der;
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if ( c == 0x05 )
{
/* gpgrt_log_debug ("%s: parameter: NULL \n", __func__); */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if (c)
return gpg_error (GPG_ERR_BAD_BER); /* NULL must have a
length of 0 */
seqlen -= 2;
}
else if (r_parm_pos && r_parm_len && c == 0x04)
{
/* This is an octet string parameter and we need it. */
if (r_parm_type)
*r_parm_type = TYPE_OCTET_STRING;
TLV_LENGTH(der);
*r_parm_pos = der - start;
*r_parm_len = len;
seqlen -= der - startparm;
der += len;
derlen -= len;
seqlen -= len;
}
else if (r_parm_pos && r_parm_len && c == 0x06)
{
/* This is an object identifier. */
if (r_parm_type)
*r_parm_type = TYPE_OBJECT_ID;
TLV_LENGTH(der);
*r_parm_pos = der - start;
*r_parm_len = len;
seqlen -= der - startparm;
der += len;
derlen -= len;
seqlen -= len;
}
else if (r_parm_pos && r_parm_len && c == 0x30)
{
/* This is a sequence. */
if (r_parm_type)
*r_parm_type = TYPE_SEQUENCE;
TLV_LENGTH(der);
*r_parm_pos = startparm - start;
*r_parm_len = len + (der - startparm);
seqlen -= der - startparm;
der += len;
derlen -= len;
seqlen -= len;
}
else
{
/* printf ("parameter: with tag %02x - ignored\n", c); */
TLV_LENGTH(der);
seqlen -= der - startparm;
/* skip the value */
der += len;
derlen -= len;
seqlen -= len;
}
}
if (seqlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
if (mode)
{
/* move forward to the BIT_STR */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if (c == 0x03)
*r_bitstr = 1; /* BIT STRING */
else if (c == 0x04)
; /* OCTECT STRING */
else
return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a BIT STRING */
TLV_LENGTH(der);
}
*r_nread = der - start;
return 0;
}
gpg_error_t
_ksba_parse_algorithm_identifier (const unsigned char *der, size_t derlen,
size_t *r_nread, char **r_oid)
{
return _ksba_parse_algorithm_identifier2 (der, derlen,
r_nread, r_oid, NULL, NULL);
}
/* Note that R_NREAD, R_PARM, and R_PARMLEN are optional. */
gpg_error_t
_ksba_parse_algorithm_identifier2 (const unsigned char *der, size_t derlen,
size_t *r_nread, char **r_oid,
char **r_parm, size_t *r_parmlen)
{
gpg_error_t err;
int is_bitstr;
size_t nread, off, len, off2, len2;
int parm_type;
/* fixme: get_algorithm might return the error invalid keyinfo -
this should be invalid algorithm identifier */
*r_oid = NULL;
if (r_nread)
*r_nread = 0;
off2 = len2 = 0;
err = get_algorithm (0, der, derlen, &nread, &off, &len, &is_bitstr,
&off2, &len2, &parm_type);
if (err)
return err;
if (r_nread)
*r_nread = nread;
*r_oid = ksba_oid_to_str (der+off, len);
if (!*r_oid)
return gpg_error (GPG_ERR_ENOMEM);
/* Special hack for ecdsaWithSpecified. We replace the returned OID
by the one in the parameter. */
if (off2 && len2 && parm_type == TYPE_SEQUENCE
&& !strcmp (*r_oid, "1.2.840.10045.4.3"))
{
xfree (*r_oid);
*r_oid = NULL;
err = get_algorithm (0, der+off2, len2, &nread, &off, &len, &is_bitstr,
NULL, NULL, NULL);
if (err)
{
if (r_nread)
*r_nread = 0;
return err;
}
*r_oid = ksba_oid_to_str (der+off2+off, len);
if (!*r_oid)
{
if (r_nread)
*r_nread = 0;
return gpg_error (GPG_ERR_ENOMEM);
}
off2 = len2 = 0; /* So that R_PARM is set to NULL. */
}
if (r_parm && r_parmlen)
{
if (off2 && len2)
{
*r_parm = xtrymalloc (len2);
if (!*r_parm)
{
xfree (*r_oid);
*r_oid = NULL;
return gpg_error (GPG_ERR_ENOMEM);
}
memcpy (*r_parm, der+off2, len2);
*r_parmlen = len2;
}
else
{
*r_parm = NULL;
*r_parmlen = 0;
}
}
return 0;
}
/* Assume that der is a buffer of length DERLEN with a DER encoded
ASN.1 structure like this:
keyInfo ::= SEQUENCE {
SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
publicKey BIT STRING }
The function parses this structure and create a SEXP suitable to be
used as a public key in Libgcrypt. The S-Exp will be returned in a
string which the caller must free.
We don't pass an ASN.1 node here but a plain memory block. */
gpg_error_t
_ksba_keyinfo_to_sexp (const unsigned char *der, size_t derlen,
ksba_sexp_t *r_string)
{
gpg_error_t err;
int c;
size_t nread, off, len, parm_off, parm_len;
int parm_type;
char *parm_oid = NULL;
int algoidx;
int is_bitstr;
const unsigned char *parmder = NULL;
size_t parmderlen = 0;
const unsigned char *ctrl;
const char *elem;
struct stringbuf sb;
*r_string = NULL;
/* check the outer sequence */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if ( c != 0x30 )
return gpg_error (GPG_ERR_UNEXPECTED_TAG); /* not a SEQUENCE */
TLV_LENGTH(der);
/* and now the inner part */
err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr,
&parm_off, &parm_len, &parm_type);
if (err)
return err;
/* look into our table of supported algorithms */
for (algoidx=0; pk_algo_table[algoidx].oid; algoidx++)
{
if ( len == pk_algo_table[algoidx].oidlen
&& !memcmp (der+off, pk_algo_table[algoidx].oid, len))
break;
}
if (!pk_algo_table[algoidx].oid)
return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
if (!pk_algo_table[algoidx].supported)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
if (parm_off && parm_len && parm_type == TYPE_OBJECT_ID)
parm_oid = ksba_oid_to_str (der+parm_off, parm_len);
else if (parm_off && parm_len)
{
parmder = der + parm_off;
parmderlen = parm_len;
}
der += nread;
derlen -= nread;
if (is_bitstr)
{ /* Funny: X.509 defines the signature value as a bit string but
CMS as an octet string - for ease of implementation we always
allow both */
if (!derlen)
{
xfree (parm_oid);
return gpg_error (GPG_ERR_INV_KEYINFO);
}
c = *der++; derlen--;
if (c)
fprintf (stderr, "warning: number of unused bits is not zero\n");
}
/* fixme: we should calculate the initial length form the size of the
sequence, so that we don't need a realloc later */
init_stringbuf (&sb, 100);
put_stringbuf (&sb, "(10:public-key(");
/* fixme: we can also use the oidstring here and prefix it with
"oid." - this way we can pass more information into Libgcrypt or
whatever library is used */
put_stringbuf_sexp (&sb, pk_algo_table[algoidx].algo_string);
/* Insert the curve name for ECC. */
if (pk_algo_table[algoidx].pkalgo == PKALGO_ECC && parm_oid)
{
put_stringbuf (&sb, "(");
put_stringbuf_sexp (&sb, "curve");
put_stringbuf_sexp (&sb, parm_oid);
put_stringbuf (&sb, ")");
}
else if (pk_algo_table[algoidx].pkalgo == PKALGO_ED25519
|| pk_algo_table[algoidx].pkalgo == PKALGO_ED448
|| pk_algo_table[algoidx].pkalgo == PKALGO_X25519
|| pk_algo_table[algoidx].pkalgo == PKALGO_X448)
{
put_stringbuf (&sb, "(");
put_stringbuf_sexp (&sb, "curve");
put_stringbuf_sexp (&sb, pk_algo_table[algoidx].oidstring);
put_stringbuf (&sb, ")");
}
/* If parameters are given and we have a description for them, parse
them. */
if (parmder && parmderlen
&& pk_algo_table[algoidx].parmelem_string
&& pk_algo_table[algoidx].parmctrl_string)
{
elem = pk_algo_table[algoidx].parmelem_string;
ctrl = pk_algo_table[algoidx].parmctrl_string;
for (; *elem; ctrl++, elem++)
{
int is_int;
if ( (*ctrl & 0x80) && !elem[1] )
{
/* Hack to allow reading a raw value. */
is_int = 1;
len = parmderlen;
}
else
{
if (!parmderlen)
{
xfree (parm_oid);
return gpg_error (GPG_ERR_INV_KEYINFO);
}
c = *parmder++; parmderlen--;
if ( c != *ctrl )
{
xfree (parm_oid);
return gpg_error (GPG_ERR_UNEXPECTED_TAG);
}
is_int = c == 0x02;
TLV_LENGTH (parmder);
}
if (is_int && *elem != '-') /* Take this integer. */
{
char tmp[2];
put_stringbuf (&sb, "(");
tmp[0] = *elem; tmp[1] = 0;
put_stringbuf_sexp (&sb, tmp);
put_stringbuf_mem_sexp (&sb, parmder, len);
parmder += len;
parmderlen -= len;
put_stringbuf (&sb, ")");
}
}
}
/* FIXME: We don't release the stringbuf in case of error
better let the macro jump to a label */
elem = pk_algo_table[algoidx].elem_string;
ctrl = pk_algo_table[algoidx].ctrl_string;
for (; *elem; ctrl++, elem++)
{
int is_int;
if ( (*ctrl & 0x80) && !elem[1] )
{
/* Hack to allow reading a raw value. */
is_int = 1;
len = derlen;
}
else
{
if (!derlen)
{
xfree (parm_oid);
return gpg_error (GPG_ERR_INV_KEYINFO);
}
c = *der++; derlen--;
if ( c != *ctrl )
{
xfree (parm_oid);
return gpg_error (GPG_ERR_UNEXPECTED_TAG);
}
is_int = c == 0x02;
TLV_LENGTH (der);
}
if (is_int && *elem != '-') /* Take this integer. */
{
char tmp[2];
put_stringbuf (&sb, "(");
tmp[0] = *elem; tmp[1] = 0;
put_stringbuf_sexp (&sb, tmp);
put_stringbuf_mem_sexp (&sb, der, len);
der += len;
derlen -= len;
put_stringbuf (&sb, ")");
}
}
put_stringbuf (&sb, "))");
xfree (parm_oid);
*r_string = get_stringbuf (&sb);
if (!*r_string)
return gpg_error (GPG_ERR_ENOMEM);
return 0;
}
/* Match the algorithm string given in BUF which is of length BUFLEN
* with the known algorithms from our table and return the table
* entriy with the OID string. If WITH_SIG is true, the table of
* signature algorithms is consulted first. */
static const char *
oid_from_buffer (const unsigned char *buf, unsigned int buflen,
pkalgo_t *r_pkalgo, int with_sig)
{
int i;
/* Ignore an optional "oid." prefix. */
if (buflen > 4 && buf[3] == '.' && digitp (buf+4)
&& ((buf[0] == 'o' && buf[1] == 'i' && buf[2] == 'd')
||(buf[0] == 'O' && buf[1] == 'I' && buf[2] == 'D')))
{
buf += 4;
buflen -= 4;
}
if (with_sig)
{
/* Scan the signature table first. */
for (i=0; sig_algo_table[i].oid; i++)
{
if (!sig_algo_table[i].supported)
continue;
if (buflen == strlen (sig_algo_table[i].oidstring)
&& !memcmp (buf, sig_algo_table[i].oidstring, buflen))
break;
if (buflen == strlen (sig_algo_table[i].algo_string)
&& !memcmp (buf, sig_algo_table[i].algo_string, buflen))
break;
}
if (sig_algo_table[i].oid)
{
*r_pkalgo = sig_algo_table[i].pkalgo;
return sig_algo_table[i].oidstring;
}
}
/* Scan the standard table. */
for (i=0; pk_algo_table[i].oid; i++)
{
if (!pk_algo_table[i].supported)
continue;
if (buflen == strlen (pk_algo_table[i].oidstring)
&& !memcmp (buf, pk_algo_table[i].oidstring, buflen))
break;
if (buflen == strlen (pk_algo_table[i].algo_string)
&& !memcmp (buf, pk_algo_table[i].algo_string, buflen))
break;
}
if (!pk_algo_table[i].oid)
return NULL;
*r_pkalgo = pk_algo_table[i].pkalgo;
return pk_algo_table[i].oidstring;
}
/* If ALGOINFOMODE is false: Take the "public-key" s-expression SEXP
* and convert it into a DER encoded publicKeyInfo.
*
* If ALGOINFOMODE is true: Take the "sig-val" s-expression SEXP and
* convert it into a DER encoded algorithmInfo. */
gpg_error_t
_ksba_keyinfo_from_sexp (ksba_const_sexp_t sexp, int algoinfomode,
unsigned char **r_der, size_t *r_derlen)
{
gpg_error_t err;
const unsigned char *s;
char *endp;
unsigned long n;
const char *algo_oid;
char *curve_oid = NULL;
- pkalgo_t pkalgo;
+ pkalgo_t pkalgo, force_pkalgo;
int i;
struct {
const char *name;
int namelen;
const unsigned char *value;
int valuelen;
} parm[10];
int parmidx;
const char *parmdesc, *algoparmdesc;
ksba_der_t dbld = NULL;
ksba_der_t dbld2 = NULL;
unsigned char *tmpder;
size_t tmpderlen;
if (!sexp)
return gpg_error (GPG_ERR_INV_VALUE);
s = sexp;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
n = strtoul (s, &endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */
s++;
if (algoinfomode && n == 7 && !memcmp (s, "sig-val", 7))
s += 7;
else if (n == 10 || !memcmp (s, "public-key", 10))
s += 10;
else
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
if (*s != '(')
return gpg_error (digitp (s)? GPG_ERR_UNKNOWN_SEXP : GPG_ERR_INV_SEXP);
s++;
/* Break out the algorithm ID */
n = strtoul (s, &endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP); /* We don't allow empty lengths. */
s++;
algo_oid = oid_from_buffer (s, n, &pkalgo, algoinfomode);
if (!algo_oid)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-
s += n;
/* Collect all the values. */
+ force_pkalgo = 0;
for (parmidx = 0; *s != ')' ; parmidx++)
{
if (parmidx >= DIM(parm))
{
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
if (*s != '(')
{
err = gpg_error (digitp(s)? GPG_ERR_UNKNOWN_SEXP:GPG_ERR_INV_SEXP);
goto leave;
}
s++;
n = strtoul (s, &endp, 10);
s = endp;
if (!n || *s != ':')
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
s++;
parm[parmidx].name = s;
parm[parmidx].namelen = n;
s += n;
if (!digitp(s))
{
err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */
goto leave;
}
n = strtoul (s, &endp, 10);
s = endp;
if (!n || *s != ':')
return gpg_error (GPG_ERR_INV_SEXP);
s++;
parm[parmidx].value = s;
parm[parmidx].valuelen = n;
s += n;
if ( *s != ')')
{
err = gpg_error (GPG_ERR_UNKNOWN_SEXP); /* ... or invalid S-Exp. */
goto leave;
}
s++;
if (parm[parmidx].namelen == 5
&& !memcmp (parm[parmidx].name, "curve", 5)
&& !curve_oid)
{
curve_oid = get_ecc_curve_oid (parm[parmidx].value,
- parm[parmidx].valuelen);
+ parm[parmidx].valuelen, &force_pkalgo);
parmidx--; /* No need to store this parameter. */
}
}
s++;
/* Allow for optional elements. */
if (*s == '(')
{
int depth = 1;
err = sskip (&s, &depth);
if (err)
goto leave;
}
/* We need another closing parenthesis. */
if ( *s != ')' )
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto leave;
}
+ if (force_pkalgo)
+ pkalgo = force_pkalgo;
+
/* Describe the parameters in the order we want them. For DSA wie
* also set algoparmdesc so that we can later build the parameters
* for the algorithmIdentifier. */
algoparmdesc = NULL;
switch (pkalgo)
{
case PKALGO_RSA:
parmdesc = algoinfomode? "" : "ne";
break;
case PKALGO_DSA:
parmdesc = algoinfomode? "" : "y";
algoparmdesc = "pqg";
break;
case PKALGO_ECC:
+ parmdesc = algoinfomode? "" : "q";
+ break;
case PKALGO_ED25519:
+ case PKALGO_X25519:
case PKALGO_ED448:
+ case PKALGO_X448:
parmdesc = algoinfomode? "" : "q";
+ if (curve_oid)
+ algo_oid = curve_oid;
break;
default:
err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
goto leave;
}
/* Create a builder. */
dbld = _ksba_der_builder_new (0);
if (!dbld)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* The outer sequence. */
if (!algoinfomode)
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
/* The sequence. */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
/* The object id. */
_ksba_der_add_oid (dbld, algo_oid);
/* The parameter. */
if (algoparmdesc)
{
/* Write the sequence tag followed by the integers. */
_ksba_der_add_tag (dbld, 0, TYPE_SEQUENCE);
for (s = algoparmdesc; *s; s++)
for (i=0; i < parmidx; i++)
if (parm[i].namelen == 1 && parm[i].name[0] == *s)
{
_ksba_der_add_int (dbld, parm[i].value, parm[i].valuelen, 1);
break; /* inner loop */
}
_ksba_der_add_end (dbld);
}
- else if (pkalgo == PKALGO_ECC)
+ else if (pkalgo == PKALGO_ECC && !algoinfomode)
{
/* We only support the namedCurve choice for ECC parameters. */
if (!curve_oid)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
goto leave;
}
_ksba_der_add_oid (dbld, curve_oid);
}
else if (pkalgo == PKALGO_RSA)
{
_ksba_der_add_ptr (dbld, 0, TYPE_NULL, NULL, 0);
}
_ksba_der_add_end (dbld); /* sequence. */
/* Add the bit string if we are not in algoinfomode. */
if (!algoinfomode)
{
if (*parmdesc == 'q' && !parmdesc[1])
{
/* This is ECC - Q is directly written as a bit string. */
for (i=0; i < parmidx; i++)
if (parm[i].namelen == 1 && parm[i].name[0] == 'q')
{
- _ksba_der_add_bts (dbld, parm[i].value, parm[i].valuelen, 0);
+ if ((parm[i].valuelen & 1) && parm[i].valuelen > 32
+ && (parm[i].value[0] == 0x40
+ || parm[i].value[0] == 0x41
+ || parm[i].value[0] == 0x42))
+ {
+ /* Odd length and prefixed with 0x40 - this is the
+ * rfc4880bis indicator octet for extended point
+ * formats - we may not emit that octet here. */
+ _ksba_der_add_bts (dbld, parm[i].value+1,
+ parm[i].valuelen-1, 0);
+ }
+ else
+ _ksba_der_add_bts (dbld, parm[i].value, parm[i].valuelen, 0);
break;
}
}
else /* Non-ECC - embed the values. */
{
dbld2 = _ksba_der_builder_new (10);
if (!dbld2)
{
err = gpg_error_from_syserror ();
goto leave;
}
/* Note that no sequence is used if only one integer is written. */
if (parmdesc[0] && parmdesc[1])
_ksba_der_add_tag (dbld2, 0, TYPE_SEQUENCE);
for (s = parmdesc; *s; s++)
for (i=0; i < parmidx; i++)
if (parm[i].namelen == 1 && parm[i].name[0] == *s)
{
_ksba_der_add_int (dbld2, parm[i].value, parm[i].valuelen, 1);
break; /* inner loop */
}
if (parmdesc[0] && parmdesc[1])
_ksba_der_add_end (dbld2);
err = _ksba_der_builder_get (dbld2, &tmpder, &tmpderlen);
if (err)
goto leave;
_ksba_der_add_bts (dbld, tmpder, tmpderlen, 0);
xfree (tmpder);
}
_ksba_der_add_end (dbld); /* Outer sequence. */
}
/* Get the result. */
err = _ksba_der_builder_get (dbld, r_der, r_derlen);
leave:
_ksba_der_release (dbld2);
_ksba_der_release (dbld);
xfree (curve_oid);
return err;
}
/* Helper function to parse the parameters used for rsaPSS.
* Given this sample DER object in (DER,DERLEN):
*
* SEQUENCE {
* [0] {
* SEQUENCE {
* OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3)
* }
* }
* [1] {
* SEQUENCE {
* OBJECT IDENTIFIER pkcs1-MGF (1 2 840 113549 1 1 8)
* SEQUENCE {
* OBJECT IDENTIFIER sha-512 (2 16 840 1 101 3 4 2 3)
* }
* }
* }
* [2] {
* INTEGER 64
* }
* }
*
* The function returns the first OID at R_PSSHASH and the salt length
* at R_SALTLEN. If the salt length is missing its default value is
* returned. In case object does not resemble a the expected rsaPSS
* parameters GPG_ERR_INV_OBJ is returned; other errors are returned
* for an syntatically invalid object. On error NULL is stored at
* R_PSSHASH.
*/
gpg_error_t
_ksba_keyinfo_get_pss_info (const unsigned char *der, size_t derlen,
char **r_psshash, unsigned int *r_saltlen)
{
gpg_error_t err;
struct tag_info ti;
char *psshash = NULL;
char *tmpoid = NULL;
unsigned int saltlen;
*r_psshash = NULL;
*r_saltlen = 0;
err = parse_sequence (&der, &derlen, &ti);
if (err)
goto leave;
/* Get the hash algo. */
err = parse_context_tag (&der, &derlen, &ti, 0);
if (err)
goto unknown_parms;
err = parse_sequence (&der, &derlen, &ti);
if (err)
goto unknown_parms;
err = parse_object_id_into_str (&der, &derlen, &psshash);
if (err)
goto unknown_parms;
err = parse_optional_null (&der, &derlen, NULL);
if (err)
goto unknown_parms;
/* Check the MGF OID and that its hash algo matches. */
err = parse_context_tag (&der, &derlen, &ti, 1);
if (err)
goto unknown_parms;
err = parse_sequence (&der, &derlen, &ti);
if (err)
goto leave;
err = parse_object_id_into_str (&der, &derlen, &tmpoid);
if (err)
goto unknown_parms;
if (strcmp (tmpoid, "1.2.840.113549.1.1.8")) /* MGF1 */
goto unknown_parms;
err = parse_sequence (&der, &derlen, &ti);
if (err)
goto leave;
xfree (tmpoid);
err = parse_object_id_into_str (&der, &derlen, &tmpoid);
if (err)
goto unknown_parms;
if (strcmp (tmpoid, psshash))
goto unknown_parms;
err = parse_optional_null (&der, &derlen, NULL);
if (err)
goto unknown_parms;
/* Get the optional saltLength. */
err = parse_context_tag (&der, &derlen, &ti, 2);
if (gpg_err_code (err) == GPG_ERR_INV_OBJ
|| gpg_err_code (err) == GPG_ERR_FALSE)
saltlen = 20; /* Optional element - use default value */
else if (err)
goto unknown_parms;
else
{
err = parse_integer (&der, &derlen, &ti);
if (err)
goto leave;
for (saltlen=0; ti.length; ti.length--)
{
saltlen <<= 8;
saltlen |= (*der++) & 0xff;
derlen--;
}
}
/* All fine. */
*r_psshash = psshash;
psshash = NULL;
*r_saltlen = saltlen;
err = 0;
goto leave;
unknown_parms:
err = gpg_error (GPG_ERR_INV_OBJ);
leave:
xfree (psshash);
xfree (tmpoid);
return err;
}
/* Mode 0: work as described under _ksba_sigval_to_sexp
* mode 1: work as described under _ksba_encval_to_sexp
* mode 2: same as mode 1 but for ECDH; in this mode
* KEYENCRYALO, KEYWRAPALGO, ENCRKEY, ENCRYKLEYLEN
* are also required.
*/
static gpg_error_t
cryptval_to_sexp (int mode, const unsigned char *der, size_t derlen,
const char *keyencralgo, const char *keywrapalgo,
const void *encrkey, size_t encrkeylen,
ksba_sexp_t *r_string)
{
gpg_error_t err;
const struct algo_table_s *algo_table;
int c;
size_t nread, off, len;
int algoidx;
int is_bitstr;
const unsigned char *ctrl;
const char *elem;
struct stringbuf sb;
size_t parm_off, parm_len;
int parm_type;
char *pss_hash = NULL;
unsigned int salt_length = 0;
/* FIXME: The entire function is very similar to keyinfo_to_sexp */
*r_string = NULL;
if (!mode)
algo_table = sig_algo_table;
else
algo_table = enc_algo_table;
err = get_algorithm (1, der, derlen, &nread, &off, &len, &is_bitstr,
&parm_off, &parm_len, &parm_type);
if (err)
return err;
/* look into our table of supported algorithms */
for (algoidx=0; algo_table[algoidx].oid; algoidx++)
{
if ( len == algo_table[algoidx].oidlen
&& !memcmp (der+off, algo_table[algoidx].oid, len))
break;
}
if (!algo_table[algoidx].oid)
return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
if (!algo_table[algoidx].supported)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
if (parm_type == TYPE_SEQUENCE
&& algo_table[algoidx].supported == SUPPORTED_RSAPSS)
{
/* This is rsaPSS and we collect the parameters. We simplify
* this by assuming that pkcs1-MGF is used with an identical
* hash algorithm. All other kinds of parameters are ignored. */
err = _ksba_keyinfo_get_pss_info (der + parm_off, parm_len,
&pss_hash, &salt_length);
if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
err = 0;
if (err)
return err;
}
der += nread;
derlen -= nread;
if (is_bitstr)
{ /* Funny: X.509 defines the signature value as a bit string but
CMS as an octet string - for ease of implementation we always
allow both */
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if (c)
fprintf (stderr, "warning: number of unused bits is not zero\n");
}
/* fixme: we should calculate the initial length form the size of the
sequence, so that we don't neen a realloc later */
init_stringbuf (&sb, 100);
put_stringbuf (&sb, mode? "(7:enc-val(":"(7:sig-val(");
put_stringbuf_sexp (&sb, algo_table[algoidx].algo_string);
/* FIXME: We don't release the stringbuf in case of error
better let the macro jump to a label */
if (!mode && (algo_table[algoidx].pkalgo == PKALGO_ED25519
||algo_table[algoidx].pkalgo == PKALGO_ED448))
{
/* EdDSA is special: R and S are simply concatenated; see rfc8410. */
put_stringbuf (&sb, "(1:r");
put_stringbuf_mem_sexp (&sb, der, derlen/2);
put_stringbuf (&sb, ")");
der += derlen/2;
derlen /= 2;
put_stringbuf (&sb, "(1:s");
put_stringbuf_mem_sexp (&sb, der, derlen);
put_stringbuf (&sb, ")");
}
else
{
elem = algo_table[algoidx].elem_string;
ctrl = algo_table[algoidx].ctrl_string;
for (; *elem; ctrl++, elem++)
{
int is_int;
if ( (*ctrl & 0x80) && !elem[1] )
{ /* Hack to allow a raw value */
is_int = 1;
len = derlen;
}
else
{
if (!derlen)
return gpg_error (GPG_ERR_INV_KEYINFO);
c = *der++; derlen--;
if ( c != *ctrl )
return gpg_error (GPG_ERR_UNEXPECTED_TAG);
is_int = c == 0x02;
TLV_LENGTH (der);
}
if (is_int && *elem != '-')
{ /* take this integer */
char tmp[2];
put_stringbuf (&sb, "(");
tmp[0] = *elem; tmp[1] = 0;
put_stringbuf_sexp (&sb, tmp);
put_stringbuf_mem_sexp (&sb, der, len);
der += len;
derlen -= len;
put_stringbuf (&sb, ")");
}
}
}
if (mode == 2) /* ECDH */
{
put_stringbuf (&sb, "(1:s");
put_stringbuf_mem_sexp (&sb, encrkey, encrkeylen);
put_stringbuf (&sb, ")");
}
put_stringbuf (&sb, ")");
if (!mode && algo_table[algoidx].digest_string)
{
/* Insert the hash algorithm if included in the OID. */
put_stringbuf (&sb, "(4:hash");
put_stringbuf_sexp (&sb, algo_table[algoidx].digest_string);
put_stringbuf (&sb, ")");
}
if (!mode && pss_hash)
{
put_stringbuf (&sb, "(5:flags3:pss)");
put_stringbuf (&sb, "(9:hash-algo");
put_stringbuf_sexp (&sb, pss_hash);
put_stringbuf (&sb, ")");
put_stringbuf (&sb, "(11:salt-length");
put_stringbuf_uint (&sb, salt_length);
put_stringbuf (&sb, ")");
}
if (mode == 2) /* ECDH */
{
put_stringbuf (&sb, "(9:encr-algo");
put_stringbuf_sexp (&sb, keyencralgo);
put_stringbuf (&sb, ")(9:wrap-algo");
put_stringbuf_sexp (&sb, keywrapalgo);
put_stringbuf (&sb, ")");
}
put_stringbuf (&sb, ")");
*r_string = get_stringbuf (&sb);
if (!*r_string)
return gpg_error (GPG_ERR_ENOMEM);
xfree (pss_hash);
return 0;
}
/* Assume that DER is a buffer of length DERLEN with a DER encoded
Asn.1 structure like this:
SEQUENCE {
algorithm OBJECT IDENTIFIER,
parameters ANY DEFINED BY algorithm OPTIONAL }
signature BIT STRING
We only allow parameters == NULL.
The function parses this structure and creates a S-Exp suitable to be
used as signature value in Libgcrypt:
(sig-val
(
( )
...
( ))
(hash algo))
The S-Exp will be returned in a string which the caller must free.
We don't pass an ASN.1 node here but a plain memory block. */
gpg_error_t
_ksba_sigval_to_sexp (const unsigned char *der, size_t derlen,
ksba_sexp_t *r_string)
{
return cryptval_to_sexp (0, der, derlen, NULL, NULL, NULL, 0, r_string);
}
/* Assume that der is a buffer of length DERLEN with a DER encoded
* ASN.1 structure like this:
*
* SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
* encryptedKey OCTET STRING
*
* The function parses this structure and creates a S-expression
* suitable to be used as encrypted value in Libgcrypt's public key
* functions:
*
* (enc-val
* (
* ( )
* ...
* ( )
* ))
*
* The S-expression will be returned in a string which the caller must
* free. Note that the input buffer may not a proper ASN.1 object but
* a plain memory block; this is becuase the SEQUENCE is followed by
* an OCTET STRING or BIT STRING.
*/
gpg_error_t
_ksba_encval_to_sexp (const unsigned char *der, size_t derlen,
ksba_sexp_t *r_string)
{
return cryptval_to_sexp (1, der, derlen, NULL, NULL, NULL, 0, r_string);
}
/* Assume that der is a buffer of length DERLEN with a DER encoded
* ASN.1 structure like this:
*
* [1] {
* SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
* encryptedKey BIT STRING
* }
*
* The function parses this structure and creates an S-expression
* conveying all parameters required for ECDH:
*
* (enc-val
* (ecdh
* (e )
* (s )
* (ukm )
* (encr-algo )
* (wrap-algo )))
*
* E is the ephemeral public key and S is the encrypted key. The user
* keying material (ukm) is optional. The S-expression will be
* returned in a string which the caller must free.
*/
gpg_error_t
_ksba_encval_kari_to_sexp (const unsigned char *der, size_t derlen,
const char *keyencralgo, const char *keywrapalgo,
const void *enckey, size_t enckeylen,
ksba_sexp_t *r_string)
{
gpg_error_t err;
struct tag_info ti;
size_t save_derlen = derlen;
err = parse_context_tag (&der, &derlen, &ti, 1);
if (err)
return err;
if (save_derlen < ti.nhdr)
return gpg_error (GPG_ERR_INV_BER);
derlen = save_derlen - ti.nhdr;
return cryptval_to_sexp (2, der, derlen,
keyencralgo, keywrapalgo, enckey, enckeylen,
r_string);
}