diff options
47 files changed, 2748 insertions, 547 deletions
@@ -1,6 +1,17 @@ Noteworthy changes in version 2.2.42 (unreleased) ------------------------------------------------- + * gpg: Support OCB encryption. [T6263] + + * gpg: New command --quick-update-pref. [rGf16c946be7] + + * gpg: New list-options show-pref and show-pref-verbose. + [rGb6ba7054a0] + + * gpgsm: Support ECC certificates. [T6253] + + * gpgsm: Also announce AES256-CBC in signatures. [rGaa397fdcdb21] + Release-info: https://dev.gnupg.org/T6307 diff --git a/common/compliance.c b/common/compliance.c index eaecee7b0..9f407fad2 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -45,6 +45,9 @@ static int module; * using a confue file. */ static unsigned int min_compliant_rsa_length; +/* Temporary hack to allow OCB mode in de-vs mode. */ +static unsigned int vsd_allow_ocb; + /* Return the address of a compliance cache variable for COMPLIANCE. * If no such variable exists NULL is returned. FOR_RNG returns the * cache variable for the RNG compliance check. */ @@ -380,7 +383,8 @@ gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, switch (module) { case GNUPG_MODULE_NAME_GPG: - return mode == GCRY_CIPHER_MODE_CFB; + return (mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)); case GNUPG_MODULE_NAME_GPGSM: return mode == GCRY_CIPHER_MODE_CBC; } @@ -424,7 +428,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, { case GNUPG_MODULE_NAME_GPG: return (mode == GCRY_CIPHER_MODE_NONE - || mode == GCRY_CIPHER_MODE_CFB); + || mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)); case GNUPG_MODULE_NAME_GPGSM: return (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CBC @@ -441,7 +446,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, case CIPHER_ALGO_TWOFISH: return (module == GNUPG_MODULE_NAME_GPG && (mode == GCRY_CIPHER_MODE_NONE - || mode == GCRY_CIPHER_MODE_CFB) + || mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)) && ! producer); default: return 0; @@ -696,7 +702,15 @@ gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) /* Set additional infos for example taken from config files at startup. */ void -gnupg_set_compliance_extra_info (unsigned int min_rsa) +gnupg_set_compliance_extra_info (enum gnupg_co_extra_infos what, + unsigned int value) { - min_compliant_rsa_length = min_rsa; + switch (what) + { + case CO_EXTRA_INFO_MIN_RSA: + min_compliant_rsa_length = value; + break; + case CO_EXTRA_INFO_VSD_ALLOW_OCB: + vsd_allow_ocb = value; + } } diff --git a/common/compliance.h b/common/compliance.h index e29ff4ee2..bd805258a 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -36,12 +36,14 @@ void gnupg_initialize_compliance (int gnupg_module_name); + enum gnupg_compliance_mode { CO_GNUPG, CO_RFC4880, CO_RFC2440, CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS }; + enum pk_use_case { PK_USE_ENCRYPTION, PK_USE_DECRYPTION, @@ -91,7 +93,14 @@ int gnupg_parse_compliance_option (const char *string, const char *gnupg_compliance_option_string (enum gnupg_compliance_mode compliance); -void gnupg_set_compliance_extra_info (unsigned int min_rsa); +enum gnupg_co_extra_infos + { + CO_EXTRA_INFO_MIN_RSA, + CO_EXTRA_INFO_VSD_ALLOW_OCB + }; + +void gnupg_set_compliance_extra_info (enum gnupg_co_extra_infos what, + unsigned int value); #endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index 05f362159..f7ea0b52c 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -130,8 +130,8 @@ sigsubpkttype_t; typedef enum { AEAD_ALGO_NONE = 0, - AEAD_ALGO_EAX = 1, - AEAD_ALGO_OCB = 2 + AEAD_ALGO_EAX = 1, /* Deprecated. */ + AEAD_ALGO_OCB = 2 /* The one and only. */ } aead_algo_t; diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index 238036903..00f76d4df 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -1625,8 +1625,21 @@ start_sig_check (ksba_crl_t crl, gcry_md_hd_t *md, int *algo, int *use_pss) } else *algo = gcry_md_map_name (algoid); + if (!*algo && algoid) + { + if (!strcmp (algoid, "1.2.840.10045.4.3.1")) + *algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.2")) + *algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.3")) + *algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ + else if (!strcmp (algoid, "1.2.840.10045.4.3.4")) + *algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ + } if (!*algo) { + log_debug ("XXXXX %s: %s <%s>\n", + __func__, gpg_strerror (err), gpg_strsource (err)); log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); return gpg_error (GPG_ERR_DIGEST_ALGO); } @@ -1660,6 +1673,7 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, size_t n; gcry_sexp_t s_sig = NULL, s_hash = NULL, s_pkey = NULL; unsigned int saltlen = 0; /* (used only with use_pss) */ + int pkalgo; /* This also stops debugging on the MD. */ gcry_md_final (md); @@ -1784,6 +1798,54 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, gcry_md_read (md, algo), saltlen); } + else if ((pkalgo = pk_algo_from_sexp (s_pkey)) == GCRY_PK_ECC) + { + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + gcry_pk_algo_name (pkalgo), qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + n = gcry_md_get_algo_dlen (algo); + if (n < qbits/8) + { + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)n*8, + qbits0, + gcry_pk_algo_name (pkalgo)); + if (n < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + } + + /* Truncate. */ + if (n > qbits/8) + n = qbits/8; + + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)n, + gcry_md_read (md, algo)); + + } else { err = gcry_sexp_build (&s_hash, NULL, @@ -1801,7 +1863,7 @@ finish_sig_check (ksba_crl_t crl, gcry_md_hd_t md, int algo, /* Pass this on to the signature verification. */ err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) - log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + log_debug ("%s: gcry_pk_verify: %s\n", __func__, gpg_strerror (err)); leave: xfree (sigval); diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 3483ab992..1f8cdb16f 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -406,32 +406,115 @@ validate_responder_cert (ctrl_t ctrl, ksba_cert_t cert, } -/* Helper for check_signature. */ -static int +/* Helper for check_signature. MD is the finalized hash context. */ +static gpg_error_t check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, - gcry_sexp_t s_hash, fingerprint_list_t signer_fpr_list) + gcry_md_hd_t md, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - ksba_sexp_t pubkey; gcry_sexp_t s_pkey = NULL; + gcry_sexp_t s_hash = NULL; + const char *s; + int mdalgo, mdlen; + + /* Get the public key as a gcrypt s-expression. */ + { + ksba_sexp_t pk = ksba_cert_get_public_key (cert); + if (!pk) + err = gpg_error (GPG_ERR_INV_OBJ); + else + { + err = canon_sexp_to_gcry (pk, &s_pkey); + xfree (pk); + } + if (err) + goto leave; + } + + mdalgo = gcry_md_get_algo (md); + mdlen = gcry_md_get_algo_dlen (mdalgo); + + if (pk_algo_from_sexp (s_pkey) == GCRY_PK_ECC) + { + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + "ECDSA", qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + if (mdlen < qbits/8) + { + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)mdlen*8, + qbits0, + "ECDSA"); + if (mdlen < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + } + + /* Truncate. */ + if (mdlen > qbits/8) + mdlen = qbits/8; + + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", + (int)mdlen, gcry_md_read (md, mdalgo)); + } + else if (mdalgo && (s = gcry_md_algo_name (mdalgo)) && strlen (s) < 16) + { + /* Assume RSA */ + char hashalgostr[16+1]; + int i; - pubkey = ksba_cert_get_public_key (cert); - if (!pubkey) - err = gpg_error (GPG_ERR_INV_OBJ); + for (i=0; s[i]; i++) + hashalgostr[i] = ascii_tolower (s[i]); + hashalgostr[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + hashalgostr, + (int)mdlen, + gcry_md_read (md, mdalgo)); + } else - err = canon_sexp_to_gcry (pubkey, &s_pkey); - xfree (pubkey); - if (!err) - err = gcry_pk_verify (s_sig, s_hash, s_pkey); - if (!err) - err = validate_responder_cert (ctrl, cert, signer_fpr_list); - if (!err) + err = gpg_error (GPG_ERR_DIGEST_ALGO); + if (err) + { + log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) { - gcry_sexp_release (s_pkey); - return 0; /* Successfully verified the signature. */ + gcry_log_debugsxp ("sig ", s_sig); + gcry_log_debugsxp ("hash", s_hash); } - /* We simply ignore all errors. */ + err = gcry_pk_verify (s_sig, s_hash, s_pkey); + if (err) + goto leave; + + err = validate_responder_cert (ctrl, cert, signer_fpr_list); + + leave: + gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return err; } @@ -449,35 +532,11 @@ check_signature (ctrl_t ctrl, fingerprint_list_t signer_fpr_list) { gpg_error_t err; - int algo, cert_idx; - gcry_sexp_t s_hash; + int cert_idx; ksba_cert_t cert; - const char *s; /* Create a suitable S-expression with the hash value of our response. */ gcry_md_final (md); - algo = gcry_md_get_algo (md); - s = gcry_md_algo_name (algo); - if (algo && s && strlen (s) < 16) - { - char hashalgostr[16+1]; - int i; - - for (i=0; s[i]; i++) - hashalgostr[i] = ascii_tolower (s[i]); - hashalgostr[i] = 0; - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", - hashalgostr, - (int)gcry_md_get_algo_dlen (algo), - gcry_md_read (md, algo)); - } - else - err = gpg_error (GPG_ERR_DIGEST_ALGO); - if (err) - { - log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); - return err; - } /* Get rid of old OCSP specific certificate references. */ release_ctrl_ocsp_certs (ctrl); @@ -492,13 +551,12 @@ check_signature (ctrl_t ctrl, cert = get_cert_local (ctrl, signer_fpr_list->hexfpr); if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); cert = NULL; if (!err) { - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } } @@ -558,14 +616,12 @@ check_signature (ctrl_t ctrl, if (cert) { - err = check_signature_core (ctrl, cert, s_sig, s_hash, - signer_fpr_list); + err = check_signature_core (ctrl, cert, s_sig, md, signer_fpr_list); ksba_cert_release (cert); if (!err) { ksba_free (name); ksba_free (keyid); - gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } log_error ("responder certificate "); @@ -583,7 +639,6 @@ check_signature (ctrl_t ctrl, ksba_free (keyid); } - gcry_sexp_release (s_hash); log_error (_("no suitable certificate found to verify the OCSP response\n")); return gpg_error (GPG_ERR_NO_PUBKEY); } diff --git a/dirmngr/validate.c b/dirmngr/validate.c index 901c165ec..b3a1c14a1 100644 --- a/dirmngr/validate.c +++ b/dirmngr/validate.c @@ -231,7 +231,8 @@ allowed_ca (ksba_cert_t cert, int *chainlen) /* The German SigG Root CA's certificate does not flag itself as a CA; thus we relax this requirement if we trust a root CA. I think this is reasonable. Note, that - gpgsm implements a far stricter scheme here. */ + gpgsm implements a far stricter scheme here but also + features a "relax" flag in the trustlist.txt. */ if (chainlen) *chainlen = 3; /* That is what the SigG implements. */ if (opt.verbose) @@ -858,7 +859,7 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, /* Return the public key algorithm id from the S-expression PKEY. FIXME: libgcrypt should provide such a function. Note that this implementation uses the names as used by libksba. */ -static int +int pk_algo_from_sexp (gcry_sexp_t pkey) { gcry_sexp_t l1, l2; @@ -879,6 +880,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; + else if (n==3 && !memcmp (name, "ecc", 3)) + algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else @@ -952,15 +955,24 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) int digestlen; unsigned char *digest; int use_pss = 0; - unsigned int saltlen; + unsigned int saltlen; /* (use is controlled by use_pss) */ /* Hash the target certificate using the algorithm from that certificate. */ algoid = ksba_cert_get_digest_algo (cert); algo = gcry_md_map_name (algoid); 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_debug ("XXXXX %s\n", __func__); log_error (_("unknown hash algorithm '%s'\n"), algoid? algoid:"?"); return gpg_error (GPG_ERR_GENERAL); } @@ -1106,19 +1118,48 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) digest, saltlen); } - else if (pk_algo_from_sexp (s_pkey) == GCRY_PK_DSA) + else if (pk_algo_from_sexp (s_pkey) == GCRY_PK_ECC) { - /* NB.: We support only SHA-1 here because we had problems back - * then to get test data for DSA-2. Meanwhile DSA has been - * replaced by ECDSA which we do not yet support. */ - if (digestlen != 20) + unsigned int qbits0, qbits; + + qbits0 = gcry_pk_get_nbits (s_pkey); + qbits = qbits0 == 521? 512 : qbits0; + + if ((qbits%8)) + { + log_error ("ECDSA requires the hash length to be a" + " multiple of 8 bits\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Don't allow any Q smaller than 160 bits. */ + if (qbits < 160) + { + log_error (_("%s key uses an unsafe (%u bit) hash\n"), + "ECDSA", qbits0); + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } + + /* Check if we're too short. */ + if (digestlen < qbits/8) { - log_error ("DSA requires the use of a 160 bit hash algorithm\n"); - gcry_md_close (md); - gcry_sexp_release (s_sig); - gcry_sexp_release (s_pkey); - return gpg_error (GPG_ERR_INTERNAL); + log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), + (unsigned int)digestlen*8, + qbits0, + "ECDSA"); + if (digestlen < 20) + { + err = gpg_error (GPG_ERR_INTERNAL); + goto leave; + } } + + /* Truncate. */ + if (digestlen > qbits/8) + digestlen = qbits/8; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags raw)(value %b))", (int)digestlen, digest); } @@ -1131,7 +1172,9 @@ check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) if (!err) err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) - log_debug ("gcry_pk_verify: %s\n", gpg_strerror (err)); + log_debug ("%s: gcry_pk_verify: %s\n", __func__, gpg_strerror (err)); + + leave: gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); diff --git a/dirmngr/validate.h b/dirmngr/validate.h index c7082e3d1..5b23cb4de 100644 --- a/dirmngr/validate.h +++ b/dirmngr/validate.h @@ -48,6 +48,9 @@ #define VALIDATE_FLAG_NOCRLCHECK 1024 +/* Helper to get the public key algo from a public key. */ +int pk_algo_from_sexp (gcry_sexp_t pkey); + /* Validate the certificate CHAIN up to the trust anchor. Optionally return the closest expiration time in R_EXPTIME. */ gpg_error_t validate_cert_chain (ctrl_t ctrl, diff --git a/doc/gpg.texi b/doc/gpg.texi index fac97c440..9491dc83f 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -956,7 +956,8 @@ signing. --version} to get a list of available algorithms. Note that while you can change the preferences on an attribute user ID (aka "photo ID"), GnuPG does not select keys via attribute user IDs so these preferences - will not be used by GnuPG. + will not be used by GnuPG. Note that an unattended version of this + command is available as @option{--quick-update-pref}. When setting preferences, you should list the algorithms in the order which you'd like to see them used by someone else when encrypting a @@ -1164,6 +1165,16 @@ user ID which shall be flagged as the primary user ID. The primary user ID flag is removed from all other user ids and the timestamp of all affected self-signatures is set one second ahead. +@item --quick-update-pref @var{user-id} +@opindex quick-update-pref +This command updates the preference list of the key to the current +default value (either built-in or set via +@option{--default-preference-list}). This is the unattended version +of of using "setpref" in the @option{--key-edit} menu without giving a +list. Note that you can show the preferences in a key listing by +using @option{--list-options show-pref} or @option{--list-options +show-pref-verbose}. You should also re-distribute updated keys to +your peers. @item --change-passphrase @var{user-id} @opindex change-passphrase @@ -2714,6 +2725,14 @@ is the default. @itemx --no-force-v4-certs These options are obsolete and have no effect since GnuPG 2.1. +@item --force-ocb +@opindex force-ocb +Force the use of OCB mode encryption instead of CFB+MDC encryption. +OCB is a modern and faster way to do authenticated encryption than the +older CFB+MDC method. This option is only useful for symmetric-only +encryption because the mode is automatically selected based on the +preferences of the recipients's public keys. + @item --force-mdc @itemx --disable-mdc @opindex force-mdc diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index ba91aed92..82450d2b1 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -1140,10 +1140,12 @@ General Parameters: @item Key-Type: @var{algo} Starts a new parameter block by giving the type of the primary key. The algorithm must be capable of signing. This is a required -parameter. The only supported value for @var{algo} is @samp{rsa}. +parameter. The supported values for @var{algo} are @samp{rsa}, +@samp{ecdsa}, and @samp{eddsa}. @item Key-Length: @var{nbits} -The requested length of a generated key in bits. Defaults to 3072. +The requested length of a generated key in bits. Defaults to +3072. The value is ignored for ECC algorithms. @item Key-Grip: @var{hexstring} This is optional and used to generate a CSR or certificate for an @@ -1216,6 +1218,20 @@ algorithms are: @samp{sha1}, @samp{sha256}, @samp{sha384} and @samp{sha512}; they may also be specified with uppercase letters. The default is @samp{sha256}. +@item Authority-Key-Id: @var{hexstring} +Insert the decoded value of @var{hexstring} as authorityKeyIdentifier. +If this is not given and an ECC algorithm is used the public part of +the certified public key is used as authorityKeyIdentifier. To +inhibit any authorityKeyIdentifier use the special value @code{none} +for @var{hexstring}. + +@item Subject-Key-Id: @var{hexstring} +Insert the decoded value of @var{hexstring} as subjectKeyIdentifier. +If this is not given and an ECC algorithm is used the public part of +the signing key is used as authorityKeyIdentifier. To inhibit any +subjectKeyIdentifier use the special value @code{none} for +@var{hexstring}. + @end table @c ******************************************* diff --git a/g10/build-packet.c b/g10/build-packet.c index a40ed0d82..63bfadbe0 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -42,6 +42,7 @@ static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); @@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt) break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED: @@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt) case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); break; + case PKT_ENCRYPTED_AEAD: + rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted); + break; case PKT_COMPRESSED: rc = do_compressed (out, ctb, pkt->pkt.compressed); break; @@ -618,9 +623,7 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) IOBUF a = iobuf_temp(); log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC); - - /* The only acceptable version. */ - log_assert( enc->version == 4 ); + log_assert (enc->version == 4 || enc->version == 5); /* RFC 4880, Section 3.7. */ switch (enc->s2k.mode) @@ -635,6 +638,8 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) } iobuf_put( a, enc->version ); iobuf_put( a, enc->cipher_algo ); + if (enc->version == 5) + iobuf_put (a, enc->aead_algo); iobuf_put( a, enc->s2k.mode ); iobuf_put( a, enc->s2k.hash_algo ); if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { @@ -821,6 +826,32 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) } +/* Serialize the symmetrically AEAD encrypted data packet + * (rfc4880bis-03, Section 5.16) described by ED and write it to OUT. + * + * Note: this only writes only packet's header. The caller must then + * follow up and write the actual encrypted data. This should be done + * by pushing the the cipher_filter_aead. */ +static int +do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed) +{ + u32 n; + + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD); + + n = ed->len ? (ed->len + ed->extralen + 4) : 0; + write_header (out, ctb, n ); + iobuf_writebyte (out, 1); /* Version. */ + iobuf_writebyte (out, ed->cipher_algo); + iobuf_writebyte (out, ed->aead_algo); + iobuf_writebyte (out, ed->chunkbyte); + + /* This is all. The caller has to write the encrypted data */ + + return 0; +} + + /* Serialize the compressed packet (RFC 4880, Section 5.6) described by CD and write it to OUT. diff --git a/g10/cipher.c b/g10/cipher.c index f577c97db..7509915e5 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -37,11 +37,29 @@ #include "../common/status.h" -#define MIN_PARTIAL_SIZE 512 +/* The size of the buffer we allocate to encrypt the data. This must + * be a multiple of the OCB blocksize (16 byte). */ +#define AEAD_ENC_BUFFER_SIZE (64*1024) + + +/* Wrapper around iobuf_write to make sure that a proper error code is + * always returned. */ +static gpg_error_t +my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen) +{ + if (iobuf_write (a, buffer, buflen)) + { + gpg_error_t err = iobuf_error (a); + if (!err || !gpg_err_code (err)) /* (The latter should never happen) */ + err = gpg_error (GPG_ERR_EIO); + return err; + } + return 0; +} static void -write_header (cipher_filter_context_t *cfx, iobuf_t a) +write_cfb_header (cipher_filter_context_t *cfx, iobuf_t a) { gcry_error_t err; PACKET pkt; @@ -116,7 +134,7 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) /* - * This filter is used to en/de-cipher data with a symmetric algorithm + * This filter is used to encrypt with a symmetric algorithm in CFB mode. */ int cipher_filter_cfb (void *opaque, int control, @@ -128,13 +146,13 @@ cipher_filter_cfb (void *opaque, int control, if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { - rc = -1; /* not yet used */ + rc = -1; /* not used */ } else if (control == IOBUFCTRL_FLUSH) /* encrypt */ { log_assert (a); if (!cfx->wrote_header) - write_header (cfx, a); + write_cfb_header (cfx, a); if (cfx->mdc_hash) gcry_md_write (cfx->mdc_hash, buf, size); gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0); @@ -185,3 +203,432 @@ cipher_filter_cfb (void *opaque, int control, return rc; } + + + +/* Set the nonce and the additional data for the current chunk. If + * FINAL is set the final AEAD chunk is processed. This also reset + * the encryption machinery so that the handle can be used for a new + * chunk. */ +static gpg_error_t +set_ocb_nonce_and_ad (cipher_filter_context_t *cfx, int final) +{ + gpg_error_t err; + unsigned char nonce[16]; + unsigned char ad[21]; + int i; + + log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB); + memcpy (nonce, cfx->startiv, 15); + i = 7; + + nonce[i++] ^= cfx->chunkindex >> 56; + nonce[i++] ^= cfx->chunkindex >> 48; + nonce[i++] ^= cfx->chunkindex >> 40; + nonce[i++] ^= cfx->chunkindex >> 32; + nonce[i++] ^= cfx->chunkindex >> 24; + nonce[i++] ^= cfx->chunkindex >> 16; + nonce[i++] ^= cfx->chunkindex >> 8; + nonce[i++] ^= cfx->chunkindex; + + if (DBG_CRYPTO) + log_printhex (nonce, 15, "nonce:"); + err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i); + if (err) + return err; + + ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); + ad[1] = 1; + ad[2] = cfx->dek->algo; + ad[3] = AEAD_ALGO_OCB; + ad[4] = cfx->chunkbyte; + ad[5] = cfx->chunkindex >> 56; + ad[6] = cfx->chunkindex >> 48; + ad[7] = cfx->chunkindex >> 40; + ad[8] = cfx->chunkindex >> 32; + ad[9] = cfx->chunkindex >> 24; + ad[10]= cfx->chunkindex >> 16; + ad[11]= cfx->chunkindex >> 8; + ad[12]= cfx->chunkindex; + if (final) + { + ad[13] = cfx->total >> 56; + ad[14] = cfx->total >> 48; + ad[15] = cfx->total >> 40; + ad[16] = cfx->total >> 32; + ad[17] = cfx->total >> 24; + ad[18] = cfx->total >> 16; + ad[19] = cfx->total >> 8; + ad[20] = cfx->total; + } + if (DBG_CRYPTO) + log_printhex (ad, final? 21 : 13, "authdata:"); + return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13); +} + + +static gpg_error_t +write_ocb_header (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + PACKET pkt; + PKT_encrypted ed; + unsigned int blocksize; + unsigned int startivlen; + enum gcry_cipher_modes ciphermode; + + log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB); + + blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo); + if (blocksize != 16 ) + log_fatal ("unsupported blocksize %u for AEAD\n", blocksize); + + err = openpgp_aead_algo_info (cfx->dek->use_aead, &ciphermode, &startivlen); + if (err) + goto leave; + + cfx->chunkbyte = 22 - 6; /* Default to the suggested max of 4 MiB. */ + cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6); + cfx->chunklen = 0; + cfx->bufsize = AEAD_ENC_BUFFER_SIZE; + cfx->buflen = 0; + cfx->buffer = xtrymalloc (cfx->bufsize); + if (!cfx->buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + memset (&ed, 0, sizeof ed); + ed.new_ctb = 1; /* (Is anyway required for the packet type). */ + ed.len = 0; /* fixme: cfx->datalen */ + ed.extralen = startivlen + 16; /* (16 is the taglen) */ + ed.cipher_algo = cfx->dek->algo; + ed.aead_algo = cfx->dek->use_aead; + ed.chunkbyte = cfx->chunkbyte; + + init_packet (&pkt); + pkt.pkttype = PKT_ENCRYPTED_AEAD; + pkt.pkt.encrypted = &ed; + + if (DBG_FILTER) + log_debug ("aead packet: len=%lu extralen=%d\n", + (unsigned long)ed.len, ed.extralen); + + write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d", + cfx->dek->algo, ed.aead_algo); + print_cipher_algo_note (cfx->dek->algo); + + if (build_packet( a, &pkt)) + log_bug ("build_packet(ENCRYPTED_AEAD) failed\n"); + + log_assert (sizeof cfx->startiv >= startivlen); + gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM); + err = my_iobuf_write (a, cfx->startiv, startivlen); + if (err) + goto leave; + + err = openpgp_cipher_open (&cfx->cipher_hd, + cfx->dek->algo, + ciphermode, + GCRY_CIPHER_SECURE); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:"); + err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen); + if (err) + return err; + + cfx->wrote_header = 1; + + leave: + return err; +} + + +/* Get and write the auth tag to stream A. */ +static gpg_error_t +write_ocb_auth_tag (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char tag[16]; + + err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16); + if (err) + goto leave; + err = my_iobuf_write (a, tag, 16); + if (err) + goto leave; + + leave: + if (err) + log_error ("write_auth_tag failed: %s\n", gpg_strerror (err)); + return err; +} + + +/* Write the final chunk to stream A. */ +static gpg_error_t +write_ocb_final_chunk (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char dummy[1]; + + err = set_ocb_nonce_and_ad (cfx, 1); + if (err) + goto leave; + + gcry_cipher_final (cfx->cipher_hd); + + /* Encrypt an empty string. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0); + if (err) + goto leave; + + err = write_ocb_auth_tag (cfx, a); + + leave: + return err; +} + + +/* The core of the flush sub-function of cipher_filter_ocb. */ +static gpg_error_t +do_ocb_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size) +{ + gpg_error_t err = 0; + int finalize = 0; + size_t n; + + /* Put the data into a buffer, flush and encrypt as needed. */ + if (DBG_FILTER) + log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen); + do + { + const unsigned fast_threshold = 512; + const byte *src_buf = NULL; + int enc_now = 0; + + if (cfx->buflen + size < cfx->bufsize) + n = size; + else + n = cfx->bufsize - cfx->buflen; + + if (cfx->buflen % fast_threshold != 0) + { + /* Attempt to align cfx->buflen to fast threshold size first. */ + size_t nalign = fast_threshold - (cfx->buflen % fast_threshold); + if (nalign < n) + { + n = nalign; + } + } + else if (cfx->buflen == 0 && n >= fast_threshold) + { + /* Handle large input buffers as multiple of cipher blocksize. */ + n = (n / 16) * 16; + } + + if (cfx->chunklen + cfx->buflen + n >= cfx->chunksize) + { + size_t n1 = cfx->chunksize - (cfx->chunklen + cfx->buflen); + finalize = 1; + if (DBG_FILTER) + log_debug ("chunksize %zu reached;" + " cur buflen=%zu using %zu of %zu\n", + (size_t)cfx->chunksize, cfx->buflen, + n1, n); + n = n1; + } + + if (!finalize && cfx->buflen % 16 == 0 && cfx->buflen > 0 + && size >= fast_threshold) + { + /* If cfx->buffer is aligned and remaining input buffer length + * is long, encrypt cfx->buffer inplace now to allow fast path + * handling on next loop iteration. */ + src_buf = cfx->buffer; + enc_now = 1; + n = 0; + } + else if (cfx->buflen == 0 && n >= fast_threshold) + { + /* Fast path for large input buffer. This avoids memcpy and + * instead encrypts directly from input to cfx->buffer. */ + log_assert (n % 16 == 0 || finalize); + src_buf = buf; + cfx->buflen = n; + buf += n; + size -= n; + enc_now = 1; + } + else if (n > 0) + { + memcpy (cfx->buffer + cfx->buflen, buf, n); + src_buf = cfx->buffer; + cfx->buflen += n; + buf += n; + size -= n; + } + + if (cfx->buflen == cfx->bufsize || enc_now || finalize) + { + if (DBG_FILTER) + log_debug ("encrypting: size=%zu buflen=%zu %s%s n=%zu\n", + size, cfx->buflen, finalize?"(finalize)":"", + enc_now?"(now)":"", n); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_ocb_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + if (finalize) + gcry_cipher_final (cfx->cipher_hd); + if (DBG_FILTER) + { + if (finalize) + log_printhex (src_buf, cfx->buflen, "plain(1):"); + else if (cfx->buflen > 32) + log_printhex (src_buf + cfx->buflen - 32, 32, + "plain(last32):"); + } + + /* Take care: even with a buflen of zero an encrypt needs to + * be called after gcry_cipher_final and before + * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB + * mode. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, + cfx->buflen, src_buf, cfx->buflen); + if (err) + goto leave; + if (finalize && DBG_FILTER) + log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):"); + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + cfx->buflen = 0; + + if (finalize) + { + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_ocb_auth_tag (cfx, a); + if (err) + goto leave; + + cfx->chunkindex++; + cfx->chunklen = 0; + finalize = 0; + } + } + } + while (size); + + leave: + return err; +} + + +/* The core of the free sub-function of cipher_filter_aead. */ +static gpg_error_t +do_ocb_free (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err = 0; + + if (DBG_FILTER) + log_debug ("do_free: buflen=%zu\n", cfx->buflen); + + if (cfx->chunklen || cfx->buflen) + { + if (DBG_FILTER) + log_debug ("encrypting last %zu bytes of the last chunk\n",cfx->buflen); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_ocb_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + gcry_cipher_final (cfx->cipher_hd); + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, + NULL, 0); + if (err) + goto leave; + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */ + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + + /* Get and write the authentication tag. */ + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_ocb_auth_tag (cfx, a); + if (err) + goto leave; + cfx->chunkindex++; + cfx->chunklen = 0; + } + + /* Write the final chunk. */ + if (DBG_FILTER) + log_debug ("creating final chunk\n"); + err = write_ocb_final_chunk (cfx, a); + + leave: + xfree (cfx->buffer); + cfx->buffer = NULL; + gcry_cipher_close (cfx->cipher_hd); + cfx->cipher_hd = NULL; + return err; +} + + +/* + * This filter is used to encrypt with a symmetric algorithm in OCB mode. + */ +int +cipher_filter_ocb (void *opaque, int control, + iobuf_t a, byte *buf, size_t *ret_len) +{ + cipher_filter_context_t *cfx = opaque; + size_t size = *ret_len; + int rc = 0; + + if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ + { + rc = -1; /* not used */ + } + else if (control == IOBUFCTRL_FLUSH) /* encrypt */ + { + if (!cfx->wrote_header && (rc=write_ocb_header (cfx, a))) + ; + else + rc = do_ocb_flush (cfx, a, buf, size); + } + else if (control == IOBUFCTRL_FREE) + { + rc = do_ocb_free (cfx, a); + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "cipher_filter_ocb", *ret_len); + } + + return rc; +} @@ -31,7 +31,9 @@ typedef struct * verbose mode. */ unsigned int algo_info_printed : 1; - /* AEAD shall be used. The value is the AEAD algo. */ + /* AEAD shall be used. The value is the AEAD algo. Note that in + * practise only AEAD_ALGO_OCB, AEAD_ALGO_EAX is only used for + * decryption. */ int use_aead : 4; /* MDC shall be used. */ diff --git a/g10/encrypt.c b/g10/encrypt.c index 5f9480fad..2e3ee13c7 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -52,7 +52,7 @@ static int write_pubkey_enc_from_list (ctrl_t ctrl, int encrypt_symmetric (const char *filename) { - return encrypt_simple( filename, 1, 0 ); + return encrypt_simple( filename, 1, opt.force_ocb); } @@ -126,45 +126,165 @@ create_dek_with_warnings (int fallback_to_3des, pk_list_t pk_list) } -/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and - the algorithm that will be used to encrypt the contents of the SED - packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random - session key that is appropriate for DEK->ALGO is generated and - stored there. - - Encrypt that session key using DEK and store the result in ENCKEY, - which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */ -void -encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) +/* Encrypt a session key using DEK and store a pointer to the result + * at R_ENCKEY and its length at R_ENCKEYLEN. + * + * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and + * the algorithm that will be used to encrypt the contents of the + * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random + * session key that is appropriate for DEK->ALGO is generated and + * stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm + * is used for encryption. + */ +static gpg_error_t +encrypt_seskey (DEK *dek, aead_algo_t aead_algo, + DEK **r_seskey, void **r_enckey, size_t *r_enckeylen) { - gcry_cipher_hd_t hd; - byte buf[33]; + gpg_error_t err; + gcry_cipher_hd_t hd = NULL; + byte *buf = NULL; + DEK *seskey; + + *r_enckey = NULL; + *r_enckeylen = 0; - log_assert ( dek->keylen <= 32 ); - if (!*seskey) + if (*r_seskey) + seskey = *r_seskey; + else { - *seskey=xmalloc_clear(sizeof(DEK)); - (*seskey)->algo=dek->algo; - make_session_key(*seskey); + seskey = xtrycalloc (1, sizeof(DEK)); + if (!seskey) + { + err = gpg_error_from_syserror (); + goto leave; + } + seskey->algo = dek->algo; + make_session_key (seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } - /* The encrypted session key is prefixed with a one-octet algorithm id. */ - buf[0] = (*seskey)->algo; - memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); - - /* We only pass already checked values to the following function, - thus we consider any failure as fatal. */ - if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG (); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG (); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + + if (aead_algo) + { + unsigned int noncelen; + enum gcry_cipher_modes ciphermode; + byte ad[4]; + + err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen); + if (err) + goto leave; + + /* Allocate space for the nonce, the key, and the authentication + * tag (16). */ + buf = xtrymalloc_secure (noncelen + seskey->keylen + 16); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM); + + err = openpgp_cipher_open (&hd, dek->algo, + ciphermode, GCRY_CIPHER_SECURE); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, buf, noncelen); + if (err) + goto leave; + + ad[0] = (0xc0 | PKT_SYMKEY_ENC); + ad[1] = 5; + ad[2] = dek->algo; + ad[3] = aead_algo; + err = gcry_cipher_authenticate (hd, ad, 4); + if (err) + goto leave; + + memcpy (buf + noncelen, seskey->key, seskey->keylen); + gcry_cipher_final (hd); + err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0); + if (err) + goto leave; + err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16); + if (err) + goto leave; + *r_enckeylen = noncelen + seskey->keylen + 16; + *r_enckey = buf; + buf = NULL; + } + else + { + /* In the old version 4 SKESK the encrypted session key is + * prefixed with a one-octet algorithm id. */ + buf = xtrymalloc_secure (1 + seskey->keylen); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + buf[0] = seskey->algo; + memcpy (buf + 1, seskey->key, seskey->keylen ); + + err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, NULL, 0); + if (!err) + err = gcry_cipher_encrypt (hd, buf, 1 + seskey->keylen, NULL, 0); + if (err) + goto leave; + *r_enckeylen = 1 + seskey->keylen; + *r_enckey = buf; + buf = NULL; + } + + /* Return the session key in case we allocated it. */ + *r_seskey = seskey; + seskey = NULL; + + leave: gcry_cipher_close (hd); + if (seskey != *r_seskey) + xfree (seskey); + xfree (buf); + return err; +} - memcpy( enckey, buf, (*seskey)->keylen + 1 ); - wipememory( buf, sizeof buf ); /* burn key */ + +/* Return the AEAD algo if we shall use AEAD mode. Returns 0 if AEAD + * shall not be used. */ +aead_algo_t +use_aead (pk_list_t pk_list, int algo) +{ + int can_use; + + can_use = openpgp_cipher_get_algo_blklen (algo) == 16; + + /* With --force-aead we want AEAD. */ + if (opt.force_ocb) + { + if (!can_use) + { + log_info ("Warning: request to use OCB ignored for cipher '%s'\n", + openpgp_cipher_algo_name (algo)); + return 0; + } + return AEAD_ALGO_OCB; + } + + /* AEAD does only work with 128 bit cipher blocklength. */ + if (!can_use) + return 0; + + /* Note the user which keys have no AEAD feature flag set. */ + if (opt.verbose) + warn_missing_aead_from_pklist (pk_list); + + /* If all keys support AEAD we can use it. */ + return select_aead_from_pklist (pk_list); } @@ -196,9 +316,9 @@ encrypt_simple (const char *filename, int mode, int use_seskey) PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; - byte enckey[33]; + void *enckey = NULL; + size_t enckeylen = 0; int rc = 0; - int seskeylen = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; @@ -250,6 +370,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey) cfx.dek = NULL; if ( mode ) { + aead_algo_t aead_algo; + rc = setup_symkey (&s2k, &cfx.dek); if (rc) { @@ -265,27 +387,46 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { use_seskey = 0; - log_info (_("can't use a symmetric ESK packet " + log_info (_("can't use a SKESK packet" "due to the S2K mode\n")); } + /* See whether we want to use OCB. */ + aead_algo = use_aead (NULL, cfx.dek->algo); + if ( use_seskey ) { - DEK *dek = NULL; /* Dummy. */ + DEK *dek = NULL; - seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); - encrypt_seskey( cfx.dek, &dek, enckey ); - xfree( cfx.dek ); cfx.dek = dek; + rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + { + xfree (cfx.dek); + xfree (s2k); + iobuf_close (inp); + release_progress_context (pfx); + return rc; + } + /* Replace key in DEK. */ + xfree (cfx.dek); + cfx.dek = dek; } - if (opt.verbose) - log_info(_("using cipher %s\n"), - openpgp_cipher_algo_name (cfx.dek->algo)); + if (aead_algo) + cfx.dek->use_aead = aead_algo; + else + cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); - cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); + if (opt.verbose) + log_info(_("using cipher %s.%s\n"), + openpgp_cipher_algo_name (cfx.dek->algo), + cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) + /**/ : "CFB"); } - if (do_compress && cfx.dek && cfx.dek->use_mdc + if (do_compress + && cfx.dek + && (cfx.dek->use_mdc || cfx.dek->use_aead) && is_file_compressed(filename, &rc)) { if (opt.verbose) @@ -310,20 +451,23 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( s2k ) { - PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); - enc->version = 4; + PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen); + enc->version = cfx.dek->use_aead ? 5 : 4; enc->cipher_algo = cfx.dek->algo; + enc->aead_algo = cfx.dek->use_aead; enc->s2k = *s2k; - if ( use_seskey && seskeylen ) + if (enckeylen) { - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy (enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet( out, &pkt ))) log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); xfree (enc); + xfree (enckey); + enckey = NULL; } if (!opt.no_literal) @@ -380,12 +524,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /* Register the cipher filter. */ if (mode) - iobuf_push_filter ( out, cipher_filter_cfb, &cfx ); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &cfx ); /* Register the compress filter. */ if ( do_compress ) { - if (cfx.dek && cfx.dek->use_mdc) + if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } @@ -400,15 +547,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey) { /* User requested not to create a literal packet, so we copy the plain data. */ - byte copy_buffer[4096]; - int bytes_copied; - while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { - log_error ("copying input to output failed: %s\n", - gpg_strerror (rc) ); - break; - } - wipememory (copy_buffer, 4096); /* burn buffer */ + byte copy_buffer[4096]; + int bytes_copied; + while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) + if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { + log_error ("copying input to output failed: %s\n", + gpg_strerror (rc) ); + break; + } + wipememory (copy_buffer, 4096); /* burn buffer */ } /* Finish the stuff. */ @@ -424,6 +571,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if (pt) pt->buf = NULL; free_packet (&pkt, NULL); + xfree (enckey); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); @@ -476,23 +624,33 @@ setup_symkey (STRING2KEY **symkey_s2k, DEK **symkey_dek) static int -write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, - iobuf_t out) +write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, + DEK *symkey_dek, DEK *dek, iobuf_t out) { - int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); - + int rc; + void *enckey; + size_t enckeylen; PKT_symkey_enc *enc; - byte enckey[33]; PACKET pkt; - enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); - encrypt_seskey(symkey_dek,&dek,enckey); + rc = encrypt_seskey (symkey_dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + return rc; + enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen); + if (!enc) + { + rc = gpg_error_from_syserror (); + xfree (enckey); + return rc; + } - enc->version = 4; + enc->version = aead_algo? 5 : 4; enc->cipher_algo = opt.s2k_cipher_algo; + enc->aead_algo = aead_algo; enc->s2k = *symkey_s2k; - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy( enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); + xfree (enckey); pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; @@ -500,7 +658,7 @@ write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, if ((rc=build_packet(out,&pkt))) log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc)); - xfree(enc); + xfree (enc); return rc; } @@ -706,14 +864,18 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (rc) goto leave; - cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); + cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); + if (!cfx.dek->use_aead) + cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ - if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2)) + if (do_compress + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && is_file_compressed(filename, &rc2)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); @@ -737,7 +899,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ - if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) + if(use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead, + symkey_dek, cfx.dek, out))) goto leave; if (!opt.no_literal) @@ -780,7 +943,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ - iobuf_push_filter (out, cipher_filter_cfb, &cfx); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &cfx); /* Register the compress filter. */ if (do_compress) @@ -889,7 +1055,9 @@ encrypt_filter (void *opaque, int control, if (rc) return rc; - efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); + efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo); + if (!efx->cfx.dek->use_aead) + efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) @@ -902,13 +1070,16 @@ encrypt_filter (void *opaque, int control, if(efx->symkey_s2k && efx->symkey_dek) { - rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, - efx->cfx.dek,a); + rc = write_symkey_enc (efx->symkey_s2k, efx->cfx.dek->use_aead, + efx->symkey_dek, efx->cfx.dek, a); if(rc) return rc; } - iobuf_push_filter (a, cipher_filter_cfb, &efx->cfx); + iobuf_push_filter (a, + efx->cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &efx->cfx); } rc = iobuf_write (a, buf, size); @@ -971,6 +1142,12 @@ write_pubkey_enc (ctrl_t ctrl, openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), ustr ); + log_info (_("%s/%s.%s encrypted for: \"%s\"\n"), + openpgp_pk_algo_name (enc->pubkey_algo), + openpgp_cipher_algo_name (dek->algo), + dek->use_aead? openpgp_aead_algo_name (dek->use_aead) + /**/ : "CFB", + ustr ); xfree (ustr); } /* And write it. */ diff --git a/g10/filter.h b/g10/filter.h index d2f6c3f0f..fdb35232d 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -88,15 +88,52 @@ struct compress_filter_context_s { typedef struct compress_filter_context_s compress_filter_context_t; -typedef struct { - DEK *dek; - u32 datalen; - gcry_cipher_hd_t cipher_hd; - unsigned int wrote_header : 1; - unsigned int short_blklen_warn : 1; - unsigned long short_blklen_count; - gcry_md_hd_t mdc_hash; - byte enchash[20]; +typedef struct +{ + /* Object with the key and algo */ + DEK *dek; + + /* Length of the data to encrypt if known - 32 bit because OpenPGP + * requires partial encoding for a larger data size. */ + u32 datalen; + + /* The current cipher handle. */ + gcry_cipher_hd_t cipher_hd; + + /* Various processing flags. */ + unsigned int wrote_header : 1; + unsigned int short_blklen_warn : 1; + unsigned long short_blklen_count; + + /* The encoded chunk byte for AEAD. */ + byte chunkbyte; + + /* The decoded CHUNKBYTE. */ + uint64_t chunksize; + + /* The chunk index for AEAD. */ + uint64_t chunkindex; + + /* The number of bytes in the current chunk. */ + uint64_t chunklen; + + /* The total count of encrypted plaintext octets. Note that we + * don't care about encrypting more than 16 Exabyte. */ + uint64_t total; + + /* The hash context and a buffer used for MDC. */ + gcry_md_hd_t mdc_hash; + byte enchash[20]; + + /* The start IV for AEAD encryption. */ + byte startiv[16]; + + /* Using a large buffer for encryption makes processing easier and + * also makes sure the data is well aligned. */ + char *buffer; + size_t bufsize; /* Allocated length. */ + size_t buflen; /* Used length. */ + } cipher_filter_context_t; @@ -148,6 +185,8 @@ gpg_error_t push_compress_filter2 (iobuf_t out,compress_filter_context_t *zfx, /*-- cipher.c --*/ int cipher_filter_cfb (void *opaque, int control, iobuf_t chain, byte *buf, size_t *ret_len); +int cipher_filter_ocb (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *ret_len); /*-- textfilter.c --*/ int text_filter( void *opaque, int control, @@ -1,7 +1,7 @@ /* gpg.c - The GnuPG utility (main for gpg) * Copyright (C) 1998-2020 Free Software Foundation, Inc. * Copyright (C) 1997-2019 Werner Koch - * Copyright (C) 2015-2021 g10 Code GmbH + * Copyright (C) 2015-2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -128,6 +128,7 @@ enum cmd_and_opt_values aQuickRevUid, aQuickSetExpire, aQuickSetPrimaryUid, + aQuickUpdatePref, aListConfig, aListGcryptConfig, aGPGConfList, @@ -299,6 +300,7 @@ enum cmd_and_opt_values oShowPhotos, oNoShowPhotos, oPhotoViewer, + oForceOCB, oS2KMode, oS2KDigest, oS2KCipher, @@ -479,6 +481,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aQuickSetExpire, "quick-set-expire", N_("quickly set a new expiration date")), ARGPARSE_c (aQuickSetPrimaryUid, "quick-set-primary-uid", "@"), + ARGPARSE_c (aQuickUpdatePref, "quick-update-pref", "@"), ARGPARSE_c (aFullKeygen, "full-generate-key" , N_("full featured key pair generation")), ARGPARSE_c (aFullKeygen, "full-gen-key", "@"), @@ -835,6 +838,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"), ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"), ARGPARSE_s_i (oS2KCount, "s2k-count", "@"), + ARGPARSE_s_n (oForceOCB, "force-ocb", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"), ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"), @@ -991,6 +995,7 @@ static struct debug_flags_s debug_flags [] = /* The list of compatibility flags. */ static struct compatibility_flags_s compatibility_flags [] = { + { COMPAT_VSD_ALLOW_OCB, "vsd-allow-ocb" }, { 0, NULL } }; @@ -2040,6 +2045,10 @@ parse_list_options(char *str) N_("show the keyring name in key listings")}, {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL, N_("show expiration dates during signature listings")}, + {"show-pref", LIST_SHOW_PREF, NULL, + N_("show preferences")}, + {"show-pref-verbose", LIST_SHOW_PREF_VERBOSE, NULL, + N_("show preferences")}, {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, NULL}, {"show-only-fpr-mbox",LIST_SHOW_ONLY_FPR_MBOX, NULL, @@ -2618,6 +2627,7 @@ main (int argc, char **argv) case aQuickRevUid: case aQuickSetExpire: case aQuickSetPrimaryUid: + case aQuickUpdatePref: case aExportOwnerTrust: case aImportOwnerTrust: case aRebuildKeydbCaches: @@ -2999,6 +3009,8 @@ main (int argc, char **argv) break; case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break; + case oForceOCB: opt.force_ocb = 1; break; + case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break; case oIncludeKeyBlock: opt.flags.include_key_block = 1; break; case oNoIncludeKeyBlock: opt.flags.include_key_block = 0; break; @@ -3792,7 +3804,11 @@ main (int argc, char **argv) set_debug (debug_level); 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); + if ((opt.compat_flags & COMPAT_VSD_ALLOW_OCB)) + gnupg_set_compliance_extra_info (CO_EXTRA_INFO_VSD_ALLOW_OCB, 1); + if (DBG_CLOCK) log_clock ("start"); @@ -4174,6 +4190,7 @@ main (int argc, char **argv) case aQuickAddKey: case aQuickRevUid: case aQuickSetPrimaryUid: + case aQuickUpdatePref: case aFullKeygen: case aKeygen: case aImport: @@ -4674,6 +4691,14 @@ main (int argc, char **argv) } break; + case aQuickUpdatePref: + { + if (argc != 1) + wrong_args ("--quick-update-pref USER-ID"); + keyedit_quick_update_pref (ctrl, *argv); + } + break; + case aFastImport: opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index d82995d1d..190949278 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -2169,6 +2169,42 @@ static struct option sk_esk_options[] = { " --literal --value foo | " GPG_NAME " --list-packets" } }; + +/* Old version of encrypt_seskey copied from encrypt.c. */ +static void +encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) +{ + gcry_cipher_hd_t hd; + byte buf[33]; + + log_assert ( dek->keylen <= 32 ); + if (!*seskey) + { + *seskey=xmalloc_clear(sizeof(DEK)); + (*seskey)->algo=dek->algo; + make_session_key(*seskey); + /*log_hexdump( "thekey", c->key, c->keylen );*/ + } + + /* The encrypted session key is prefixed with a one-octet algorithm id. */ + buf[0] = (*seskey)->algo; + memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); + + /* We only pass already checked values to the following function, + thus we consider any failure as fatal. */ + if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) + BUG (); + if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) + BUG (); + gcry_cipher_setiv (hd, NULL, 0); + gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + gcry_cipher_close (hd); + + memcpy( enckey, buf, (*seskey)->keylen + 1 ); + wipememory( buf, sizeof buf ); /* burn key */ +} + + static int sk_esk (const char *option, int argc, char *argv[], void *cookie) { diff --git a/g10/keydb.h b/g10/keydb.h index 63a9f1087..4be889e07 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -266,8 +266,8 @@ int algo_available( preftype_t preftype, int algo, const struct pref_hint *hint ); int select_algo_from_prefs( PK_LIST pk_list, int preftype, int request, const struct pref_hint *hint); -int select_mdc_from_pklist (PK_LIST pk_list); -void warn_missing_mdc_from_pklist (PK_LIST pk_list); +aead_algo_t select_aead_from_pklist (PK_LIST pk_list); +void warn_missing_aead_from_pklist (PK_LIST pk_list); void warn_missing_aes_from_pklist (PK_LIST pk_list); /*-- skclist.c --*/ diff --git a/g10/keyedit.c b/g10/keyedit.c index 1cb62de8a..3e41681d0 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,7 +1,7 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch - * Copyright (C) 2015, 2016 g10 Code GmbH + * Copyright (C) 2015, 2016, 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -78,7 +78,8 @@ static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock); -static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock); +static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock, + int unattended); static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock); static int menu_set_notation (ctrl_t ctrl, @@ -2111,7 +2112,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, " for the selected user IDs? (y/N) ") : _("Really update the preferences? (y/N) "))) { - if (menu_set_preferences (ctrl, keyblock)) + if (menu_set_preferences (ctrl, keyblock, 0)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; @@ -2604,6 +2605,45 @@ keyedit_quick_set_primary (ctrl_t ctrl, const char *username, } +/* Unattended updating of the preference tro the standard preferences. + * USERNAME specifies the key. This is basically the same as + * gpg --edit-key <<userif> updpref save + */ +void +keyedit_quick_update_pref (ctrl_t ctrl, const char *username) +{ + gpg_error_t err; + KEYDB_HANDLE kdbhd = NULL; + kbnode_t keyblock = NULL; + +#ifdef HAVE_W32_SYSTEM + /* See keyedit_menu for why we need this. */ + check_trustdb_stale (ctrl); +#endif + + err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock); + if (err) + goto leave; + + if (menu_set_preferences (ctrl, keyblock, 1)) + { + merge_keys_and_selfsig (ctrl, keyblock); + err = keydb_update_keyblock (ctrl, kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + } + + leave: + if (err) + write_status_error ("keyedit.updpref", err); + release_kbnode (keyblock); + keydb_release (kdbhd); +} + + /* Find a keyblock by fingerprint because only this uniquely * identifies a key and may thus be used to select a key for * unattended subkey creation os key signing. */ @@ -3316,146 +3356,12 @@ tty_print_notations (int indent, PKT_signature * sig) static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) { - const prefitem_t fake = { 0, 0 }; - const prefitem_t *prefs; - int i; - if (!uid) return; - if (uid->prefs) - prefs = uid->prefs; - else if (verbose) - prefs = &fake; - else - return; - if (verbose) { - int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; - - tty_printf (" "); - tty_printf (_("Cipher: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_SYM) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!openpgp_cipher_test_algo (prefs[i].value) - && prefs[i].value < 100) - tty_printf ("%s", openpgp_cipher_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == CIPHER_ALGO_3DES) - des_seen = 1; - } - } - if (!des_seen) - { - if (any) - tty_printf (", "); - tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); - } - tty_printf ("\n "); - tty_printf (_("AEAD: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_AEAD) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!openpgp_aead_test_algo (prefs[i].value) - && prefs[i].value < 100) - tty_printf ("%s", openpgp_aead_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - } - } - tty_printf ("\n "); - tty_printf (_("Digest: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_HASH) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) - tty_printf ("%s", gcry_md_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == DIGEST_ALGO_SHA1) - sha1_seen = 1; - } - } - if (!sha1_seen) - { - if (any) - tty_printf (", "); - tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); - } - tty_printf ("\n "); - tty_printf (_("Compression: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_ZIP) - { - const char *s = compress_algo_to_string (prefs[i].value); - - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (s && prefs[i].value < 100) - tty_printf ("%s", s); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == COMPRESS_ALGO_NONE) - uncomp_seen = 1; - } - } - if (!uncomp_seen) - { - if (any) - tty_printf (", "); - else - { - tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_ZIP)); - tty_printf (", "); - } - tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); - } - if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) - { - tty_printf ("\n "); - tty_printf (_("Features: ")); - any = 0; - if (uid->flags.mdc) - { - tty_printf ("MDC"); - any = 1; - } - if (!uid->flags.aead) - { - if (any) - tty_printf (", "); - tty_printf ("AEAD"); - } - if (!uid->flags.ks_modify) - { - if (any) - tty_printf (", "); - tty_printf (_("Keyserver no-modify")); - } - } - tty_printf ("\n"); - + show_preferences (uid, 4, -1, 1); if (selfsig) { const byte *pref_ks; @@ -3481,22 +3387,7 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) } else { - tty_printf (" "); - for (i = 0; prefs[i].type; i++) - { - tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : - prefs[i].type == PREFTYPE_AEAD ? 'A' : - prefs[i].type == PREFTYPE_HASH ? 'H' : - prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', - prefs[i].value); - } - if (uid->flags.mdc) - tty_printf (" [mdc]"); - if (uid->flags.aead) - tty_printf (" [aead]"); - if (!uid->flags.ks_modify) - tty_printf (" [no-ks-modify]"); - tty_printf ("\n"); + show_preferences (uid, 4, -1, 0); } } @@ -5212,10 +5103,11 @@ menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock) /* - * Set preferences to new values for the selected user IDs + * Set preferences to new values for the selected user IDs. + * --quick-update-pred calls this with UNATTENDED set. */ static int -menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) +menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended) { PKT_public_key *main_pk; PKT_user_id *uid; @@ -5224,9 +5116,10 @@ menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) int selected, select_all; int modified = 0; - no_primary_warning (pub_keyblock); + if (!unattended) + no_primary_warning (pub_keyblock); - select_all = !count_selected_uids (pub_keyblock); + select_all = unattended? 1 : !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; diff --git a/g10/keyedit.h b/g10/keyedit.h index 1aa95c19d..e397b4a98 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -54,6 +54,7 @@ void keyedit_quick_set_expire (ctrl_t ctrl, char **subkeyfprs); void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); +void keyedit_quick_update_pref (ctrl_t ctrl, const char *username); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock, int print_sec); int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, int rc, kbnode_t keyblock, diff --git a/g10/keygen.c b/g10/keygen.c index 2041d30af..e1fc8d6d0 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -137,6 +137,8 @@ static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static int mdc_available,ks_modify; +static int aead_available; + static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, @@ -363,8 +365,12 @@ keygen_set_std_prefs (const char *string,int personal) byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ + int ocb; char dummy_string[20*4+1]; /* Enough for 20 items. */ + /* Use OCB as default in GnuPG and de-vs mode. */ + ocb = GNUPG; + if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) @@ -489,14 +495,24 @@ keygen_set_std_prefs (const char *string,int personal) if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } - else if (ascii_strcasecmp(tok,"mdc")==0) + else if (!ascii_strcasecmp(tok, "mdc") + || !ascii_strcasecmp(tok, "[mdc]")) mdc=1; - else if (ascii_strcasecmp(tok,"no-mdc")==0) + else if (!ascii_strcasecmp(tok, "no-mdc") + || !ascii_strcasecmp(tok, "[no-mdc]")) mdc=0; - else if (ascii_strcasecmp(tok,"ks-modify")==0) + else if (!ascii_strcasecmp(tok, "ks-modify") + || !ascii_strcasecmp(tok, "[ks-modify]")) modify=1; - else if (ascii_strcasecmp(tok,"no-ks-modify")==0) + else if (!ascii_strcasecmp(tok,"no-ks-modify") + || !ascii_strcasecmp(tok,"[no-ks-modify]")) modify=0; + else if (!ascii_strcasecmp(tok,"aead") + || !ascii_strcasecmp(tok,"[aead]")) + ocb = 1; + else if (!ascii_strcasecmp(tok,"no-aead") + || !ascii_strcasecmp(tok,"[no-aead]")) + ocb = 0; else { log_info (_("invalid item '%s' in preference string\n"),tok); @@ -587,6 +603,7 @@ keygen_set_std_prefs (const char *string,int personal) memcpy (hash_prefs, hash, (nhash_prefs=nhash)); memcpy (zip_prefs, zip, (nzip_prefs=nzip)); mdc_available = mdc; + aead_available = ocb; ks_modify = modify; prefs_initialized = 1; } @@ -595,6 +612,7 @@ keygen_set_std_prefs (const char *string,int personal) return rc; } + /* Return a fake user ID containing the preferences. Caller must free. */ PKT_user_id * @@ -633,6 +651,7 @@ keygen_get_std_prefs(void) uid->prefs[j].value=0; uid->flags.mdc=mdc_available; + uid->flags.aead=aead_available; uid->flags.ks_modify=ks_modify; return uid; @@ -679,6 +698,49 @@ add_feature_mdc (PKT_signature *sig,int enabled) xfree (buf); } + +static void +add_feature_aead (PKT_signature *sig, int enabled) +{ + const byte *s; + size_t n; + int i; + char *buf; + + s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); + if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02)))) + return; /* Already set or cleared */ + + if (!s || !n) + { /* Create a new one */ + n = 1; + buf = xmalloc_clear (n); + } + else + { + buf = xmalloc (n); + memcpy (buf, s, n); + } + + if (enabled) + buf[0] |= 0x02; /* AEAD supported */ + else + buf[0] &= ~0x02; + + /* Are there any bits set? */ + for (i=0; i < n; i++) + if (buf[i]) + break; + + if (i == n) + delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); + else + build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); + + xfree (buf); +} + + static void add_keyserver_modify (PKT_signature *sig,int enabled) { @@ -740,6 +802,14 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } + if (aead_available) /* The only preference is AEAD_ALGO_OCB. */ + build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, "\x02", 1); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD); + } + if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else @@ -756,8 +826,9 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } - /* Make sure that the MDC feature flag is set if needed. */ + /* Make sure that the MDC and AEAD feature flags are set as needed. */ add_feature_mdc (sig,mdc_available); + add_feature_aead (sig, aead_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); diff --git a/g10/keylist.c b/g10/keylist.c index af0ce9d24..e10a4c12f 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -287,6 +287,173 @@ print_card_key_info (estream_t fp, kbnode_t keyblock) #endif /*ENABLE_CARD_SUPPORT*/ +/* Print the preferences line. Allowed values for MODE are: + * -1 - print to the TTY + * 0 - print to stdout. + * 1 - use log_info + */ +void +show_preferences (PKT_user_id *uid, int indent, int mode, int verbose) +{ + estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; + const prefitem_t fake = { 0, 0 }; + const prefitem_t *prefs; + int i; + + if (!uid) + return; + + if (uid->prefs) + prefs = uid->prefs; + else if (verbose) + prefs = &fake; + else + return; + + if (verbose) + { + int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; + + tty_fprintf (fp, "%*s %s", indent, "", _("Cipher: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_SYM) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!openpgp_cipher_test_algo (prefs[i].value) + && prefs[i].value < 100) + tty_fprintf (fp, "%s", openpgp_cipher_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == CIPHER_ALGO_3DES) + des_seen = 1; + } + } + if (!des_seen) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("AEAD: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_AEAD) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!openpgp_aead_test_algo (prefs[i].value) + && prefs[i].value < 100) + tty_fprintf (fp, "%s", openpgp_aead_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + } + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("Digest: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_HASH) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) + tty_fprintf (fp, "%s", gcry_md_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == DIGEST_ALGO_SHA1) + sha1_seen = 1; + } + } + if (!sha1_seen) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("Compression: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_ZIP) + { + const char *s = compress_algo_to_string (prefs[i].value); + + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (s && prefs[i].value < 100) + tty_fprintf (fp, "%s", s); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == COMPRESS_ALGO_NONE) + uncomp_seen = 1; + } + } + if (!uncomp_seen) + { + if (any) + tty_fprintf (fp, ", "); + else + { + tty_fprintf (fp, "%s", + compress_algo_to_string (COMPRESS_ALGO_ZIP)); + tty_fprintf (fp, ", "); + } + tty_fprintf (fp, "%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); + } + if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) + { + tty_fprintf (fp, "\n%*s %s", indent, "", _("Features: ")); + any = 0; + if (uid->flags.mdc) + { + tty_fprintf (fp, "MDC"); + any = 1; + } + if (uid->flags.aead) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "AEAD"); + } + if (!uid->flags.ks_modify) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, _("Keyserver no-modify")); + } + } + tty_fprintf (fp, "\n"); + } + else + { + tty_fprintf (fp, "%*s", indent, ""); + for (i = 0; prefs[i].type; i++) + { + tty_fprintf (fp, " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : + prefs[i].type == PREFTYPE_AEAD ? 'A' : + prefs[i].type == PREFTYPE_HASH ? 'H' : + prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', + prefs[i].value); + } + if (uid->flags.mdc) + tty_fprintf (fp, " [mdc]"); + if (uid->flags.aead) + tty_fprintf (fp, " [aead]"); + if (!uid->flags.ks_modify) + tty_fprintf (fp, " [no-ks-modify]"); + tty_fprintf (fp, "\n"); + } +} + + /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, @@ -1057,6 +1224,11 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); + if ((opt.list_options & LIST_SHOW_PREF_VERBOSE)) + show_preferences (uid, indent+2, 0, 1); + else if ((opt.list_options & LIST_SHOW_PREF)) + show_preferences (uid, indent+2, 0, 0); + if (opt.with_wkd_hash) { char *mbox, *hash, *p; diff --git a/g10/main.h b/g10/main.h index 273ddaaaf..29e0b6961 100644 --- a/g10/main.h +++ b/g10/main.h @@ -233,7 +233,6 @@ void display_online_help( const char *keyword ); /*-- encode.c --*/ gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); -void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); @@ -466,6 +465,7 @@ int cmp_signodes (const void *av, const void *bv); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); +void show_preferences (PKT_user_id *uid, int indent, int mode, int verbose); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); diff --git a/g10/misc.c b/g10/misc.c index 0b19e1a2b..23a627a66 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -687,7 +687,7 @@ openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, *r_noncelen = 15; break; - case AEAD_ALGO_EAX: + case AEAD_ALGO_EAX: /* Only for decryption of some old data. */ *r_mode = MY_GCRY_CIPHER_MODE_EAX; *r_noncelen = 16; break; diff --git a/g10/options.h b/g10/options.h index 4917b7055..ccf7397d0 100644 --- a/g10/options.h +++ b/g10/options.h @@ -89,6 +89,7 @@ struct int list_packets; /* Option --list-packets active. */ int def_cipher_algo; int def_digest_algo; + int force_ocb; int cert_digest_algo; int compress_algo; int compress_level; @@ -350,7 +351,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; /* Compatibility flags */ -/* #define COMPAT_FOO 1 */ +#define COMPAT_VSD_ALLOW_OCB 1 /* Compliance test macors. */ @@ -406,6 +407,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SHOW_SIG_SUBPACKETS (1<<10) #define LIST_SHOW_USAGE (1<<11) #define LIST_SHOW_ONLY_FPR_MBOX (1<<12) +#define LIST_SHOW_PREF (1<<14) +#define LIST_SHOW_PREF_VERBOSE (1<<15) #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) diff --git a/g10/pkclist.c b/g10/pkclist.c index fb8b17620..54326822d 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1648,36 +1648,37 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, return result; } -/* - * Select the MDC flag from the pk_list. We can only use MDC if all - * recipients support this feature. - */ -int -select_mdc_from_pklist (PK_LIST pk_list) + +/* Select the AEAD flag from the pk_list. We can only use AEAD if all + * recipients support this feature. Returns the AEAD to be used or 0 + * if AEAD shall not be used. */ +aead_algo_t +select_aead_from_pklist (PK_LIST pk_list) { - PK_LIST pkr; + pk_list_t pkr; + int aead; - if ( !pk_list ) + if (!pk_list) return 0; for (pkr = pk_list; pkr; pkr = pkr->next) { - int mdc; - if (pkr->pk->user_id) /* selected by user ID */ - mdc = pkr->pk->user_id->flags.mdc; + aead = pkr->pk->user_id->flags.aead; else - mdc = pkr->pk->flags.mdc; - if (!mdc) + aead = pkr->pk->flags.aead; + if (!aead) return 0; /* At least one recipient does not support it. */ } - return 1; /* Can be used. */ + + return AEAD_ALGO_OCB; /* Yes, AEAD can be used. */ } -/* Print a warning for all keys in PK_LIST missing the MDC feature. */ +/* Print a warning for all keys in PK_LIST missing the AEAD feature + * flag or AEAD algorithms. */ void -warn_missing_mdc_from_pklist (PK_LIST pk_list) +warn_missing_aead_from_pklist (PK_LIST pk_list) { PK_LIST pkr; @@ -1686,12 +1687,12 @@ warn_missing_mdc_from_pklist (PK_LIST pk_list) int mdc; if (pkr->pk->user_id) /* selected by user ID */ - mdc = pkr->pk->user_id->flags.mdc; + mdc = pkr->pk->user_id->flags.aead; else - mdc = pkr->pk->flags.mdc; + mdc = pkr->pk->flags.aead; if (!mdc) log_info (_("Note: key %s has no %s feature\n"), - keystr_from_pk (pkr->pk), "MDC"); + keystr_from_pk (pkr->pk), "AEAD"); } } 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) { @@ -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); @@ -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) diff --git a/tests/openpgp/Makefile.am b/tests/openpgp/Makefile.am index 2b06fb18c..2cef2fc34 100644 --- a/tests/openpgp/Makefile.am +++ b/tests/openpgp/Makefile.am @@ -209,7 +209,9 @@ priv_keys = privkeys/50B2D4FA4122C212611048BC5FC31BD44393626E.asc \ privkeys/1E28F20E41B54C2D1234D896096495FF57E08D18.asc \ privkeys/EB33B687EB8581AB64D04852A54453E85F3DF62D.asc \ privkeys/C6A6390E9388CDBAD71EAEA698233FE5E04F001E.asc \ - privkeys/D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc + privkeys/D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3.asc \ + privkeys/891067FFFC6D67D37BD4BFC399191C5F3989D1B5.key \ + privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key sample_keys = samplekeys/README \ samplekeys/ecc-sample-1-pub.asc \ @@ -228,6 +230,7 @@ sample_keys = samplekeys/README \ samplekeys/E657FB607BB4F21C90BB6651BC067AF28BC90111.asc \ samplekeys/rsa-rsa-sample-1.asc \ samplekeys/ed25519-cv25519-sample-1.asc \ + samplekeys/ed25519-cv25519-sample-2.asc \ samplekeys/silent-running.asc \ samplekeys/ssh-dsa.key \ samplekeys/ssh-ecdsa.key \ diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm index b864005ba..8e0631e5c 100644 --- a/tests/openpgp/defs.scm +++ b/tests/openpgp/defs.scm @@ -215,6 +215,9 @@ (define (tr:gpg input args) (tr:spawn input `(,@GPG --output **out** ,@args **in**))) +(define (tr:gpgstatus input args) + (tr:spawn input `(,@GPG --output dummy --status-file **out** ,@args **in**))) + (define (pipe:gpg args) (pipe:spawn `(,@GPG --output - ,@args -))) @@ -410,6 +413,13 @@ "EB33B687EB8581AB64D04852A54453E85F3DF62D" "C6A6390E9388CDBAD71EAEA698233FE5E04F001E" "D69102E0F5AC6B6DB8E4D16DA8E18CF46D88CAE3")) + (for-each + (lambda (name) + (file-copy (in-srcdir "tests" "openpgp" "privkeys" + (string-append name ".key")) + (string-append "private-keys-v1.d/" name ".key"))) + '("891067FFFC6D67D37BD4BFC399191C5F3989D1B5" + "F27FC04CB01723A4CB6F5399F7B86CCD82C0169C")) (log "Importing public demo and test keys") (for-each diff --git a/tests/openpgp/encrypt.scm b/tests/openpgp/encrypt.scm index f59a1f0c1..ef2f7b0bc 100755 --- a/tests/openpgp/encrypt.scm +++ b/tests/openpgp/encrypt.scm @@ -59,3 +59,68 @@ (tr:gpg "" '(--yes --decrypt)) (tr:assert-identity source))) plain-files) + + +(info "Importing additional sample keys for OCB tests") +(for-each + (lambda (name) + (call `(,@GPG --yes --import ,(in-srcdir "tests" "openpgp" "samplekeys" + (string-append name ".asc"))))) + '("ed25519-cv25519-sample-1" + "ed25519-cv25519-sample-2" + "rsa-rsa-sample-1")) + +(for-each-p + "Checking OCB mode" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpg "" `(--yes -er ,"patrice.lumumba")) + (tr:gpg "" '(--yes -d)) + (tr:assert-identity source))) + all-files) + +;; For reference: +;; BEGIN_ENCRYPTION <mdc_method> <sym_algo> [<aead_algo>] + +(for-each-p + "Checking two OCB capable keys" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpgstatus "" `(--yes -e + -r ,"patrice.lumumba" + -r ,"mahsa.amini")) + (tr:call-with-content + (lambda (c) + (unless (string-contains? c "[GNUPG:] BEGIN_ENCRYPTION 0 9 2") + (fail (string-append "Unexpected status: " c))))))) + '("plain-1")) + +(for-each-p + "Checking two OCB capable keys plus one not capable" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpgstatus "" `(--yes -o out -e + -r ,"patrice.lumumba" + -r ,"mahsa.amini" + -r ,"steve.biko")) + (tr:call-with-content + (lambda (c) + (unless (string-contains? c "[GNUPG:] BEGIN_ENCRYPTION 2 9") + (fail (string-append "Unexpected status: " c))))))) + '("plain-1")) + +(for-each-p + "Checking non OCB capable key with --force-ocb" + (lambda (source) + (tr:do + (tr:open source) + (tr:gpgstatus "" `(--yes -e --force-ocb + -r ,"steve.biko")) + (tr:call-with-content + (lambda (c) + (unless (string-contains? c "[GNUPG:] BEGIN_ENCRYPTION 0 9 2") + (fail (string-append "Unexpected status: " c))))))) + '("plain-1")) diff --git a/tests/openpgp/privkeys/891067FFFC6D67D37BD4BFC399191C5F3989D1B5.key b/tests/openpgp/privkeys/891067FFFC6D67D37BD4BFC399191C5F3989D1B5.key new file mode 100644 index 000000000..3e805d49f --- /dev/null +++ b/tests/openpgp/privkeys/891067FFFC6D67D37BD4BFC399191C5F3989D1B5.key @@ -0,0 +1,5 @@ +Created: 20220916T120000 +Key: (private-key (ecc (curve Curve25519)(flags djb-tweak)(q + #409651F6DD19C8F562792274BCE044F8916609FBDA25EE3DFA21207DCE8CBA0C63#) + (d #778955D781825551C8B8025DF6A9D7A00613331DE35711F56C65676A98E565F8#) + )) diff --git a/tests/openpgp/privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key b/tests/openpgp/privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key new file mode 100644 index 000000000..544643807 --- /dev/null +++ b/tests/openpgp/privkeys/F27FC04CB01723A4CB6F5399F7B86CCD82C0169C.key @@ -0,0 +1,5 @@ +Created: 20220916T120000 +Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q + #403905D615CA9A98D674F1CC7AA8B5E9F948D7D2FB2E7536ED6027B014B1F948E6#) + (d #F1E5A1387736A9BD0976AA1FA1D217C3A75EC636605EA8EEAF3C84A9C13E01B4#) + )) diff --git a/tests/openpgp/samplekeys/README b/tests/openpgp/samplekeys/README index f8a7e9ed7..f850ccf08 100644 --- a/tests/openpgp/samplekeys/README +++ b/tests/openpgp/samplekeys/README @@ -17,6 +17,7 @@ E657FB607BB4F21C90BB6651BC067AF28BC90111.asc Key with subkeys (no protection) pgp-desktop-skr.asc Secret key with subkeys w/o signatures rsa-rsa-sample-1.asc RSA+RSA sample key (no passphrase) ed25519-cv25519-sample-1.asc Ed25519+CV25519 sample key (no passphrase) +ed25519-cv25519-sample-2.asc Ed25519+CV25519 sample key (no passphrase) silent-running.asc Collection of sample secret keys (no passphrases) rsa-primary-auth-only.pub.asc rsa2408 primary only, usage: cert,auth rsa-primary-auth-only.sec.asc Ditto but the secret keyblock. diff --git a/tests/openpgp/samplekeys/ed25519-cv25519-sample-1.asc b/tests/openpgp/samplekeys/ed25519-cv25519-sample-1.asc index 54d204427..53e2440ba 100644 --- a/tests/openpgp/samplekeys/ed25519-cv25519-sample-1.asc +++ b/tests/openpgp/samplekeys/ed25519-cv25519-sample-1.asc @@ -1,21 +1,21 @@ pub ed25519 2016-06-22 [SC] B21DEAB4F875FB3DA42F1D1D139563682A020D0A Keygrip = 1E28F20E41B54C2D1234D896096495FF57E08D18 -uid [ unknown] [email protected] +uid [email protected] sub cv25519 2016-06-22 [E] 8D0221D9B2877A741D69AC4E9185878E4FCD74C0 Keygrip = EB33B687EB8581AB64D04852A54453E85F3DF62D -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v2 mDMEV2o9XRYJKwYBBAHaRw8BAQdAZ8zkuQDL9x7rcvvoo6s3iEF1j88Dknd9nZhL -nTEoBRm0G3BhdHJpY2UubHVtdW1iYUBleGFtcGxlLm5ldIh5BBMWCAAhBQJXaj1d -AhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEBOVY2gqAg0KmQ0BAMUNzAlT -OzG7tolSI92lhePi5VqutdqTEQTyYYWi1aEsAP0YfiuosNggTc0oRTSz46S3i0Qj -AlpXwfU00888yIreDbg4BFdqPY0SCisGAQQBl1UBBQEBB0AWeeZlz31O4qTmIKr3 -CZhlRUXZFxc3YKyoCXyIZBBRawMBCAeIYQQYFggACQUCV2o9jQIbDAAKCRATlWNo -KgINCsuFAP9BplWl813pi779V8OMsRGs/ynyihnOESft/H8qlM8PDQEAqIUPpIty -OX/OBFy2RIlIi7J1bTp9RzcbzQ/4Fk4hWQQ= -=qRfF +nTEoBRm0G3BhdHJpY2UubHVtdW1iYUBleGFtcGxlLm5ldIiTBBMWCAA7AhsDAheA +FiEEsh3qtPh1+z2kLx0dE5VjaCoCDQoFAmNkyZ0FCwkIBwICIgIGFQgJCgsCBBYC +AwECHgcACgkQE5VjaCoCDQoKxwEAyVSPe4kwcvjlL9iZYftqwmCQpL6Sd7smgBdb +naqvAEMA/RrGBjSTGzTvFMVlIcT0Jr1uPVHig7twPnpzbL1uWUwLuDgEV2o9jRIK +KwYBBAGXVQEFAQEHQBZ55mXPfU7ipOYgqvcJmGVFRdkXFzdgrKgJfIhkEFFrAwEI +B4hhBBgWCAAJBQJXaj2NAhsMAAoJEBOVY2gqAg0Ky4UA/0GmVaXzXemLvv1Xw4yx +Eaz/KfKKGc4RJ+38fyqUzw8NAQCohQ+ki3I5f84EXLZEiUiLsnVtOn1HNxvND/gW +TiFZBA== +=u4Iu -----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/openpgp/samplekeys/ed25519-cv25519-sample-2.asc b/tests/openpgp/samplekeys/ed25519-cv25519-sample-2.asc new file mode 100644 index 000000000..2e7285195 --- /dev/null +++ b/tests/openpgp/samplekeys/ed25519-cv25519-sample-2.asc @@ -0,0 +1,21 @@ +pub ed25519 2022-09-16 [SC] + 5F1438D784C8C68400645518AE08687BF38AFFF3 + Keygrip = F27FC04CB01723A4CB6F5399F7B86CCD82C0169C +uid [email protected] +sub cv25519 2022-09-16 [E] + FFE7440568492D986F3B88BD9E64CB003A8D6449 + Keygrip = 891067FFFC6D67D37BD4BFC399191C5F3989D1B5 + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYyRlQBYJKwYBBAHaRw8BAQdAOQXWFcqamNZ08cx6qLXp+UjX0vsudTbtYCew +FLH5SOa0F21haHNhLmFtaW5pQGV4YW1wbGUubmV0iJMEExYKADsWIQRfFDjXhMjG +hABkVRiuCGh784r/8wUCYyRlQAIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX +gAAKCRCuCGh784r/8wYzAQDTikkZd/G/o1DtfGq/k0R9ctcZCD9vHKH3PNj2atfX +cwEAt5zFYyEe2OPzJ5HYffOPhcyK2kPsvkerLfdXy/K8QAe4OARjJGVAEgorBgEE +AZdVAQUBAQdAllH23RnI9WJ5InS84ET4kWYJ+9ol7j36ISB9zoy6DGMDAQgHiHgE +GBYKACAWIQRfFDjXhMjGhABkVRiuCGh784r/8wUCYyRlQAIbDAAKCRCuCGh784r/ +89lTAQDpupXGKLSlga2qHgtaud47oU5edY48MZ7CBnFByi5IAQEA2nJpUsVuaQl2 +XSURaPTUi0C98ny61kwGcVtOcTFpPgY= +=r11D +-----END PGP PUBLIC KEY BLOCK----- |