summaryrefslogtreecommitdiffstats
path: root/sm/certreqgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'sm/certreqgen.c')
-rw-r--r--sm/certreqgen.c240
1 files changed, 194 insertions, 46 deletions
diff --git a/sm/certreqgen.c b/sm/certreqgen.c
index 92d6ffe..63c35a2 100644
--- a/sm/certreqgen.c
+++ b/sm/certreqgen.c
@@ -67,6 +67,7 @@
#include "keydb.h"
#include "../common/i18n.h"
+#include "../common/membuf.h"
enum para_name
@@ -74,6 +75,7 @@ enum para_name
pKEYTYPE,
pKEYLENGTH,
pKEYGRIP,
+ pKEYCURVE,
pKEYUSAGE,
pNAMEDN,
pNAMEEMAIL,
@@ -236,6 +238,7 @@ read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp)
{ "Key-Type", pKEYTYPE},
{ "Key-Length", pKEYLENGTH },
{ "Key-Grip", pKEYGRIP },
+ { "Key-Curve", pKEYCURVE },
{ "Key-Usage", pKEYUSAGE },
{ "Name-DN", pNAMEDN },
{ "Name-Email", pNAMEEMAIL, 1 },
@@ -433,6 +436,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
struct para_data_s *r;
const char *s, *string;
int i;
+ int algo;
unsigned int nbits;
char numbuf[20];
unsigned char keyparms[100];
@@ -446,30 +450,30 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
/* Check that we have all required parameters; */
assert (get_parameter (para, pKEYTYPE, 0));
- /* We can only use RSA for now. There is a problem with pkcs-10 on
- how to use ElGamal because it is expected that a PK algorithm can
- always be used for signing. Another problem is that on-card
- generated encryption keys may not be used for signing. */
- i = get_parameter_algo (para, pKEYTYPE);
- if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
+ /* There is a problem with pkcs-10 on how to use ElGamal because it
+ is expected that a PK algorithm can always be used for
+ signing. Another problem is that on-card generated encryption
+ keys may not be used for signing. */
+ algo = get_parameter_algo (para, pKEYTYPE);
+ if (!algo && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s)
{
/* Hack to allow creation of certificates directly from a smart
card. For example: "Key-Type: card:OPENPGP.3". */
if (!strncmp (s, "card:", 5) && s[5])
cardkeyid = xtrystrdup (s+5);
}
- if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid )
+ if (algo < 1 && !cardkeyid)
{
r = get_parameter (para, pKEYTYPE, 0);
if (r)
- log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0);
+ log_error (_("line %d: invalid algorithm\n"), r->lnr);
else
log_error ("No Key-Type specified\n");
return gpg_error (GPG_ERR_INV_PARAMETER);
}
/* Check the keylength. NOTE: If you change this make sure that it
- macthes the gpgconflist item in gpgsm.c */
+ matches the gpgconflist item in gpgsm.c */
if (!get_parameter (para, pKEYLENGTH, 0))
nbits = 3072;
else
@@ -626,7 +630,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
/* Check the optional AuthorityKeyId. */
string = get_parameter_value (para, pAUTHKEYID, 0);
- if (string)
+ if (string && strcmp (string, "none"))
{
for (s=string, i=0; hexdigitp (s); s++, i++)
;
@@ -641,7 +645,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
/* Check the optional SubjectKeyId. */
string = get_parameter_value (para, pSUBJKEYID, 0);
- if (string)
+ if (string && strcmp (string, "none"))
{
for (s=string, i=0; hexdigitp (s); s++, i++)
;
@@ -721,10 +725,38 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para,
}
else if (!outctrl->dryrun) /* Generate new key. */
{
- sprintf (numbuf, "%u", nbits);
- snprintf ((char*)keyparms, DIM (keyparms),
- "(6:genkey(3:rsa(5:nbits%d:%s)))",
- (int)strlen (numbuf), numbuf);
+ if (algo == GCRY_PK_RSA)
+ {
+ sprintf (numbuf, "%u", nbits);
+ snprintf ((char*)keyparms, DIM (keyparms),
+ "(6:genkey(3:rsa(5:nbits%d:%s)))",
+ (int)strlen (numbuf), numbuf);
+ }
+ else if ((opt.compat_flags & COMPAT_ALLOW_ECC_ENCR)
+ && (algo == GCRY_PK_ECC || algo == GCRY_PK_EDDSA))
+ {
+ const char *curve = get_parameter_value (para, pKEYCURVE, 0);
+ const char *flags;
+
+ if (algo == GCRY_PK_EDDSA)
+ flags = "(flags eddsa)";
+ else if (!strcmp (curve, "Curve25519"))
+ flags = "(flags djb-tweak)";
+ else
+ flags = "";
+
+ snprintf ((char*)keyparms, DIM (keyparms),
+ "(genkey(ecc(curve %zu:%s)%s))",
+ strlen (curve), curve, flags);
+ }
+ else
+ {
+ r = get_parameter (para, pKEYTYPE, 0);
+ log_error (_("line %d: invalid algorithm\n"), r->lnr);
+ xfree (sigkey);
+ xfree (cardkeyid);
+ return gpg_error (GPG_ERR_INV_PARAMETER);
+ }
rc = gpgsm_agent_genkey (ctrl, keyparms, &public);
if (rc)
{
@@ -805,16 +837,40 @@ create_request (ctrl_t ctrl,
ksba_isotime_t atime;
int certmode = 0;
int mdalgo;
+ membuf_t tbsbuffer;
+ membuf_t *tbsmb = NULL;
+ size_t publiclen;
+ size_t sigkeylen;
+ int publicpkalgo; /* The gcrypt public key algo of the public key. */
+ int sigkeypkalgo; /* The gcrypt public key algo of the signing key. */
err = ksba_certreq_new (&cr);
if (err)
return err;
- string = get_parameter_value (para, pHASHALGO, 0);
- if (string)
- mdalgo = gcry_md_map_name (string);
+ publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL);
+ sigkeylen = sigkey? gcry_sexp_canon_len (sigkey, 0, NULL, NULL) : 0;
+
+ publicpkalgo = get_pk_algo_from_canon_sexp (public, publiclen);
+ sigkeypkalgo = sigkey? get_pk_algo_from_canon_sexp (public, publiclen) : 0;
+
+ if (publicpkalgo == GCRY_PK_EDDSA)
+ {
+ mdalgo = GCRY_MD_SHA512;
+ md = NULL; /* We sign the data and not a hash. */
+ init_membuf (&tbsbuffer, 2048);
+ tbsmb = &tbsbuffer;
+ ksba_certreq_set_hash_function
+ (cr, (void (*)(void *, const void*,size_t))put_membuf, tbsmb);
+ }
else
- mdalgo = GCRY_MD_SHA256;
+ {
+ string = get_parameter_value (para, pHASHALGO, 0);
+ if (string)
+ mdalgo = gcry_md_map_name (string);
+ else
+ mdalgo = GCRY_MD_SHA256;
+ }
rc = gcry_md_open (&md, mdalgo, 0);
if (rc)
{
@@ -912,12 +968,10 @@ create_request (ctrl_t ctrl,
}
}
-
err = ksba_certreq_set_public_key (cr, public);
if (err)
{
- log_error ("error setting the public key: %s\n",
- gpg_strerror (err));
+ log_error ("error setting the public key: %s\n", gpg_strerror (err));
rc = err;
goto leave;
}
@@ -1113,14 +1167,17 @@ create_request (ctrl_t ctrl,
given we set it to the public key to create a self-signed
certificate. */
if (!sigkey)
- sigkey = public;
+ {
+ sigkey = public;
+ sigkeylen = publiclen;
+ sigkeypkalgo = publicpkalgo;
+ }
+ /* Set the the digestinfo aka siginfo. */
{
unsigned char *siginfo;
- err = transform_sigval (sigkey,
- gcry_sexp_canon_len (sigkey, 0, NULL, NULL),
- mdalgo, &siginfo, NULL);
+ err = transform_sigval (sigkey, sigkeylen, mdalgo, &siginfo, NULL);
if (!err)
{
err = ksba_certreq_set_siginfo (cr, siginfo);
@@ -1135,9 +1192,12 @@ create_request (ctrl_t ctrl,
}
}
+
/* Insert the AuthorityKeyId. */
string = get_parameter_value (para, pAUTHKEYID, 0);
- if (string)
+ if (string && !strcmp (string, "none"))
+ ; /* Do not issue an AKI. */
+ else if (string)
{
char *hexbuf;
@@ -1163,20 +1223,69 @@ create_request (ctrl_t ctrl,
hexbuf[2] = 0x80; /* Context tag for an implicit Octet string. */
hexbuf[3] = len;
err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier,
- 0,
- hexbuf, 4+len);
+ 0, hexbuf, 4+len);
xfree (hexbuf);
if (err)
{
- log_error ("error setting the authority-key-id: %s\n",
+ log_error ("error setting the AKI: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ }
+ else if (publicpkalgo == GCRY_PK_EDDSA || publicpkalgo == GCRY_PK_ECC)
+ {
+ /* For EdDSA and ECC we add the public key as default identifier. */
+ const unsigned char *q;
+ size_t qlen, derlen;
+ unsigned char *der;
+
+ err = get_ecc_q_from_canon_sexp (public, publiclen, &q, &qlen);
+ if (err)
+ {
+ log_error ("error getting Q from public key: %s\n",
gpg_strerror (err));
goto leave;
}
+ if (publicpkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40)
+ {
+ /* Skip our optional native encoding octet. */
+ q++;
+ qlen--;
+ }
+ /* FIXME: For plain ECC we should better use a compressed
+ * point. That requires an updated Libgcrypt. Without that
+ * using nistp521 won't work due to the length check below. */
+ if (qlen > 125)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+ derlen = 4 + qlen;
+ der = xtrymalloc (derlen);
+ if (!der)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ der[0] = 0x30; /* Sequence */
+ der[1] = qlen + 2;
+ der[2] = 0x80; /* Context tag for an implict Octet String. */
+ der[3] = qlen;
+ memcpy (der+4, q, qlen);
+ err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier,
+ 0, der, derlen);
+ xfree (der);
+ if (err)
+ {
+ log_error ("error setting the AKI: %s\n", gpg_strerror (err));
+ goto leave;
+ }
}
/* Insert the SubjectKeyId. */
string = get_parameter_value (para, pSUBJKEYID, 0);
- if (string)
+ if (string && !strcmp (string, "none"))
+ ; /* Do not issue an SKI. */
+ else if (string)
{
char *hexbuf;
@@ -1204,10 +1313,53 @@ create_request (ctrl_t ctrl,
xfree (hexbuf);
if (err)
{
- log_error ("error setting the subject-key-id: %s\n",
+ log_error ("error setting SKI: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ }
+ else if (sigkeypkalgo == GCRY_PK_EDDSA || sigkeypkalgo == GCRY_PK_ECC)
+ {
+ /* For EdDSA and ECC we add the public key as default identifier. */
+ const unsigned char *q;
+ size_t qlen, derlen;
+ unsigned char *der;
+
+ err = get_ecc_q_from_canon_sexp (sigkey, sigkeylen, &q, &qlen);
+ if (err)
+ {
+ log_error ("error getting Q from signature key: %s\n",
gpg_strerror (err));
goto leave;
}
+ if (sigkeypkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40)
+ {
+ /* Skip our optional native encoding octet. */
+ q++;
+ qlen--;
+ }
+ if (qlen > 127)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ goto leave;
+ }
+ derlen = 2 + qlen;
+ der = xtrymalloc (derlen);
+ if (!der)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ der[0] = 0x04; /* Octet String */
+ der[1] = qlen;
+ memcpy (der+2, q, qlen);
+ err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0,
+ der, derlen);
+ xfree (der);
+ if (err)
+ {
+ log_error ("error setting the SKI: %s\n", gpg_strerror (err));
+ goto leave;
+ }
}
/* Insert additional extensions. */
@@ -1269,7 +1421,11 @@ create_request (ctrl_t ctrl,
}
}
else
- sigkey = public;
+ {
+ sigkey = public;
+ sigkeylen = publiclen;
+ sigkeypkalgo = publicpkalgo;
+ }
do
{
@@ -1283,20 +1439,12 @@ create_request (ctrl_t ctrl,
if (stopreason == KSBA_SR_NEED_SIG)
{
gcry_sexp_t s_pkey;
- size_t n;
unsigned char grip[20];
char hexgrip[41];
unsigned char *sigval, *newsigval;
size_t siglen;
- n = gcry_sexp_canon_len (sigkey, 0, NULL, NULL);
- if (!n)
- {
- log_error ("libksba did not return a proper S-Exp\n");
- rc = gpg_error (GPG_ERR_BUG);
- goto leave;
- }
- rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, n);
+ rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, sigkeylen);
if (rc)
{
log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
@@ -1312,8 +1460,9 @@ create_request (ctrl_t ctrl,
gcry_sexp_release (s_pkey);
bin2hex (grip, 20, hexgrip);
- log_info ("about to sign the %s for key: &%s\n",
- certmode? "certificate":"CSR", hexgrip);
+ if (!opt.quiet)
+ log_info ("about to sign the %s for key: &%s\n",
+ certmode? "certificate":"CSR", hexgrip);
if (carddirect && !certmode)
rc = gpgsm_scd_pksign (ctrl, carddirect, NULL,
@@ -1345,8 +1494,7 @@ create_request (ctrl_t ctrl,
goto leave;
}
- err = transform_sigval (sigval, siglen, mdalgo,
- &newsigval, NULL);
+ err = transform_sigval (sigval, siglen, mdalgo, &newsigval, NULL);
xfree (sigval);
if (!err)
{