diff options
Diffstat (limited to 'sm/certreqgen.c')
-rw-r--r-- | sm/certreqgen.c | 240 |
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) { |