aboutsummaryrefslogtreecommitdiffstats
path: root/sm
diff options
context:
space:
mode:
Diffstat (limited to 'sm')
-rw-r--r--sm/certcheck.c31
-rw-r--r--sm/certlist.c45
-rw-r--r--sm/certreqgen-ui.c4
-rw-r--r--sm/certreqgen.c233
-rw-r--r--sm/decrypt.c376
-rw-r--r--sm/encrypt.c321
-rw-r--r--sm/fingerprint.c42
-rw-r--r--sm/gpgsm.c6
-rw-r--r--sm/gpgsm.h9
-rw-r--r--sm/keylist.c31
-rw-r--r--sm/misc.c109
-rw-r--r--sm/sign.c102
-rw-r--r--sm/verify.c4
13 files changed, 1137 insertions, 176 deletions
diff --git a/sm/certcheck.c b/sm/certcheck.c
index d6b967c8a..534f47c1b 100644
--- a/sm/certcheck.c
+++ b/sm/certcheck.c
@@ -74,14 +74,17 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
size_t nframe;
unsigned char *frame;
- if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA)
+ if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECC)
{
- unsigned int qbits;
+ unsigned int qbits0, qbits;
- if ( pkalgo == GCRY_PK_ECDSA )
- qbits = gcry_pk_get_nbits (pkey);
+ if ( pkalgo == GCRY_PK_ECC )
+ {
+ qbits0 = gcry_pk_get_nbits (pkey);
+ qbits = qbits0 == 521? 512 : qbits0;
+ }
else
- qbits = get_dsa_qbits (pkey);
+ qbits0 = qbits = get_dsa_qbits (pkey);
if ( (qbits%8) )
{
@@ -98,7 +101,7 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
if (qbits < 160)
{
log_error (_("%s key uses an unsafe (%u bit) hash\n"),
- gcry_pk_algo_name (pkalgo), qbits);
+ gcry_pk_algo_name (pkalgo), qbits0);
return gpg_error (GPG_ERR_INTERNAL);
}
@@ -109,7 +112,7 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits,
{
log_error (_("a %u bit hash is not valid for a %u bit %s key\n"),
(unsigned int)nframe*8,
- gcry_pk_get_nbits (pkey),
+ qbits0,
gcry_pk_algo_name (pkalgo));
/* FIXME: we need to check the requirements for ECDSA. */
if (nframe < 20 || pkalgo == GCRY_PK_DSA )
@@ -210,10 +213,8 @@ pk_algo_from_sexp (gcry_sexp_t pkey)
algo = GCRY_PK_RSA;
else if (n==3 && !memcmp (name, "dsa", 3))
algo = GCRY_PK_DSA;
- /* Because this function is called only for verification we can
- assume that ECC actually means ECDSA. */
else if (n==3 && !memcmp (name, "ecc", 3))
- algo = GCRY_PK_ECDSA;
+ algo = GCRY_PK_ECC;
else if (n==13 && !memcmp (name, "ambiguous-rsa", 13))
algo = GCRY_PK_RSA;
else
@@ -357,9 +358,19 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
int use_pss = 0;
unsigned int saltlen;
+ /* Note that we map the 4 algos which current Libgcrypt versions are
+ * not aware of the OID. */
algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert)));
if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10"))
use_pss = 1;
+ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.1"))
+ algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */
+ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.2"))
+ algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */
+ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.3"))
+ algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */
+ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.4"))
+ algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */
else if (!algo)
{
log_error ("unknown digest algorithm '%s' used certificate\n",
diff --git a/sm/certlist.c b/sm/certlist.c
index b1ae58c52..94f85d2b2 100644
--- a/sm/certlist.c
+++ b/sm/certlist.c
@@ -34,7 +34,16 @@
#include "keydb.h"
#include "../common/i18n.h"
-
+/* Mode values for cert_usage_p.
+ * Take care: the values have a semantic. */
+#define USE_MODE_SIGN 0
+#define USE_MODE_ENCR 1
+#define USE_MODE_VRFY 2
+#define USE_MODE_DECR 3
+#define USE_MODE_CERT 4
+#define USE_MODE_OCSP 5
+
+/* OIDs we use here. */
static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1";
static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2";
static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3";
@@ -42,6 +51,7 @@ static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4";
static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8";
static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9";
+
/* Return 0 if the cert is usable for encryption. A MODE of 0 checks
for signing a MODE of 1 checks for encryption, a MODE of 2 checks
for verification and a MODE of 3 for decryption (just for
@@ -120,7 +130,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
{
err = 0;
- if (opt.verbose && mode < 2 && !silent)
+ if (opt.verbose && mode < USE_MODE_VRFY && !silent)
log_info (_("no key usage specified - assuming all usages\n"));
use = ~0;
}
@@ -137,7 +147,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
return err;
}
- if (mode == 4)
+ if (mode == USE_MODE_CERT)
{
if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN)))
return 0;
@@ -147,7 +157,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
- if (mode == 5)
+ if (mode == USE_MODE_OCSP)
{
if (use != ~0
&& (have_ocsp_signing
@@ -161,7 +171,8 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
}
encr_bits = (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT);
- if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR))
+ if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)
+ || gpgsm_is_ecc_key (cert))
encr_bits |= KSBA_KEYUSAGE_KEY_AGREEMENT;
sign_bits = (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION);
@@ -170,11 +181,13 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
return 0;
if (!silent)
- log_info
- (mode==3? _("certificate should not have been used for encryption\n"):
- mode==2? _("certificate should not have been used for signing\n"):
- mode==1? _("certificate is not usable for encryption\n"):
- /**/ _("certificate is not usable for signing\n"));
+ log_info (mode == USE_MODE_DECR?
+ _("certificate should not have been used for encryption\n") :
+ mode == USE_MODE_VRFY?
+ _("certificate should not have been used for signing\n") :
+ mode == USE_MODE_ENCR?
+ _("certificate is not usable for encryption\n") :
+ _("certificate is not usable for signing\n"));
return gpg_error (GPG_ERR_WRONG_KEY_USAGE);
}
@@ -184,7 +197,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent)
int
gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent)
{
- return cert_usage_p (cert, 0, silent);
+ return cert_usage_p (cert, USE_MODE_SIGN, silent);
}
@@ -192,31 +205,31 @@ gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent)
int
gpgsm_cert_use_encrypt_p (ksba_cert_t cert)
{
- return cert_usage_p (cert, 1, 0);
+ return cert_usage_p (cert, USE_MODE_ENCR, 0);
}
int
gpgsm_cert_use_verify_p (ksba_cert_t cert)
{
- return cert_usage_p (cert, 2, 0);
+ return cert_usage_p (cert, USE_MODE_VRFY, 0);
}
int
gpgsm_cert_use_decrypt_p (ksba_cert_t cert)
{
- return cert_usage_p (cert, 3, 0);
+ return cert_usage_p (cert, USE_MODE_DECR, 0);
}
int
gpgsm_cert_use_cert_p (ksba_cert_t cert)
{
- return cert_usage_p (cert, 4, 0);
+ return cert_usage_p (cert, USE_MODE_CERT, 0);
}
int
gpgsm_cert_use_ocsp_p (ksba_cert_t cert)
{
- return cert_usage_p (cert, 5, 0);
+ return cert_usage_p (cert, USE_MODE_OCSP, 0);
}
diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c
index d75b017c7..fbfaa8a8f 100644
--- a/sm/certreqgen-ui.c
+++ b/sm/certreqgen-ui.c
@@ -113,7 +113,9 @@ check_keygrip (ctrl_t ctrl, const char *hexgrip)
case GCRY_PK_RSA: return "RSA";
case GCRY_PK_DSA: return "DSA";
case GCRY_PK_ELG: return "ELG";
- case GCRY_PK_EDDSA: return "ECDSA";
+ case GCRY_PK_ECC: return "ECC";
+ case GCRY_PK_ECDSA: return "ECDSA";
+ case GCRY_PK_EDDSA: return "EdDSA";
default: return NULL;
}
}
diff --git a/sm/certreqgen.c b/sm/certreqgen.c
index 92d6ffe05..73ca7d2ea 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,37 @@ 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 (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 +836,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 +967,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 +1166,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 +1191,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 +1222,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 +1312,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. */
@@ -1283,20 +1434,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 +1455,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 +1489,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)
{
diff --git a/sm/decrypt.c b/sm/decrypt.c
index 2aa716f5b..01260f599 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1,7 +1,7 @@
/* decrypt.c - Decrypt a message
* Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc.
* Copyright (C) 2001-2019 Werner Koch
- * Copyright (C) 2015-2021 g10 Code GmbH
+ * Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -82,6 +82,335 @@ string_from_gcry_buffer (gcry_buffer_t *buffer)
}
+/* Helper to construct and hash the
+ * ECC-CMS-SharedInfo ::= SEQUENCE {
+ * keyInfo AlgorithmIdentifier,
+ * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
+ * suppPubInfo [2] EXPLICIT OCTET STRING }
+ * as described in RFC-5753, 7.2. */
+gpg_error_t
+hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str,
+ unsigned int keylen,
+ const void *ukm, unsigned int ukmlen)
+{
+ gpg_error_t err;
+ void *p;
+ unsigned char *oid;
+ size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo;
+ unsigned char keylenbuf[6];
+ membuf_t mb = MEMBUF_ZERO;
+
+ err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen);
+ if (err)
+ return err;
+ toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
+ tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen);
+
+ tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0;
+
+ keylen *= 8;
+ keylenbuf[0] = TAG_OCTET_STRING;
+ keylenbuf[1] = 4;
+ keylenbuf[2] = (keylen >> 24);
+ keylenbuf[3] = (keylen >> 16);
+ keylenbuf[4] = (keylen >> 8);
+ keylenbuf[5] = keylen;
+
+ tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
+
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
+ tkeyinfo + tukmlen + tsupppubinfo);
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1,
+ toidlen);
+ put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen);
+ put_membuf (&mb, oid, oidlen);
+ ksba_free (oid);
+
+ if (ukm)
+ {
+ put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen);
+ put_membuf (&mb, ukm, ukmlen);
+ }
+
+ put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf);
+ put_membuf (&mb, keylenbuf, sizeof keylenbuf);
+
+ p = get_membuf (&mb, &n);
+ if (!p)
+ return gpg_error_from_syserror ();
+
+ gcry_md_write (hash_hd, p, n);
+ xfree (p);
+ return 0;
+}
+
+
+
+/* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an
+ * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal
+ * dotted form, and the hash algorithm HASH_ALGO. On success a key of
+ * length KEYLEN is stored at KEY. */
+gpg_error_t
+ecdh_derive_kek (unsigned char *key, unsigned int keylen,
+ int hash_algo, const char *wrap_algo_str,
+ const void *secret, unsigned int secretlen,
+ const void *ukm, unsigned int ukmlen)
+{
+ gpg_error_t err = 0;
+ unsigned int hashlen;
+ gcry_md_hd_t hash_hd;
+ unsigned char counter;
+ unsigned int n, ncopy;
+
+ hashlen = gcry_md_get_algo_dlen (hash_algo);
+ if (!hashlen)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ err = gcry_md_open (&hash_hd, hash_algo, 0);
+ if (err)
+ return err;
+
+ /* According to SEC1 3.6.1 we should check that
+ * SECRETLEN + UKMLEN + 4 < maxhashlen
+ * However, we have no practical limit on the hash length and thus
+ * there is no point in checking this. The second check that
+ * KEYLEN < hashlen*(2^32-1)
+ * is obviously also not needed.
+ */
+ for (n=0, counter=1; n < keylen; counter++)
+ {
+ if (counter > 1)
+ gcry_md_reset (hash_hd);
+ gcry_md_write (hash_hd, secret, secretlen);
+ gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */
+ gcry_md_write (hash_hd, &counter, 1);
+ err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen,
+ ukm, ukmlen);
+ if (err)
+ break;
+ gcry_md_final (hash_hd);
+ if (n + hashlen > keylen)
+ ncopy = keylen - n;
+ else
+ ncopy = hashlen;
+ memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy);
+ n += ncopy;
+ }
+
+ gcry_md_close (hash_hd);
+ return err;
+}
+
+
+/* This function will modify SECRET. NBITS is the size of the curve
+ * which which we took from the certificate. */
+static gpg_error_t
+ecdh_decrypt (unsigned char *secret, size_t secretlen,
+ unsigned int nbits, gcry_sexp_t enc_val,
+ unsigned char **r_result, unsigned int *r_resultlen)
+{
+ gpg_error_t err;
+ gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} };
+ char *encr_algo_str = NULL;
+ char *wrap_algo_str = NULL;
+ int hash_algo, cipher_algo;
+ const unsigned char *ukm; /* Alias for ioarray[2]. */
+ unsigned int ukmlen;
+ const unsigned char *data; /* Alias for ioarray[3]. */
+ unsigned int datalen;
+ unsigned int keylen;
+ unsigned char key[32];
+ gcry_cipher_hd_t cipher_hd = NULL;
+ unsigned char *result = NULL;
+ unsigned int resultlen;
+
+ *r_resultlen = 0;
+ *r_result = NULL;
+
+ /* Extract X from SECRET; this is the actual secret. Unless a
+ * smartcard diretcly returns X, it must be in the format of:
+ *
+ * 04 || X || Y
+ * 40 || X
+ * 41 || X
+ */
+ if (secretlen < 2)
+ return gpg_error (GPG_ERR_BAD_DATA);
+ if (secretlen == (nbits+7)/8)
+ ; /* Matches curve length - this is already the X coordinate. */
+ else if (*secret == 0x04)
+ {
+ secretlen--;
+ memmove (secret, secret+1, secretlen);
+ if ((secretlen & 1))
+ return gpg_error (GPG_ERR_BAD_DATA);
+ secretlen /= 2;
+ }
+ else if (*secret == 0x40 || *secret == 0x41)
+ {
+ secretlen--;
+ memmove (secret, secret+1, secretlen);
+ }
+ else
+ return gpg_error (GPG_ERR_BAD_DATA);
+ if (!secretlen)
+ return gpg_error (GPG_ERR_BAD_DATA);
+
+ if (DBG_CRYPTO)
+ log_printhex (secret, secretlen, "ECDH X ..:");
+
+ /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now
+ * we will compute the KEK using a value dervied from the secret
+ * bytes. */
+ err = gcry_sexp_extract_param (enc_val, "enc-val",
+ "&'encr-algo''wrap-algo''ukm'?s",
+ ioarray+0, ioarray+1,
+ ioarray+2, ioarray+3, NULL);
+ if (err)
+ {
+ log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ encr_algo_str = string_from_gcry_buffer (ioarray);
+ if (!encr_algo_str)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ wrap_algo_str = string_from_gcry_buffer (ioarray+1);
+ if (!wrap_algo_str)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ ukm = ioarray[2].data;
+ ukmlen = ioarray[2].len;
+ data = ioarray[3].data;
+ datalen = ioarray[3].len;
+
+ /* Check parameters. */
+ if (DBG_CRYPTO)
+ {
+ log_debug ("encr_algo: %s\n", encr_algo_str);
+ log_debug ("wrap_algo: %s\n", wrap_algo_str);
+ log_printhex (ukm, ukmlen, "ukm .....:");
+ log_printhex (data, datalen, "data ....:");
+ }
+
+ if (!strcmp (encr_algo_str, "1.3.132.1.11.1"))
+ {
+ /* dhSinglePass-stdDH-sha256kdf-scheme */
+ hash_algo = GCRY_MD_SHA256;
+ }
+ else if (!strcmp (encr_algo_str, "1.3.132.1.11.2"))
+ {
+ /* dhSinglePass-stdDH-sha384kdf-scheme */
+ hash_algo = GCRY_MD_SHA384;
+ }
+ else if (!strcmp (encr_algo_str, "1.3.132.1.11.3"))
+ {
+ /* dhSinglePass-stdDH-sha512kdf-scheme */
+ hash_algo = GCRY_MD_SHA512;
+ }
+ else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2"))
+ {
+ /* dhSinglePass-stdDH-sha1kdf-scheme */
+ hash_algo = GCRY_MD_SHA1;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5"))
+ {
+ cipher_algo = GCRY_CIPHER_AES128;
+ keylen = 16;
+ }
+ else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25"))
+ {
+ cipher_algo = GCRY_CIPHER_AES192;
+ keylen = 24;
+ }
+ else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45"))
+ {
+ cipher_algo = GCRY_CIPHER_AES256;
+ keylen = 32;
+ }
+ else
+ {
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
+
+ err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str,
+ secret, secretlen, ukm, ukmlen);
+ if (err)
+ goto leave;
+
+ if (DBG_CRYPTO)
+ log_printhex (key, keylen, "KEK .....:");
+
+ /* Unwrap the key. */
+ if ((datalen % 8) || datalen < 16)
+ {
+ log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen);
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ goto leave;
+ }
+
+ resultlen = datalen - 8;
+ result = xtrymalloc_secure (resultlen);
+ if (!result)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
+ if (err)
+ {
+ log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_cipher_setkey (cipher_hd, key, keylen);
+ wipememory (key, sizeof key);
+ if (err)
+ {
+ log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen);
+ if (err)
+ {
+ log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err));
+ goto leave;
+ }
+
+ *r_resultlen = resultlen;
+ *r_result = result;
+ result = NULL;
+
+ leave:
+ if (result)
+ {
+ wipememory (result, resultlen);
+ xfree (result);
+ }
+ gcry_cipher_close (cipher_hd);
+ xfree (encr_algo_str);
+ xfree (wrap_algo_str);
+ xfree (ioarray[0].data);
+ xfree (ioarray[1].data);
+ xfree (ioarray[2].data);
+ xfree (ioarray[3].data);
+ return err;
+}
+
+
/* Helper for pwri_decrypt to parse the derive info.
* Example data for (DER,DERLEN):
* SEQUENCE {
@@ -447,13 +776,14 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
/* Decrypt the session key and fill in the parm structure. The
algo and the IV is expected to be already in PARM. */
static int
-prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
+prepare_decryption (ctrl_t ctrl, const char *hexkeygrip,
+ int pk_algo, unsigned int nbits, const char *desc,
ksba_const_sexp_t enc_val,
struct decrypt_filter_parm_s *parm)
{
char *seskey = NULL;
size_t n, seskeylen;
- int pwri = !hexkeygrip;
+ int pwri = !hexkeygrip && !pk_algo;
int rc;
if (DBG_CRYPTO)
@@ -468,6 +798,9 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
goto leave;
}
+
+ if (DBG_CRYPTO)
+ log_printhex (seskey, seskeylen, "DEK frame:");
}
n=0;
@@ -490,6 +823,27 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
seskey = decrypted;
seskeylen = decryptedlen;
}
+ else if (pk_algo == GCRY_PK_ECC)
+ {
+ gcry_sexp_t s_enc_val;
+ unsigned char *decrypted;
+ unsigned int decryptedlen;
+
+ rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val,
+ gcry_sexp_canon_len (enc_val, 0, NULL, NULL));
+ if (rc)
+ goto leave;
+
+ rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val,
+ &decrypted, &decryptedlen);
+ gcry_sexp_release (s_enc_val);
+ if (rc)
+ goto leave;
+ xfree (seskey);
+ seskey = decrypted;
+ seskeylen = decryptedlen;
+
+ }
else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16)
{
/* Smells like an AES-128, 3-DES, or AES-256 key. This might
@@ -530,7 +884,10 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
}
if (DBG_CRYPTO)
- log_printhex (seskey+n, seskeylen-n, "session key:");
+ {
+ log_printhex (seskey+n, seskeylen-n, "CEK .......:");
+ log_printhex (parm->iv, parm->ivlen, "IV ........:");
+ }
if (opt.verbose)
log_info (_("%s.%s encrypted data\n"),
@@ -839,7 +1196,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
dfparm.mode = mode;
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
- return gpg_error (GPG_ERR_BUG);
+ {
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
@@ -951,6 +1311,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
hexkeygrip = gpgsm_get_keygrip_hexstring (cert);
desc = gpgsm_format_keydesc (cert);
+
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
pk_algo = gpgsm_get_key_algo_info (cert, &nbits);
@@ -1006,7 +1367,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
if (maybe_pwri && opt.verbose)
log_info ("recp %d - KEKRI or PWRI\n", recp);
- rc = prepare_decryption (ctrl, hexkeygrip,
+ rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits,
desc, enc_val, &dfparm);
xfree (enc_val);
if (rc)
@@ -1076,7 +1437,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
if (!any_key)
{
- rc = gpg_error (GPG_ERR_NO_SECKEY);
+ if (!rc)
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
}
diff --git a/sm/encrypt.c b/sm/encrypt.c
index 587b568c6..fb36977af 100644
--- a/sm/encrypt.c
+++ b/sm/encrypt.c
@@ -1,6 +1,8 @@
/* encrypt.c - Encrypt a message
* Copyright (C) 2001, 2003, 2004, 2007, 2008,
* 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2019 Werner Koch
+ * Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -16,6 +18,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
@@ -144,7 +147,7 @@ init_dek (DEK dek)
return 0;
}
-
+/* Encrypt an RSA session key. */
static int
encode_session_key (DEK dek, gcry_sexp_t * r_data)
{
@@ -165,10 +168,282 @@ encode_session_key (DEK dek, gcry_sexp_t * r_data)
}
+/* Encrypt DEK using ECDH. S_PKEY is the public key. On success the
+ * result is stored at R_ENCVAL. Example of a public key:
+ *
+ * (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#)))
+ *
+ */
+static gpg_error_t
+ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval)
+{
+ gpg_error_t err;
+ gcry_sexp_t l1;
+ char *curvebuf = NULL;
+ const char *curve;
+ unsigned int curvebits;
+ const char *encr_algo_str;
+ const char *wrap_algo_str;
+ int hash_algo, cipher_algo;
+ unsigned int keylen, hashlen;
+ unsigned char key[32];
+ gcry_sexp_t s_data = NULL;
+ gcry_sexp_t s_encr = NULL;
+ gcry_buffer_t ioarray[2] = { {0}, {0} };
+ unsigned char *secret; /* Alias for ioarray[0]. */
+ unsigned int secretlen;
+ unsigned char *pubkey; /* Alias for ioarray[1]. */
+ unsigned int pubkeylen;
+ gcry_cipher_hd_t cipher_hd = NULL;
+ unsigned char *result = NULL;
+ unsigned int resultlen;
+
+ *r_encval = NULL;
+
+ /* Figure out the encryption and wrap algo OIDs. */
+ /* Get the curve name if any, */
+ l1 = gcry_sexp_find_token (s_pkey, "curve", 0);
+ if (l1)
+ {
+ curvebuf = gcry_sexp_nth_string (l1, 1);
+ gcry_sexp_release (l1);
+ }
+ if (!curvebuf)
+ {
+ err = gpg_error (GPG_ERR_INV_CURVE);
+ log_error ("%s: invalid public key: no curve\n", __func__);
+ goto leave;
+ }
+
+ /* We need to use our OpenPGP mapping to turn a curve name into its
+ * canonical numerical OID. We also use this to get the size of the
+ * curve which we need to figure out a suitable hash algo. We
+ * should have a Libgcrypt function to do this; see bug report #4926. */
+ curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL);
+ if (!curve)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err));
+ goto leave;
+ }
+ xfree (curvebuf);
+ curvebuf = NULL;
+
+ /* Our mapping matches the recommended algorithms from RFC-5753 but
+ * not supporing the short curves which would require 3DES. */
+ if (curvebits < 255)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ log_error ("%s: curve '%s' is not supported\n", __func__, curve);
+ goto leave;
+ }
+ else if (curvebits <= 256)
+ {
+ /* dhSinglePass-stdDH-sha256kdf-scheme */
+ encr_algo_str = "1.3.132.1.11.1";
+ wrap_algo_str = "2.16.840.1.101.3.4.1.5";
+ hash_algo = GCRY_MD_SHA256;
+ hashlen = 32;
+ cipher_algo = GCRY_CIPHER_AES128;
+ keylen = 16;
+ }
+ else if (curvebits <= 384)
+ {
+ /* dhSinglePass-stdDH-sha384kdf-scheme */
+ encr_algo_str = "1.3.132.1.11.2";
+ wrap_algo_str = "2.16.840.1.101.3.4.1.25";
+ hash_algo = GCRY_MD_SHA384;
+ hashlen = 48;
+ cipher_algo = GCRY_CIPHER_AES256;
+ keylen = 24;
+ }
+ else
+ {
+ /* dhSinglePass-stdDH-sha512kdf-scheme*/
+ encr_algo_str = "1.3.132.1.11.3";
+ wrap_algo_str = "2.16.840.1.101.3.4.1.45";
+ hash_algo = GCRY_MD_SHA512;
+ hashlen = 64;
+ cipher_algo = GCRY_CIPHER_AES256;
+ keylen = 32;
+ }
+
+
+ /* Create a secret and an ephemeral key. */
+ {
+ char *k;
+ k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM);
+ if (DBG_CRYPTO)
+ log_printhex (k, (curvebits+7)/8, "ephm. k .:");
+ err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k);
+ xfree (k);
+ }
+ if (err)
+ {
+ log_error ("%s: error building ephemeral secret: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_pk_encrypt (&s_encr, s_data, s_pkey);
+ if (err)
+ {
+ log_error ("%s: error encrypting ephemeral secret: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+ err = gcry_sexp_extract_param (s_encr, NULL, "&se",
+ &ioarray+0, ioarray+1, NULL);
+ if (err)
+ {
+ log_error ("%s: error extracting ephemeral key and secret: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+ secret = ioarray[0].data;
+ secretlen = ioarray[0].len;
+ pubkey = ioarray[1].data;
+ pubkeylen = ioarray[1].len;
+
+ if (DBG_CRYPTO)
+ {
+ log_printhex (pubkey, pubkeylen, "pubkey ..:");
+ log_printhex (secret, secretlen, "secret ..:");
+ }
+
+ /* Extract X coordinate from SECRET. */
+ if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ else if (*secret == 0x04)
+ {
+ secretlen--;
+ memmove (secret, secret+1, secretlen);
+ if ((secretlen & 1))
+ {
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ goto leave;
+ }
+ secretlen /= 2;
+ }
+ else if (*secret == 0x40 || *secret == 0x41)
+ {
+ secretlen--;
+ memmove (secret, secret+1, secretlen);
+ }
+ else
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ if (err)
+ goto leave;
+
+ if (DBG_CRYPTO)
+ log_printhex (secret, secretlen, "ECDH X ..:");
+
+ /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X.
+ * According to SEC1 3.6.1 we should check that
+ * SECRETLEN + UKMLEN + 4 < maxhashlen
+ * However, we have no practical limit on the hash length and thus
+ * there is no point in checking this. The second check that
+ * KEYLEN < hashlen*(2^32-1)
+ * is obviously also not needed. Because with our allowed
+ * parameters KEYLEN is always less or equal to HASHLEN so that we
+ * do not need to iterate at all.
+ */
+ log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen);
+ {
+ gcry_md_hd_t hash_hd;
+ err = gcry_md_open (&hash_hd, hash_algo, 0);
+ if (err)
+ goto leave;
+ gcry_md_write(hash_hd, secret, secretlen);
+ gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */
+ err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, NULL, 0);
+ gcry_md_final (hash_hd);
+ log_assert (keylen <= sizeof key && keylen <= hashlen);
+ memcpy (key, gcry_md_read (hash_hd, 0), keylen);
+ gcry_md_close (hash_hd);
+ if (err)
+ goto leave;
+ }
+
+ if (DBG_CRYPTO)
+ log_printhex (key, keylen, "KEK .....:");
+
+ /* Wrap the key. */
+ if ((dek->keylen % 8) || dek->keylen < 16)
+ {
+ log_error ("%s: can't use a session key of %u bytes\n",
+ __func__, dek->keylen);
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ goto leave;
+ }
+
+ resultlen = dek->keylen + 8;
+ result = xtrymalloc_secure (resultlen);
+ if (!result)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
+ if (err)
+ {
+ log_error ("%s: failed to initialize AESWRAP: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_cipher_setkey (cipher_hd, key, keylen);
+ wipememory (key, sizeof key);
+ if (err)
+ {
+ log_error ("%s: failed in gcry_cipher_setkey: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gcry_cipher_encrypt (cipher_hd, result, resultlen,
+ dek->key, dek->keylen);
+ if (err)
+ {
+ log_error ("%s: failed in gcry_cipher_encrypt: %s\n",
+ __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ if (DBG_CRYPTO)
+ log_printhex (result, resultlen, "w(CEK) ..:");
+
+ err = gcry_sexp_build (r_encval, NULL,
+ "(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))",
+ (int)pubkeylen, pubkey,
+ (int)resultlen, result,
+ encr_algo_str,
+ wrap_algo_str,
+ NULL);
+ if (err)
+ log_error ("%s: failed building final S-exp: %s\n",
+ __func__, gpg_strerror (err));
+
+ leave:
+ gcry_cipher_close (cipher_hd);
+ wipememory (key, sizeof key);
+ xfree (result);
+ xfree (ioarray[0].data);
+ xfree (ioarray[1].data);
+ gcry_sexp_release (s_data);
+ gcry_sexp_release (s_encr);
+ xfree (curvebuf);
+ return err;
+}
+
+
/* Encrypt the DEK under the key contained in CERT and return it as a
- canonical S-Exp in encval. */
+ * canonical S-expressions at ENCVAL. PK_ALGO is the public key
+ * algorithm which the caller has already retrieved from CERT. */
static int
-encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval)
+encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo,
+ unsigned char **encval)
{
gcry_sexp_t s_ciph, s_data, s_pkey;
int rc;
@@ -198,21 +473,41 @@ encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval)
return rc;
}
+ if (DBG_CRYPTO)
+ {
+ log_printsexp (" pubkey:", s_pkey);
+ log_printhex (dek->key, dek->keylen, "CEK .....:");
+ }
+
/* Put the encoded cleartext into a simple list. */
s_data = NULL; /* (avoid compiler warning) */
- rc = encode_session_key (dek, &s_data);
- if (rc)
+ if (pk_algo == GCRY_PK_ECC)
{
- gcry_sexp_release (s_pkey);
- log_error ("encode_session_key failed: %s\n", gpg_strerror (rc));
- return rc;
+ if (!(opt.compat_flags & COMPAT_ALLOW_ECC_ENCR))
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ rc = ecdh_encrypt (dek, s_pkey, &s_ciph);
}
+ else
+ {
+ rc = encode_session_key (dek, &s_data);
+ if (rc)
+ {
+ log_error ("encode_session_key failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+ if (DBG_CRYPTO)
+ log_printsexp (" data:", s_data);
- /* pass it to libgcrypt */
- rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
+ /* pass it to libgcrypt */
+ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
+ }
gcry_sexp_release (s_data);
gcry_sexp_release (s_pkey);
+ if (DBG_CRYPTO)
+ log_printsexp ("enc-val:", s_ciph);
+
/* Reformat it. */
if (!rc)
{
@@ -387,7 +682,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp)
err = ksba_cms_set_reader_writer (cms, reader, writer);
if (err)
{
- log_debug ("ksba_cms_set_reader_writer failed: %s\n",
+ log_error ("ksba_cms_set_reader_writer failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
@@ -402,7 +697,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp)
err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA);
if (err)
{
- log_debug ("ksba_cms_set_content_type failed: %s\n",
+ log_error ("ksba_cms_set_content_type failed: %s\n",
gpg_strerror (err));
rc = err;
goto leave;
@@ -500,7 +795,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp)
&& !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL))
compliant = 0;
- rc = encrypt_dek (dek, cl->cert, &encval);
+ rc = encrypt_dek (dek, cl->cert, pk_algo, &encval);
if (rc)
{
audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc);
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
index 2e01cf1c0..ce0830e29 100644
--- a/sm/fingerprint.c
+++ b/sm/fingerprint.c
@@ -219,20 +219,25 @@ gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
/* Return the PK algorithm used by CERT as well as the length in bits
- of the public key at NBITS. */
+ * of the public key at NBITS. If R_CURVE is not NULL and an ECC
+ * algorithm is used the name or OID of the curve is stored there; the
+ * caller needs to free this value. */
int
-gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
+gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve)
{
gcry_sexp_t s_pkey;
int rc;
ksba_sexp_t p;
size_t n;
gcry_sexp_t l1, l2;
+ const char *curve;
const char *name;
char namebuf[128];
if (nbits)
*nbits = 0;
+ if (r_curve)
+ *r_curve = NULL;
p = ksba_cert_get_public_key (cert);
if (!p)
@@ -258,6 +263,24 @@ gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
gcry_sexp_release (s_pkey);
return 0;
}
+
+ if (r_curve)
+ {
+ curve = gcry_pk_get_curve (l1, 0, NULL);
+ if (curve)
+ {
+ name = openpgp_oid_to_curve (openpgp_curve_to_oid (curve,
+ NULL, NULL), 0);
+ *r_curve = xtrystrdup (name? name : curve);
+ if (!*r_curve)
+ {
+ gcry_sexp_release (l1);
+ gcry_sexp_release (s_pkey);
+ return 0; /* Out of core. */
+ }
+ }
+ }
+
l2 = gcry_sexp_cadr (l1);
gcry_sexp_release (l1);
l1 = l2;
@@ -277,6 +300,21 @@ gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
}
+int
+gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
+{
+ return gpgsm_get_key_algo_info2 (cert, nbits, NULL);
+}
+
+
+/* Return true if CERT is an ECC key. */
+int
+gpgsm_is_ecc_key (ksba_cert_t cert)
+{
+ return GCRY_PK_ECC == gpgsm_get_key_algo_info2 (cert, NULL, NULL);
+}
+
+
/* This is a wrapper around pubkey_algo_string which takes a KSBA
* certificate instead of a Gcrypt public key. Note that this
* function may return NULL on error. */
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 27168904c..473ef5e09 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -463,6 +463,7 @@ static struct debug_flags_s debug_flags [] =
static struct compatibility_flags_s compatibility_flags [] =
{
{ COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" },
+ { COMPAT_ALLOW_ECC_ENCR, "allow-ecc-encr" },
{ 0, NULL }
};
@@ -494,7 +495,7 @@ static int default_include_certs = DEFAULT_INCLUDE_CERTS;
static int default_validation_model;
/* The default cipher algo. */
-#define DEFAULT_CIPHER_ALGO "AES"
+#define DEFAULT_CIPHER_ALGO "AES256"
static char *build_list (const char *text,
@@ -515,6 +516,7 @@ our_pk_test_algo (int algo)
{
case GCRY_PK_RSA:
case GCRY_PK_ECDSA:
+ case GCRY_PK_EDDSA:
return gcry_pk_test_algo (algo);
default:
return 1;
@@ -1530,7 +1532,7 @@ main ( int argc, char **argv)
set_debug ();
if (opt.verbose) /* Print the compatibility flags. */
parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags);
- gnupg_set_compliance_extra_info (opt.min_rsa_length);
+ gnupg_set_compliance_extra_info (CO_EXTRA_INFO_MIN_RSA, opt.min_rsa_length);
/* Although we always use gpgsm_exit, we better install a regualr
exit handler so that at least the secure memory gets wiped
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 53ef165a1..0735fcb22 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -181,6 +181,7 @@ struct
* policies: 1.3.6.1.4.1.7924.1.1:N:
*/
#define COMPAT_ALLOW_KA_TO_ENCR 1
+#define COMPAT_ALLOW_ECC_ENCR 2
/* Forward declaration for an object defined in server.c */
@@ -236,6 +237,7 @@ struct certlist_s
ksba_cert_t cert;
int is_encrypt_to; /* True if the certificate has been set through
the --encrypto-to option. */
+ int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */
int hash_algo; /* Used to track the hash algorithm to use. */
const char *hash_algo_oid; /* And the corresponding OID. */
};
@@ -281,6 +283,9 @@ unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert,
unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array);
char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert);
int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits);
+int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits,
+ char **r_curve);
+int gpgsm_is_ecc_key (ksba_cert_t cert);
char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid);
char *gpgsm_get_certid (ksba_cert_t cert);
@@ -387,6 +392,10 @@ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist,
int in_fd, estream_t out_fp);
/*-- decrypt.c --*/
+gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd,
+ const char *wrap_algo_str,
+ unsigned int keylen,
+ const void *ukm, unsigned int ukmlen);
int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp);
/*-- certreqgen.c --*/
diff --git a/sm/keylist.c b/sm/keylist.c
index 2d51aa74d..19e7a76c8 100644
--- a/sm/keylist.c
+++ b/sm/keylist.c
@@ -247,7 +247,7 @@ print_key_data (ksba_cert_t cert, estream_t fp)
}
static void
-print_capabilities (ksba_cert_t cert, estream_t fp)
+print_capabilities (ksba_cert_t cert, int algo, estream_t fp)
{
gpg_error_t err;
unsigned int use;
@@ -299,7 +299,7 @@ print_capabilities (ksba_cert_t cert, estream_t fp)
/* We need to returned the faked key usage to frontends so that they
* can select the right key. Note that we don't do this for the
* human readable keyUsage. */
- if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)
+ if ((algo == GCRY_PK_ECC || (opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR))
&& (use & KSBA_KEYUSAGE_KEY_AGREEMENT))
is_encr = 1;
@@ -408,6 +408,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
gpg_error_t valerr;
int algo;
unsigned int nbits;
+ char *curve = NULL;
const char *chain_id;
char *chain_id_buffer = NULL;
int is_root = 0;
@@ -499,7 +500,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
if (*truststring)
es_fputs (truststring, fp);
- algo = gpgsm_get_key_algo_info (cert, &nbits);
+ algo = gpgsm_get_key_algo_info2 (cert, &nbits, &curve);
es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
ksba_cert_get_validity (cert, 0, t);
@@ -538,7 +539,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
/* Field 11, signature class - not used */
es_putc (':', fp);
/* Field 12, capabilities: */
- print_capabilities (cert, fp);
+ print_capabilities (cert, algo, fp);
es_putc (':', fp);
/* Field 13, not used: */
es_putc (':', fp);
@@ -563,6 +564,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
}
es_putc (':', fp); /* End of field 15. */
es_putc (':', fp); /* End of field 16. */
+ if (curve)
+ es_fputs (curve, fp);
es_putc (':', fp); /* End of field 17. */
print_compliance_flags (cert, algo, nbits, fp);
es_putc (':', fp); /* End of field 18. */
@@ -626,6 +629,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
xfree (p);
}
xfree (kludge_uid);
+ xfree (curve);
}
@@ -829,12 +833,11 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
es_fprintf (fp, " hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
{
- const char *algoname;
- unsigned int nbits;
+ char *algostr;
- algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
- es_fprintf (fp, " keyType: %u bit %s\n",
- nbits, algoname? algoname:"?");
+ algostr = gpgsm_pubkey_algo_string (cert, NULL);
+ es_fprintf (fp, " keyType: %s\n", algostr? algostr : "[error]");
+ xfree (algostr);
}
/* subjectKeyIdentifier */
@@ -1192,15 +1195,13 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
{
- const char *algoname;
- unsigned int nbits;
+ char *algostr;
- algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
- es_fprintf (fp, " key type: %u bit %s\n",
- nbits, algoname? algoname:"?");
+ algostr = gpgsm_pubkey_algo_string (cert, NULL);
+ es_fprintf (fp, " key type: %s\n", algostr? algostr : "[error]");
+ xfree (algostr);
}
-
err = ksba_cert_get_key_usage (cert, &kusage);
if (gpg_err_code (err) != GPG_ERR_NO_DATA)
{
diff --git a/sm/misc.c b/sm/misc.c
index 66d928c6b..1c28293aa 100644
--- a/sm/misc.c
+++ b/sm/misc.c
@@ -101,7 +101,7 @@ setup_pinentry_env (void)
function ignores missing parameters so that it can also be used to
create an siginfo value as expected by ksba_certreq_set_siginfo.
To create a siginfo s-expression a public-key s-expression may be
- used instead of a sig-val. We only support RSA for now. */
+ used instead of a sig-val. */
gpg_error_t
transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
unsigned char **r_newsigval, size_t *r_newsigvallen)
@@ -111,11 +111,15 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
size_t buflen, toklen;
int depth, last_depth1, last_depth2;
int is_pubkey = 0;
- const unsigned char *rsa_s = NULL;
- size_t rsa_s_len = 0;
+ int pkalgo;
+ const unsigned char *rsa_s, *ecc_r, *ecc_s;
+ size_t rsa_s_len, ecc_r_len, ecc_s_len;
const char *oid;
gcry_sexp_t sexp;
+ rsa_s = ecc_r = ecc_s = NULL;
+ rsa_s_len = ecc_r_len = ecc_s_len = 0;
+
*r_newsigval = NULL;
if (r_newsigvallen)
*r_newsigvallen = 0;
@@ -137,7 +141,15 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
return err;
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
- if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+ if (!tok)
+ return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+ if (toklen == 3 && !memcmp ("rsa", tok, 3))
+ pkalgo = GCRY_PK_RSA;
+ else if (toklen == 3 && !memcmp ("ecc", tok, 3))
+ pkalgo = GCRY_PK_ECC;
+ else if (toklen == 5 && !memcmp ("ecdsa", tok, 5))
+ pkalgo = GCRY_PK_ECC;
+ else
return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
last_depth1 = depth;
@@ -150,8 +162,8 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
return err;
if (tok && toklen == 1)
{
- const unsigned char **mpi;
- size_t *mpi_len;
+ const unsigned char **mpi = NULL;
+ size_t *mpi_len = NULL;
switch (*tok)
{
@@ -161,6 +173,25 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
if (mpi && *mpi)
return gpg_error (GPG_ERR_DUP_VALUE);
+ switch (*tok)
+ {
+ case 's':
+ if (pkalgo == GCRY_PK_RSA)
+ {
+ mpi = &rsa_s;
+ mpi_len = &rsa_s_len;
+ }
+ else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA)
+ {
+ mpi = &ecc_s;
+ mpi_len = &ecc_s_len;
+ }
+ break;
+
+ case 'r': mpi = &ecc_r; mpi_len = &ecc_r_len; break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+
if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
return err;
if (tok && mpi)
@@ -181,34 +212,62 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
if (err)
return err;
- /* Map the hash algorithm to an OID. */
- switch (mdalgo)
+ if (0)
+ ; /* Just to align it with the code in 2.3. */
+ else
{
- case GCRY_MD_SHA1:
- oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */
- break;
+ /* Map the hash algorithm to an OID. */
+ if (mdalgo < 0 || mdalgo > (1<<15) || pkalgo < 0 || pkalgo > (1<<15))
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+
+ switch (mdalgo | (pkalgo << 16))
+ {
+ case GCRY_MD_SHA1 | (GCRY_PK_RSA << 16):
+ oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA256 | (GCRY_PK_RSA << 16):
+ oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA384 | (GCRY_PK_RSA << 16):
+ oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA512 | (GCRY_PK_RSA << 16):
+ oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */
+ break;
+
+ case GCRY_MD_SHA224 | (GCRY_PK_ECC << 16):
+ oid = "1.2.840.10045.4.3.1"; /* ecdsa-with-sha224 */
+ break;
- case GCRY_MD_SHA256:
- oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */
- break;
+ case GCRY_MD_SHA256 | (GCRY_PK_ECC << 16):
+ oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-sha256 */
+ break;
- case GCRY_MD_SHA384:
- oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */
- break;
+ case GCRY_MD_SHA384 | (GCRY_PK_ECC << 16):
+ oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-sha384 */
+ break;
- case GCRY_MD_SHA512:
- oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */
- break;
+ case GCRY_MD_SHA512 | (GCRY_PK_ECC << 16):
+ oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-sha512 */
+ break;
- default:
- return gpg_error (GPG_ERR_DIGEST_ALGO);
+ default:
+ return gpg_error (GPG_ERR_DIGEST_ALGO);
+ }
}
- if (rsa_s && !is_pubkey)
+ if (is_pubkey)
+ err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid);
+ else if (pkalgo == GCRY_PK_RSA)
err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))",
oid, (int)rsa_s_len, rsa_s);
- else
- err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid);
+ else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA)
+ err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(r%b)(s%b)))", oid,
+ (int)ecc_r_len, ecc_r, (int)ecc_s_len, ecc_s);
+
if (err)
return err;
err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen);
diff --git a/sm/sign.c b/sm/sign.c
index dd7612f27..bc25535db 100644
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -430,6 +430,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
release_signerlist = 1;
}
+
/* Figure out the hash algorithm to use. We do not want to use the
one for the certificate but if possible an OID for the plain
algorithm. */
@@ -438,6 +439,11 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
for (i=0, cl=signerlist; cl; cl = cl->next, i++)
{
const char *oid;
+ unsigned int nbits;
+ int pk_algo;
+
+ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
+ cl->pk_algo = pk_algo;
if (opt.forced_digest_algo)
{
@@ -446,7 +452,21 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
}
else
{
- oid = ksba_cert_get_digest_algo (cl->cert);
+ if (pk_algo == GCRY_PK_ECC)
+ {
+ /* Map the Curve to a corresponding hash algo. */
+ if (nbits <= 256)
+ oid = "2.16.840.1.101.3.4.2.1"; /* sha256 */
+ else if (nbits <= 384)
+ oid = "2.16.840.1.101.3.4.2.2"; /* sha384 */
+ else
+ oid = "2.16.840.1.101.3.4.2.3"; /* sha512 */
+ }
+ else
+ {
+ /* For RSA we reuse the hash algo used by the certificate. */
+ oid = ksba_cert_get_digest_algo (cl->cert);
+ }
cl->hash_algo = oid ? gcry_md_map_name (oid) : 0;
}
switch (cl->hash_algo)
@@ -457,7 +477,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break;
case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break;
case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break;
-/* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */
case GCRY_MD_MD5: /* We don't want to use MD5. */
case 0: /* No algorithm found in cert. */
@@ -482,27 +501,22 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
goto leave;
}
- {
- unsigned int nbits;
- int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
-
- if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0,
- NULL, nbits, NULL))
- {
- char kidstr[10+1];
-
- snprintf (kidstr, sizeof kidstr, "0x%08lX",
- gpgsm_get_short_fingerprint (cl->cert, NULL));
- log_error (_("key %s may not be used for signing in %s mode\n"),
- kidstr,
- gnupg_compliance_option_string (opt.compliance));
- err = gpg_error (GPG_ERR_PUBKEY_ALGO);
- goto leave;
- }
- }
+ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0,
+ NULL, nbits, NULL))
+ {
+ char kidstr[10+1];
+
+ snprintf (kidstr, sizeof kidstr, "0x%08lX",
+ gpgsm_get_short_fingerprint (cl->cert, NULL));
+ log_error (_("key %s may not be used for signing in %s mode\n"),
+ kidstr,
+ gnupg_compliance_option_string (opt.compliance));
+ err = gpg_error (GPG_ERR_PUBKEY_ALGO);
+ goto leave;
+ }
}
- if (opt.verbose)
+ if (opt.verbose > 1 || opt.debug)
{
for (i=0, cl=signerlist; cl; cl = cl->next, i++)
log_info (_("hash algorithm used for signer %d: %s (%s)\n"),
@@ -644,13 +658,16 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
}
/* We need to write at least a minimal list of our capabilities to
- try to convince some MUAs to use 3DES and not the crippled
- RC2. Our list is:
-
- aes128-CBC
- des-EDE3-CBC
- */
- err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0);
+ * try to convince some MUAs to use 3DES and not the crippled
+ * RC2. Our list is:
+ *
+ * aes256-CBC
+ * aes128-CBC
+ * des-EDE3-CBC
+ */
+ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.42", NULL,0);
+ if (!err)
+ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL,0);
if (!err)
err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0);
if (err)
@@ -777,17 +794,23 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
goto leave;
}
rc = 0;
- {
- int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL);
- buf = xtryasprintf ("%c %d %d 00 %s %s",
- detached? 'D':'S',
- pkalgo,
- cl->hash_algo,
- signed_at,
- fpr);
- if (!buf)
- rc = gpg_error_from_syserror ();
- }
+ if (opt.verbose)
+ {
+ char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL);
+ log_info (_("%s/%s signature using %s key %s\n"),
+ pubkey_algo_to_string (cl->pk_algo),
+ gcry_md_algo_name (cl->hash_algo),
+ pkalgostr, fpr);
+ xfree (pkalgostr);
+ }
+ buf = xtryasprintf ("%c %d %d 00 %s %s",
+ detached? 'D':'S',
+ cl->pk_algo,
+ cl->hash_algo,
+ signed_at,
+ fpr);
+ if (!buf)
+ rc = gpg_error_from_syserror ();
xfree (fpr);
if (rc)
{
@@ -813,7 +836,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
audit_log (ctrl->audit, AUDIT_SIGNING_DONE);
log_info ("signature created\n");
-
leave:
if (rc)
log_error ("error creating signature: %s <%s>\n",
diff --git a/sm/verify.c b/sm/verify.c
index 5510f42cb..ea2192440 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -459,6 +459,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp)
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
pkalgo = gpgsm_get_key_algo_info (cert, &nbits);
+ /* Remap the ECC algo to the algo we use. Note that EdDSA has
+ * already been mapped. */
+ if (pkalgo == GCRY_PK_ECC)
+ pkalgo = GCRY_PK_ECDSA;
log_info (_("Signature made "));
if (*sigtime)