diff options
Diffstat (limited to 'g10')
40 files changed, 2570 insertions, 714 deletions
diff --git a/g10/build-packet.c b/g10/build-packet.c index 19a13760a..606c5c2d8 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -433,7 +433,7 @@ sos_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten) * Write an opaque string to the output stream without length info. */ gpg_error_t -gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) +gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a) { int rc; @@ -452,6 +452,45 @@ gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) } +/* + * Write an opaque MPI string with a four-byte octet count to the + * output stream. If R_NWRITTEN is not NULL the number of written + * bytes is stored there. OUT may be NULL in which case only + * R_NWRITTEN is updated and error checking is done. + */ +gpg_error_t +gpg_mpi_write_opaque_32 (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten) +{ + gpg_error_t err; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits, nbytes; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + nbytes = (nbits + 7)/8; + if (out) + { + write_32 (out, nbytes); + err = p ? iobuf_write (out, p, nbytes) : 0; + } + else + err = 0; + if (r_nwritten) + *r_nwritten = 4 + (p? nbytes : 0); + } + else + { + err = gpg_error (GPG_ERR_BAD_MPI); + if (r_nwritten) + *r_nwritten = 0; + } + + return err; +} + + /* Calculate the length of a packet described by PKT. */ u32 calc_packet_length( PACKET *pkt ) @@ -639,8 +678,14 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) { if ( (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0)) || (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0)) - || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) - err = gpg_mpi_write_nohdr (a, pk->pkey[i]); + || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)) + || (pk->pubkey_algo == PUBKEY_ALGO_KYBER && (i == 0))) + err = gpg_mpi_write_opaque_nohdr (a, pk->pkey[i]); + else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + { + /* Write a four-octet count prefixed Kyber public key. */ + err = gpg_mpi_write_opaque_32 (a, pk->pkey[2], NULL); + } else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) @@ -779,9 +824,15 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) for (j=i; j < nskey; j++ ) { - if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA - || pk->pubkey_algo == PUBKEY_ALGO_EDDSA - || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && j == 4) + { + if ((err=gpg_mpi_write_opaque_32 (NULL,pk->pkey[j], &n))) + goto leave; + } + else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_EDDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH + || pk->pubkey_algo == PUBKEY_ALGO_KYBER) { if ((err = sos_write (NULL, pk->pkey[j], &n))) goto leave; @@ -798,16 +849,26 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) } for ( ; i < nskey; i++ ) - if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA - || pk->pubkey_algo == PUBKEY_ALGO_EDDSA - || pk->pubkey_algo == PUBKEY_ALGO_ECDH) - { - if ((err = sos_write (a, pk->pkey[i], NULL))) - goto leave; - } - else - if ((err = gpg_mpi_write (a, pk->pkey[i], NULL))) - goto leave; + { + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 4) + { + err = gpg_mpi_write_opaque_32 (a, pk->pkey[i], NULL); + if (err) + goto leave; + } + else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_EDDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + if ((err = sos_write (a, pk->pkey[i], NULL))) + goto leave; + } + else + { + if ((err = gpg_mpi_write (a, pk->pkey[i], NULL))) + goto leave; + } + } write_16 (a, ski->csum ); } @@ -921,9 +982,19 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) for (i=0; i < n && !rc ; i++ ) { - if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) - rc = gpg_mpi_write_nohdr (a, enc->data[i]); - else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH) + /* For Kyber we need to insert the algo before the final data + * element because it is not stored in the data array. */ + if (enc->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + iobuf_put (a, enc->seskey_algo); + + if (i == 1 && enc->pubkey_algo == PUBKEY_ALGO_ECDH) + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[i]); + else if (i == 1 && enc->pubkey_algo == PUBKEY_ALGO_KYBER) + rc = gpg_mpi_write_opaque_32 (a, enc->data[i], NULL); + else if (i == 2 && enc->pubkey_algo == PUBKEY_ALGO_KYBER) + rc = gpg_mpi_write_opaque_nohdr (a, enc->data[i]); + else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH + || enc->pubkey_algo == PUBKEY_ALGO_KYBER) rc = sos_write (a, enc->data[i], NULL); else rc = gpg_mpi_write (a, enc->data[i], NULL); @@ -1748,6 +1819,72 @@ sig_to_notation(PKT_signature *sig) return list; } + +/* Return a list of notation data matching NAME. The caller needs to + * to free the list using free_notation. Other than sig_to_notation + * this function does not return the notation in human readable format + * but always returns the raw data. The human readable flag is set + * anyway set but .value is always NULL. */ +struct notation * +search_sig_notations (PKT_signature *sig, const char *name) +{ + const byte *p; + size_t len; + int seq = 0; + int crit; + notation_t list = NULL; + + while((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_NOTATION, &len, &seq, &crit))) + { + int n1,n2; + struct notation *n=NULL; + + if (len < 8) + { + log_info (_("WARNING: invalid notation data found\n")); + continue; + } + + /* name length. */ + n1=(p[4]<<8)|p[5]; + /* value length. */ + n2=(p[6]<<8)|p[7]; + + if (8 + n1 + n2 != len) + { + log_info (_("WARNING: invalid notation data found\n")); + continue; + } + + if (!name) + ; /* Return everything. */ + else if (n1 != strlen (name) || memcmp (p+8, name, n1)) + continue; /* Not the requested name. */ + + + n = xmalloc_clear (sizeof *n); + n->name = xmalloc (n1+1); + + memcpy (n->name,p + 8, n1); + n->name[n1]='\0'; + + /* In any case append a Nul. */ + n->bdat = xmalloc (n2+1); + memcpy (n->bdat, p + 8 + n1, n2); + n->bdat[n2] = '\0'; + n->blen = n2; + n->flags.human = !!(p[0] & 0x80); + + n->flags.critical = crit; + + n->next = list; + list = n; + } + + return list; +} + + /* Release the resources associated with the *list* of notations. To release a single notation, make sure that notation->next is NULL. */ diff --git a/g10/call-agent.c b/g10/call-agent.c index 69207566e..cfd4c086a 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -251,7 +251,8 @@ start_agent (ctrl_t ctrl, int flag_for_card) opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, NULL); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { @@ -1079,6 +1080,12 @@ agent_keytotpm (ctrl_t ctrl, const char *hexgrip) snprintf(line, DIM(line), "KEYTOTPM %s\n", hexgrip); + if (strchr (hexgrip, ',')) + { + log_error ("storing a part of a dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + rc = start_agent (ctrl, 0); if (rc) return rc; @@ -1108,6 +1115,13 @@ agent_keytocard (const char *hexgrip, int keyno, int force, memset (&parm, 0, sizeof parm); + if (strchr (hexgrip, ',')) + { + log_error ("storing a part of a dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + + snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s%s%s", force?"--force ": "", hexgrip, serialno, keyno, timestamp, ecdh_param_str? " ":"", ecdh_param_str? ecdh_param_str:""); @@ -2239,9 +2253,9 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; - char *hexgrip; - + char *hexgrip, *p; struct keyinfo_data_parm_s keyinfo; + int result, result2; memset (&keyinfo, 0, sizeof keyinfo); @@ -2252,28 +2266,64 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) err = hexkeygrip_from_pk (pk, &hexgrip); if (err) return 0; + if ((p=strchr (hexgrip, ','))) + *p++ = 0; snprintf (line, sizeof line, "KEYINFO %s", hexgrip); - xfree (hexgrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); xfree (keyinfo.serialno); if (err) - return 0; + result = 0; + else if (keyinfo.card_available) + result = 4; + else if (keyinfo.passphrase_cached) + result = 3; + else if (keyinfo.is_smartcard) + result = 2; + else + result = 1; - if (keyinfo.card_available) - return 4; + if (!p) + { + xfree (hexgrip); + return result; /* Not a dual algo - we are ready. */ + } - if (keyinfo.passphrase_cached) - return 3; + /* Now check the second keygrip. */ + memset (&keyinfo, 0, sizeof keyinfo); + snprintf (line, sizeof line, "KEYINFO %s", p); - if (keyinfo.is_smartcard) - return 2; + err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, + keyinfo_status_cb, &keyinfo); + xfree (keyinfo.serialno); + if (err) + result2 = 0; + else if (keyinfo.card_available) + result2 = 4; + else if (keyinfo.passphrase_cached) + result2 = 3; + else if (keyinfo.is_smartcard) + result2 = 2; + else + result2 = 1; - return 1; + xfree (hexgrip); + + if (result == result2) + return result; /* Both keys have the same status. */ + else if (!result && result2) + return 0; /* Only first key available - return no key. */ + else if (result && !result2) + return 0; /* Only second key not availabale - return no key. */ + else if (result == 4 || result == 2) + return result; /* First key on card - don't care where the second is. */ + else + return result; } + /* Ask the agent whether a secret key is available for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ gpg_error_t @@ -2285,6 +2335,8 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) kbnode_t kbctx, node; int nkeys; /* (always zero in secret_keygrips mode) */ unsigned char grip[KEYGRIP_LEN]; + unsigned char grip2[KEYGRIP_LEN]; + int grip2_valid; const unsigned char *s; unsigned int n; @@ -2340,22 +2392,30 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) if (ctrl && ctrl->secret_keygrips) { /* We got an array with all secret keygrips. Check this. */ - err = keygrip_from_pk (node->pkt->pkt.public_key, grip); + err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; + err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); + if (err && gpg_err_code (err) != GPG_ERR_FALSE) + return err; + grip2_valid = !err; + for (s=ctrl->secret_keygrips, n = 0; n < ctrl->secret_keygrips_len; s += 20, n += 20) { if (!memcmp (s, grip, 20)) return 0; + if (grip2_valid && !memcmp (s, grip2, 20)) + return 0; } err = gpg_error (GPG_ERR_NO_SECKEY); /* Keep on looping over the keyblock. Never bump nkeys. */ } else { - if (nkeys && ((p - line) + 41) > (ASSUAN_LINELENGTH - 2)) + if (nkeys + && ((p - line) + 4*KEYGRIP_LEN+1+1) > (ASSUAN_LINELENGTH - 2)) { err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); @@ -2365,13 +2425,24 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) nkeys = 0; } - err = keygrip_from_pk (node->pkt->pkt.public_key, grip); + err = keygrip_from_pk (node->pkt->pkt.public_key, grip, 0); if (err) return err; *p++ = ' '; bin2hex (grip, 20, p); p += 40; nkeys++; + + err = keygrip_from_pk (node->pkt->pkt.public_key, grip2, 1); + if (err && gpg_err_code (err) != GPG_ERR_FALSE) + return err; + if (!err) /* Add the second keygrip from dual algos. */ + { + *p++ = ' '; + bin2hex (grip2, 20, p); + p += 40; + nkeys++; + } } } @@ -2398,6 +2469,7 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, gpg_error_t err; char line[ASSUAN_LINELENGTH]; struct keyinfo_data_parm_s keyinfo; + const char *s; memset (&keyinfo, 0,sizeof keyinfo); @@ -2407,10 +2479,20 @@ agent_get_keyinfo (ctrl_t ctrl, const char *hexkeygrip, if (err) return err; - if (!hexkeygrip || strlen (hexkeygrip) != 40) + /* FIXME: Support dual keys. Maybe under the assumption that the + * first key might be on a card. */ + if (!hexkeygrip) + return gpg_error (GPG_ERR_INV_VALUE); + s = strchr (hexkeygrip, ','); + if (!s) + s = hexkeygrip + strlen (hexkeygrip); + if (s - hexkeygrip != 40) return gpg_error (GPG_ERR_INV_VALUE); - snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip); + /* Note that for a dual algo we only get info for the first key. + * FIXME: We need to see how we can show the status of the second + * key in a key listing. */ + snprintf (line, DIM(line), "KEYINFO %.40s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &keyinfo); @@ -2796,6 +2878,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, membuf_t data; size_t n, len; char *p, *buf, *endp; + const char *keygrip2 = NULL; struct default_inq_parm_s dfltparm; memset (&dfltparm, 0, sizeof dfltparm); @@ -2804,13 +2887,26 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, dfltparm.keyinfo.mainkeyid = mainkeyid; dfltparm.keyinfo.pubkey_algo = pubkey_algo; - if (!keygrip || strlen(keygrip) != 40 - || !s_ciphertext || !r_buf || !r_buflen || !r_padding) + if (!keygrip || !s_ciphertext || !r_buf || !r_buflen || !r_padding) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; *r_padding = -1; + /* Parse the keygrip in case of a dual algo. */ + keygrip2 = strchr (keygrip, ','); + if (!keygrip2) + keygrip2 = keygrip + strlen (keygrip); + if (keygrip2 - keygrip != 40) + return gpg_error (GPG_ERR_INV_VALUE); + if (*keygrip2) + { + keygrip2++; + if (strlen (keygrip2) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + } + + err = start_agent (ctrl, 0); if (err) return err; @@ -2821,11 +2917,19 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, if (err) return err; - snprintf (line, sizeof line, "SETKEY %s", keygrip); + snprintf (line, sizeof line, "SETKEY %.40s", keygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; + if (*keygrip2) + { + snprintf (line, sizeof line, "SETKEY --another %.40s", keygrip2); + err = assuan_transact (agent_ctx, line, NULL, NULL,NULL,NULL,NULL,NULL); + if (err) + return err; + } + if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); @@ -2844,7 +2948,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, err = make_canon_sexp (s_ciphertext, &parm.ciphertext, &parm.ciphertextlen); if (err) return err; - err = assuan_transact (agent_ctx, "PKDECRYPT", + err = assuan_transact (agent_ctx, + *keygrip2? "PKDECRYPT --kem=PQC-PGP":"PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &parm, padding_info_cb, r_padding); @@ -3174,6 +3279,7 @@ agent_delete_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, return err; } + /* FIXME: Shall we add support to DELETE_KEY for dual keys? */ snprintf (line, DIM(line), "DELETE_KEY%s %s", force? " --force":"", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c index c47bf0928..d00f61450 100644 --- a/g10/call-dirmngr.c +++ b/g10/call-dirmngr.c @@ -166,7 +166,8 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx) err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL /*gpg_status2*/, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c index 121b7aa2a..bc3806f0b 100644 --- a/g10/call-keyboxd.c +++ b/g10/call-keyboxd.c @@ -94,8 +94,6 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl) log_error ("oops: trying to cleanup an active keyboxd context\n"); else { - kbx_client_data_release (kbl->kcd); - kbl->kcd = NULL; if (kbl->ctx && in_transaction) { /* This is our hack to commit the changes done during a @@ -112,6 +110,15 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl) } assuan_release (kbl->ctx); kbl->ctx = NULL; + /* + * Since there may be pipe output FD sent to the server (so + * that it can receive data through the pipe), we should + * release the assuan connection before releasing KBL->KCD. + * This way, the data receiving thread can finish cleanly, + * and we can join the thread. + */ + kbx_client_data_release (kbl->kcd); + kbl->kcd = NULL; } xfree (kbl); } @@ -143,7 +150,8 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, - opt.autostart, opt.verbose, DBG_IPC, + opt.autostart?ASSHELP_FLAG_AUTOSTART:0, + opt.verbose, DBG_IPC, NULL, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) { @@ -223,7 +231,7 @@ open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl) return err; } - err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 1); + err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 0); if (err) { assuan_release (kbl->ctx); diff --git a/g10/card-util.c b/g10/card-util.c index a66c2e3de..7df505f62 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -28,9 +28,7 @@ # include <readline/readline.h> #endif /*HAVE_LIBREADLINE*/ -#if GNUPG_MAJOR_VERSION != 1 # include "gpg.h" -#endif /*GNUPG_MAJOR_VERSION != 1*/ #include "../common/util.h" #include "../common/i18n.h" #include "../common/ttyio.h" @@ -39,11 +37,7 @@ #include "main.h" #include "keyserver-internal.h" -#if GNUPG_MAJOR_VERSION == 1 -# include "cardglue.h" -#else /*GNUPG_MAJOR_VERSION!=1*/ -# include "call-agent.h" -#endif /*GNUPG_MAJOR_VERSION!=1*/ +#include "call-agent.h" #define CONTROL_D ('D' - 'A' + 1) @@ -949,14 +943,6 @@ get_data_from_file (const char *fname, char **r_buffer) *r_buffer = NULL; fp = es_fopen (fname, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif if (!fp) { tty_printf (_("can't open '%s': %s\n"), fname, strerror (errno)); @@ -992,14 +978,6 @@ put_data_to_file (const char *fname, const void *buffer, size_t length) estream_t fp; fp = es_fopen (fname, "wb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif if (!fp) { tty_printf (_("can't create '%s': %s\n"), fname, strerror (errno)); diff --git a/g10/dearmor.c b/g10/dearmor.c index c0bd9ecf6..f6bb59ef6 100644 --- a/g10/dearmor.c +++ b/g10/dearmor.c @@ -63,7 +63,7 @@ dearmor_file( const char *fname ) push_armor_filter ( afx, inp ); - if( (rc = open_outfile (-1, fname, 0, 0, &out)) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, fname, 0, 0, &out)) ) goto leave; iobuf_copy (out, inp); @@ -107,7 +107,7 @@ enarmor_file( const char *fname ) } - if( (rc = open_outfile (-1, fname, 1, 0, &out )) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, fname, 1, 0, &out )) ) goto leave; afx->what = 4; diff --git a/g10/decrypt.c b/g10/decrypt.c index cb9e36a93..b30359af4 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -100,7 +100,8 @@ decrypt_message (ctrl_t ctrl, const char *filename) /* Same as decrypt_message but takes a file descriptor for input and output. */ gpg_error_t -decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd) +decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, + gnupg_fd_t output_fd) { #ifdef HAVE_W32_SYSTEM /* No server mode yet. */ @@ -138,13 +139,25 @@ decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd) return err; } - opt.outfp = es_fdopen_nc (output_fd, "wb"); + if (is_secured_file (output_fd)) + { + char xname[64]; + + err = gpg_error (GPG_ERR_EPERM); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd)); + log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); + iobuf_close (fp); + release_progress_context (pfx); + return err; + } + + opt.outfp = open_stream_nc (output_fd, "w"); if (!opt.outfp) { char xname[64]; err = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", output_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (output_fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); iobuf_close (fp); release_progress_context (pfx); diff --git a/g10/ecdh.c b/g10/ecdh.c index eb14154a1..279508bec 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -156,11 +156,11 @@ build_kdf_params (unsigned char kdf_params[256], size_t *r_size, return gpg_error_from_syserror (); /* variable-length field 1, curve name OID */ - err = gpg_mpi_write_nohdr (obuf, pkey[0]); + err = gpg_mpi_write_opaque_nohdr (obuf, pkey[0]); /* fixed-length field 2 */ iobuf_put (obuf, PUBKEY_ALGO_ECDH); /* variable-length field 3, KDF params */ - err = (err ? err : gpg_mpi_write_nohdr (obuf, pkey[2])); + err = (err ? err : gpg_mpi_write_opaque_nohdr (obuf, pkey[2])); /* fixed-length field 4 */ iobuf_write (obuf, "Anonymous Sender ", 20); /* fixed-length field 5, recipient fp (or first 20 octets of fp) */ @@ -524,8 +524,7 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], size_t nbytes; byte *data_buf; int data_buf_size; - byte *in; - const void *p; + const unsigned char *p; unsigned int nbits; *r_result = NULL; @@ -538,7 +537,7 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], nbytes = (nbits+7)/8; data_buf_size = nbytes; - if ((data_buf_size & 7) != 1) + if ((data_buf_size & 7) != 1 || data_buf_size <= 1 + 8) { log_error ("can't use a shared secret of %d bytes for ecdh\n", data_buf_size); @@ -546,7 +545,10 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], return gpg_error (GPG_ERR_BAD_DATA); } - data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8); + /* The first octet is for length. It's longer than the result + because of one additional block of AESWRAP. */ + data_buf_size -= 1 + 8; + data_buf = xtrymalloc_secure (data_buf_size); if (!data_buf) { err = gpg_error_from_syserror (); @@ -560,22 +562,18 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } - memcpy (data_buf, p, nbytes); - if (data_buf[0] != nbytes-1) + if (p[0] != nbytes-1) { log_error ("ecdh inconsistent size\n"); xfree (data_buf); gcry_cipher_close (hd); return gpg_error (GPG_ERR_BAD_MPI); } - in = data_buf+data_buf_size; - data_buf_size = data_buf[0]; if (DBG_CRYPTO) - log_printhex (data_buf+1, data_buf_size, "ecdh decrypting :"); + log_printhex (p+1, nbytes-1, "ecdh decrypting :"); - err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, - data_buf_size); + err = gcry_cipher_decrypt (hd, data_buf, data_buf_size, p+1, nbytes-1); gcry_cipher_close (hd); if (err) { @@ -585,10 +583,8 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], return err; } - data_buf_size -= 8; - if (DBG_CRYPTO) - log_printhex (in, data_buf_size, "ecdh decrypted to :"); + log_printhex (data_buf, data_buf_size, "ecdh decrypted to :"); /* Padding is removed later. */ /* if (in[data_buf_size-1] > 8 ) */ @@ -598,7 +594,8 @@ pk_ecdh_decrypt (gcry_mpi_t *r_result, const byte sk_fp[MAX_FINGERPRINT_LEN], /* return gpg_error (GPG_ERR_BAD_KEY); */ /* } */ - err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, in, data_buf_size, NULL); + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, data_buf, + data_buf_size, NULL); xfree (data_buf); if (err) { diff --git a/g10/encrypt.c b/g10/encrypt.c index b335b9797..8ce6164ce 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -507,7 +507,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /**/ : "CFB"); } - if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out ))) + if (rc || (rc = open_outfile (GNUPG_INVALID_FD, filename, opt.armor? 1:0, + 0, &out ))) { iobuf_cancel (inp); xfree (cfx.dek); @@ -757,15 +758,15 @@ write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in - * PROVIDED_PKS; if not the function builds a list of keys on its own. + * PROVIDED_KEYS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the * not yet finished server.c. */ int -encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, +encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, - int outputfd) + gnupg_fd_t outputfd) { iobuf_t inp = NULL; iobuf_t out = NULL; @@ -783,7 +784,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, PK_LIST pk_list; int do_compress; - if (filefd != -1 && filename) + if (filefd != GNUPG_INVALID_FD && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ do_compress = !!opt.compress_algo; @@ -814,7 +815,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, /* Prepare iobufs. */ #ifdef HAVE_W32_SYSTEM - if (filefd == -1) + if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else { @@ -822,7 +823,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, gpg_err_set_errno (ENOSYS); } #else - if (filefd == -1) + if (filefd == GNUPG_INVALID_FD) inp = iobuf_open (filename); else inp = iobuf_fdopen_nc (filefd, "rb"); @@ -840,8 +841,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, char xname[64]; rc = gpg_error_from_syserror (); - if (filefd != -1) - snprintf (xname, sizeof xname, "[fd %d]", filefd); + if (filefd != GNUPG_INVALID_FD) + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (filefd)); else if (!filename) strcpy (xname, "[stdin]"); else @@ -1121,6 +1122,7 @@ write_pubkey_enc (ctrl_t ctrl, enc->pubkey_algo = pk->pubkey_algo; keyid_from_pk( pk, enc->keyid ); enc->throw_keyid = throw_keyid; + enc->seskey_algo = dek->algo; /* (Used only by PUBKEY_ALGO_KYBER.) */ /* Okay, what's going on: We have the session key somewhere in * the structure DEK and want to encode this session key in an @@ -1136,7 +1138,7 @@ write_pubkey_enc (ctrl_t ctrl, * build_packet(). */ frame = encode_session_key (pk->pubkey_algo, dek, pubkey_nbits (pk->pubkey_algo, pk->pkey)); - rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey); + rc = pk_encrypt (pk, frame, dek->algo, enc->data); gcry_mpi_release (frame); if (rc) log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) ); @@ -1224,7 +1226,8 @@ encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) } line[strlen(line)-1] = '\0'; print_file_status(STATUS_FILE_START, line, 2); - rc = encrypt_crypt (ctrl, -1, line, remusr, 0, NULL, -1); + rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, line, remusr, + 0, NULL, GNUPG_INVALID_FD); if (rc) log_error ("encryption of '%s' failed: %s\n", print_fname_stdin(line), gpg_strerror (rc) ); @@ -1236,7 +1239,8 @@ encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr) while (nfiles--) { print_file_status(STATUS_FILE_START, *files, 2); - if ( (rc = encrypt_crypt (ctrl, -1, *files, remusr, 0, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, *files, remusr, + 0, NULL, GNUPG_INVALID_FD))) log_error("encryption of '%s' failed: %s\n", print_fname_stdin(*files), gpg_strerror (rc) ); write_status( STATUS_FILE_DONE ); diff --git a/g10/export.c b/g10/export.c index 224847e0f..2417edef1 100644 --- a/g10/export.c +++ b/g10/export.c @@ -428,7 +428,7 @@ do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, memset( &zfx, 0, sizeof zfx); - rc = open_outfile (-1, NULL, 0, !!secret, &out ); + rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, !!secret, &out); if (rc) return rc; @@ -1961,6 +1961,11 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, err = 0; continue; } + if (strchr (hexgrip, ',')) + { + log_error ("exporting a secret dual key is not yet supported\n"); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } xfree (serialno); serialno = NULL; @@ -2707,18 +2712,18 @@ export_one_ssh_key (estream_t fp, PKT_public_key *pk) blob = get_membuf (&mb, &bloblen); if (blob) { - struct b64state b64_state; + gpgrt_b64state_t b64_state; es_fprintf (fp, "%s ", identifier); - err = b64enc_start_es (&b64_state, fp, ""); - if (err) + b64_state = gpgrt_b64enc_start (fp, ""); + if (!b64_state) { xfree (blob); goto leave; } - err = b64enc_write (&b64_state, blob, bloblen); - b64enc_finish (&b64_state); + err = gpgrt_b64enc_write (b64_state, blob, bloblen); + gpgrt_b64enc_finish (b64_state); es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL)); xfree (blob); @@ -2962,7 +2967,7 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) int pkalgo; int i; gcry_mpi_t keyparam[10] = { NULL }; - struct b64state b64_state; + gpgrt_b64state_t b64_state; init_membuf_secure (&mb, 1024); init_membuf_secure (&mb2, 1024); @@ -3140,11 +3145,11 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) goto leave; } - err = b64enc_start_es (&b64_state, fp, "OPENSSH PRIVATE_KEY"); - if (err) + b64_state = gpgrt_b64enc_start (fp, "OPENSSH PRIVATE_KEY"); + if (!b64_state) goto leave; - err = b64enc_write (&b64_state, blob, bloblen); - b64enc_finish (&b64_state); + err = gpgrt_b64enc_write (b64_state, blob, bloblen); + gpgrt_b64enc_finish (b64_state); if (err) goto leave; diff --git a/g10/filter.h b/g10/filter.h index 4b4fc55ff..321b553dc 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -29,6 +29,9 @@ typedef struct { size_t maxbuf_size; } md_filter_context_t; +typedef struct md_thd_filter_context *md_thd_filter_context_t; +void md_thd_filter_set_md (md_thd_filter_context_t mfx, gcry_md_hd_t md); + typedef struct { int refcount; /* Initialized to 1. */ @@ -165,6 +168,7 @@ typedef struct { /*-- mdfilter.c --*/ int md_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); +int md_thd_filter( void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); void free_md_filter_context( md_filter_context_t *mfx ); /*-- armor.c --*/ diff --git a/g10/getkey.c b/g10/getkey.c index ce59628a0..f2d1e7d7b 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -3779,6 +3779,16 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, continue; } + if (opt.flags.require_pqc_encryption + && (req_usage & PUBKEY_USAGE_ENC) + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + { + if (DBG_LOOKUP) + log_debug ("\tsubkey is not quantum-resistant\n"); + continue; + } + + if (want_secret) { int secret_key_avail = agent_probe_secret_key (NULL, pk); @@ -3857,6 +3867,13 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } + else if (opt.flags.require_pqc_encryption + && (req_usage & PUBKEY_USAGE_ENC) + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + { + if (DBG_LOOKUP) + log_debug ("\tprimary key is not quantum-resistant\n"); + } else /* Okay. */ { if (DBG_LOOKUP) @@ -207,6 +207,7 @@ enum cmd_and_opt_values oWithV5Fingerprint, oWithFingerprint, oWithSubkeyFingerprint, + oWithoutSubkeyFingerprint, oWithICAOSpelling, oWithKeygrip, oWithKeyScreening, @@ -355,6 +356,7 @@ enum cmd_and_opt_values oAllowSecretKeyImport, oAllowOldCipherAlgos, oEnableSpecialFilenames, + oDisableFdTranslation, oNoLiteral, oSetFilesize, oHonorHttpProxy, @@ -454,6 +456,7 @@ enum cmd_and_opt_values oAssertSigner, oAssertPubkeyAlgo, oKbxBufferSize, + oRequirePQCEncryption, oNoop }; @@ -825,6 +828,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"), + ARGPARSE_s_n (oWithoutSubkeyFingerprint, "without-subkey-fingerprint", "@"), ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), @@ -882,7 +886,6 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oAllowOldCipherAlgos, "allow-old-cipher-algos", "@"), ARGPARSE_s_s (oWeakDigest, "weak-digest","@"), ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"), - ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), @@ -894,7 +897,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), - + ARGPARSE_s_n (oRequirePQCEncryption, "require-pqc-encryption", "@"), ARGPARSE_header (NULL, N_("Options for unattended use")), @@ -914,6 +917,8 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), ARGPARSE_s_n (oForceSignKey, "force-sign-key", "@"), + ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), + ARGPARSE_s_n (oDisableFdTranslation, "disable-fd-translation", "@"), ARGPARSE_header (NULL, N_("Other options")), @@ -1033,6 +1038,8 @@ static struct debug_flags_s debug_flags [] = /* The list of compatibility flags. */ static struct compatibility_flags_s compatibility_flags [] = { + { COMPAT_PARALLELIZED, "parallelized" }, + { COMPAT_T7014_OLD, "t7014-old" }, { 0, NULL } }; @@ -1087,10 +1094,6 @@ static void read_sessionkey_from_fd (int fd); -/* NPth wrapper function definitions. */ -ASSUAN_SYSTEM_NPTH_IMPL; - - static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { @@ -2091,6 +2094,8 @@ parse_list_options(char *str) NULL}, {"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL, N_("show user-supplied notations during signature listings")}, + {"show-x509-notations",LIST_SHOW_X509_NOTATIONS,NULL, NULL }, + {"store-x509-notations",LIST_STORE_X509_NOTATIONS,NULL, NULL }, {"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL, N_("show preferred keyserver URLs during signature listings")}, {"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL, @@ -2505,6 +2510,7 @@ main (int argc, char **argv) opt.passphrase_repeat = 1; opt.emit_version = 0; opt.weak_digests = NULL; + opt.with_subkey_fingerprint = 1; opt.compliance = CO_GNUPG; /* Check special options given on the command line. */ @@ -2912,6 +2918,9 @@ main (int argc, char **argv) case oWithSubkeyFingerprint: opt.with_subkey_fingerprint = 1; break; + case oWithoutSubkeyFingerprint: + opt.with_subkey_fingerprint = 0; + break; case oWithICAOSpelling: opt.with_icao_spelling = 1; break; @@ -3063,6 +3072,9 @@ main (int argc, char **argv) break; case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break; + case oRequirePQCEncryption: + opt.flags.require_pqc_encryption = 1; + break; case oRFC2440Text: opt.rfc2440_text=1; break; case oNoRFC2440Text: opt.rfc2440_text=0; break; @@ -3569,6 +3581,10 @@ main (int argc, char **argv) enable_special_filenames (); break; + case oDisableFdTranslation: + disable_translate_sys2libc_fd (); + break; + case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break; case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break; case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break; @@ -3907,8 +3923,8 @@ main (int argc, char **argv) /* Init threading which is used by some helper functions. */ npth_init (); - assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL); if (logfile) { @@ -4453,7 +4469,8 @@ main (int argc, char **argv) { if( argc > 1 ) wrong_args("--encrypt [filename]"); - if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, fname, remusr, + 0, NULL, GNUPG_INVALID_FD))) { write_status_failure ("encrypt", rc); log_error("%s: encryption failed: %s\n", @@ -4478,7 +4495,8 @@ main (int argc, char **argv) gnupg_compliance_option_string (opt.compliance)); else { - if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) ) + if ((rc = encrypt_crypt (ctrl, GNUPG_INVALID_FD, fname, remusr, + 1, NULL, GNUPG_INVALID_FD))) { write_status_failure ("encrypt", rc); log_error ("%s: encryption failed: %s\n", @@ -5676,13 +5694,13 @@ print_mds( const char *fname, int algo ) } else { - fp = es_fopen (fname, "rb" ); - if (fp && is_secured_file (es_fileno (fp))) + if (is_secured_filename (fname)) { - es_fclose (fp); fp = NULL; gpg_err_set_errno (EPERM); } + else + fp = es_fopen (fname, "rb" ); } if (!fp) { @@ -41,6 +41,10 @@ /* Number of bits we accept when reading or writing MPIs. */ #define MAX_EXTERN_MPI_BITS 16384 +/* Number of bytes we accept when reading four-octet count prefixed + * key parameters. Needs to fit as a positive number into an int. */ +#define MAX_EXTERN_KEYPARM_BITS (32768*8) + /* The maximum length of a binary fingerprints. This is used to * provide a static buffer and will be increased if we need to support * longer fingerprints. Warning: At some places we have some diff --git a/g10/import.c b/g10/import.c index ff8847cb6..bbeeebdfe 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2175,10 +2175,12 @@ import_one_real (ctrl_t ctrl, merge_keys_done = 1; /* Note that we do not want to show the validity because the key * has not yet imported. */ - list_keyblock_direct (ctrl, keyblock, from_sk, 0, + err = list_keyblock_direct (ctrl, keyblock, from_sk, 0, opt.fingerprint || opt.with_fingerprint, 1); es_fflush (es_stdout); no_usable_encr_subkeys_warning (keyblock); + if (err) + goto leave; } /* Write the keyblock to the output and do not actually import. */ diff --git a/g10/keydb.h b/g10/keydb.h index 62a99295d..75a8ded72 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -578,7 +578,8 @@ char *hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen); char *v5hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen); char *format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen); -gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array); +gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array, + int get_second); gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); char *ecdh_param_str_from_pk (PKT_public_key *pk); diff --git a/g10/keyedit.c b/g10/keyedit.c index f4f59de03..81ea06c24 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1103,6 +1103,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock) err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; + /* FIXME: Handle dual keys. */ err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); if (!err && serialno) ; /* Key on card. */ diff --git a/g10/keygen.c b/g10/keygen.c index 48e5b3a40..4bdb9f53a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,7 +1,7 @@ /* keygen.c - Generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2015, 2016, 2017, 2018 Werner Koch - * Copyright (C) 2020 g10 Code GmbH + * Copyright (C) 2020, 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -139,10 +139,23 @@ struct common_gen_cb_parm_s * may take a copy of this so that the result can be used after we * are back from the deep key generation call stack. */ gcry_sexp_t genkey_result; + /* For a dual algorithms the result of the second algorithm + * (e.g. Kyber). */ + gcry_sexp_t genkey_result2; }; typedef struct common_gen_cb_parm_s *common_gen_cb_parm_t; +/* A communication object to help adding certain notations to a key + * binding signature. */ +struct opaque_data_usage_and_pk +{ + unsigned int usage; + const char *cpl_notation; + PKT_public_key *pk; +}; + + /* FIXME: These globals vars are ugly. And using MAX_PREFS even for * aeads is useless, given that we don't expects more than a very few * algorithms. */ @@ -174,6 +187,9 @@ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, u32 expireval, int *keygen_flags); static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max); +static void do_add_notation (PKT_signature *sig, + const char *name, const char *value, + int critical); @@ -338,6 +354,20 @@ keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) } +/* This is only used to write the key binding signature. It is not + * used for the primary key. */ +static int +keygen_add_key_flags_from_oduap (PKT_signature *sig, void *opaque) +{ + struct opaque_data_usage_and_pk *oduap = opaque; + + do_add_key_flags (sig, oduap->usage); + if (oduap->cpl_notation) + do_add_notation (sig, "[email protected]", oduap->cpl_notation, 0); + return keygen_add_key_expire (sig, oduap->pk); +} + + static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { @@ -943,6 +973,44 @@ keygen_add_keyserver_url(PKT_signature *sig, void *opaque) return 0; } + +/* This function is used to add a notations to a signature. In + * general the caller should have cleared exiting notations before + * adding new ones. For example by calling: + * + * delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); + * delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); + * + * Only human readable notaions may be added. NAME and value are + * expected to be UTF-* strings. + */ +static void +do_add_notation (PKT_signature *sig, const char *name, const char *value, + int critical) +{ + unsigned char *buf; + unsigned int n1,n2; + + n1 = strlen (name); + n2 = strlen (value); + + buf = xmalloc (8 + n1 + n2); + + buf[0] = 0x80; /* human readable. */ + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy (buf+8, name, n1); + memcpy (buf+8+n1, value, n2); + build_sig_subpkt (sig, + (SIGSUBPKT_NOTATION|(critical?SIGSUBPKT_FLAG_CRITICAL:0)), + buf, 8+n1+n2 ); + xfree (buf); +} + + int keygen_add_notations(PKT_signature *sig,void *opaque) { @@ -992,6 +1060,7 @@ keygen_add_notations(PKT_signature *sig,void *opaque) return 0; } + int keygen_add_revkey (PKT_signature *sig, void *opaque) { @@ -1225,6 +1294,7 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; + struct opaque_data_usage_and_pk oduap; if (opt.verbose) log_info(_("writing key binding signature\n")); @@ -1250,10 +1320,21 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, BUG(); /* Make the signature. */ - sub_pk->pubkey_usage = use; + oduap.usage = use; + if ((use & PUBKEY_USAGE_ENC) + && opt.compliance == CO_DE_VS + /* The required libgcrypt 1.11 won't yet claim a compliant RNG. */ + && gnupg_rng_is_compliant (CO_DE_VS)) + oduap.cpl_notation = "de-vs"; + else if ((use & PUBKEY_USAGE_ENC) + && sub_pk->pubkey_algo == PUBKEY_ALGO_KYBER) + oduap.cpl_notation = "fips203.ipd.2023-08-24"; + else + oduap.cpl_notation = NULL; + oduap.pk = sub_pk; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, timestamp, 0, - keygen_add_key_flags_and_expire, sub_pk, + keygen_add_key_flags_from_oduap, &oduap, cache_nonce); if (err) { @@ -1311,8 +1392,12 @@ curve_is_448 (gcry_sexp_t sexp) } +/* Extract the parameters in OpenPGP format from SEXP and put them + * into the caller provided ARRAY. SEXP2 is used to provide the + * parameters for dual algorithm (e.g. Kyber). */ static gpg_error_t -ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) +ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, + gcry_sexp_t sexp2, int algo, int pkversion) { gpg_error_t err; gcry_sexp_t list, l2; @@ -1355,6 +1440,10 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } + /* For v5 keys we prefer the modern OID for cv25519. */ + if (pkversion > 4 && !strcmp (oidstr, "1.3.6.1.4.1.3029.1.5.1")) + oidstr = "1.3.101.110"; + err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; @@ -1364,8 +1453,46 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) goto leave; gcry_sexp_release (list); + list = NULL; - if (algo == PUBKEY_ALGO_ECDH) + if (algo == PUBKEY_ALGO_KYBER) + { + if (!sexp2) + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + + list = gcry_sexp_find_token (sexp2, "public-key", 0); + if (!list) + { + err = gpg_error (GPG_ERR_INV_OBJ); + goto leave; + } + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (!list) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + + l2 = gcry_sexp_find_token (list, "p", 1); + if (!l2) + { + err = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ + goto leave; + } + array[2] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_OPAQUE); + gcry_sexp_release (l2); + if (!array[2]) + { + err = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ + goto leave; + } + } + else if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) @@ -1377,6 +1504,7 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) leave: xfree (curve); + gcry_sexp_release (list); if (err) { for (i=0; i < 3; i++) @@ -1455,10 +1583,24 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + gcry_sexp_t s_key2 = NULL; const char *algoelem; + char *hexkeygrip_buffer = NULL; + char *hexkeygrip2 = NULL; if (hexkeygrip[0] == '&') hexkeygrip++; + if (strchr (hexkeygrip, ',')) + { + hexkeygrip_buffer = xtrystrdup (hexkeygrip); + if (!hexkeygrip_buffer) + return gpg_error_from_syserror (); + hexkeygrip = hexkeygrip_buffer; + hexkeygrip2 = strchr (hexkeygrip_buffer, ','); + if (hexkeygrip2) + *hexkeygrip2++ = 0; + } + switch (algo) { @@ -1468,16 +1610,21 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: algoelem = ""; break; case PUBKEY_ALGO_EDDSA: algoelem = ""; break; - default: return gpg_error (GPG_ERR_INTERNAL); + case PUBKEY_ALGO_KYBER: algoelem = ""; break; + default: + xfree (hexkeygrip_buffer); + return gpg_error (GPG_ERR_INTERNAL); } - /* Ask the agent for the public key matching HEXKEYGRIP. */ if (cardkey) { err = agent_scd_readkey (ctrl, hexkeygrip, &s_key, NULL); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } } else { @@ -1485,16 +1632,41 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, err = agent_readkey (ctrl, 0, hexkeygrip, &public); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } err = gcry_sexp_sscan (&s_key, NULL, public, gcry_sexp_canon_len (public, 0, NULL, NULL)); xfree (public); if (err) - return err; + { + xfree (hexkeygrip_buffer); + return err; + } + if (hexkeygrip2) + { + err = agent_readkey (ctrl, 0, hexkeygrip2, &public); + if (err) + { + gcry_sexp_release (s_key); + xfree (hexkeygrip_buffer); + return err; + } + err = gcry_sexp_sscan (&s_key2, NULL, public, + gcry_sexp_canon_len (public, 0, NULL, NULL)); + xfree (public); + if (err) + { + gcry_sexp_release (s_key); + xfree (hexkeygrip_buffer); + return err; + } + } } - /* For X448 we force the use of v5 packets. */ - if (curve_is_448 (s_key)) + /* For X448 and Kyber we force the use of v5 packets. */ + if (curve_is_448 (s_key) || algo == PUBKEY_ALGO_KYBER) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; /* Build a public key packet. */ @@ -1503,6 +1675,8 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); + xfree (hexkeygrip_buffer); return err; } @@ -1512,26 +1686,32 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); + else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); + xfree (hexkeygrip_buffer); return err; } @@ -1539,16 +1719,18 @@ do_create_from_keygrip (ctrl_t ctrl, int algo, pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); + xfree (hexkeygrip_buffer); return 0; } -/* Common code for the key generation function gen_xxx. The optinal +/* Common code for the key generation function gen_xxx. The optional * (COMMON_GEN_CB,COMMON_GEN_CB_PARM) can be used as communication - * object. + * object. A KEYPARMS2 forces the use of a dual key (e.g. Kyber+ECC). */ static int -common_gen (const char *keyparms, int algo, const char *algoelem, +common_gen (const char *keyparms, const char *keyparms2, + int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr, @@ -1559,6 +1741,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; + gcry_sexp_t s_key2 = NULL; err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), @@ -1570,14 +1753,32 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } + if (keyparms2) + { + err = agent_genkey (NULL, NULL, NULL, keyparms2, + 1 /* No protection */, + NULL, timestamp, + &s_key2); + if (err) + { + log_error ("agent_genkey failed for second algo: %s\n", + gpg_strerror (err) ); + gcry_sexp_release (s_key); + return err; + } + } + if (common_gen_cb && common_gen_cb_parm) { common_gen_cb_parm->genkey_result = s_key; + common_gen_cb_parm->genkey_result2 = s_key2; err = common_gen_cb (common_gen_cb_parm); common_gen_cb_parm->genkey_result = NULL; + common_gen_cb_parm->genkey_result2 = NULL; if (err) { gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); return err; } } @@ -1596,10 +1797,12 @@ common_gen (const char *keyparms, int algo, const char *algoelem, pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; - if (algo == PUBKEY_ALGO_ECDSA - || algo == PUBKEY_ALGO_EDDSA - || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + if (algo == PUBKEY_ALGO_KYBER) + err = ecckey_from_sexp (pk->pkey, s_key, s_key2, algo, pk->version); + else if (algo == PUBKEY_ALGO_ECDSA + || algo == PUBKEY_ALGO_EDDSA + || algo == PUBKEY_ALGO_ECDH ) + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) @@ -1610,6 +1813,7 @@ common_gen (const char *keyparms, int algo, const char *algoelem, return err; } gcry_sexp_release (s_key); + gcry_sexp_release (s_key2); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) @@ -1675,7 +1879,7 @@ gen_elg (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "pgy", + err = common_gen (keyparms, NULL, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1767,7 +1971,7 @@ gen_dsa (unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", + err = common_gen (keyparms, NULL, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1868,7 +2072,7 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "", + err = common_gen (keyparms, NULL, algo, "", pub_root, timestamp, expireval, is_subkey, *keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -1880,6 +2084,79 @@ gen_ecc (int algo, const char *curve, kbnode_t pub_root, } +/* Generate a dual ECC+Kyber key. Note that KEYGEN_FLAGS will be + * updated by this function to indicate the forced creation of a v5 + * key. */ +static gpg_error_t +gen_kyber (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, + u32 timestamp, u32 expireval, int is_subkey, + int *keygen_flags, const char *passphrase, + char **cache_nonce_addr, char **passwd_nonce_addr, + gpg_error_t (*common_gen_cb)(common_gen_cb_parm_t), + common_gen_cb_parm_t common_gen_cb_parm) +{ + gpg_error_t err; + char *keyparms1; + const char *keyparms2; + + log_assert (algo == PUBKEY_ALGO_KYBER); + + if (nbits == 768) + keyparms2 = "(genkey(kyber768))"; + else if (nbits == 1024) + keyparms2 = "(genkey(kyber1024))"; + else + return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); + + if (!curve || !*curve) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + + *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + + if (!strcmp (curve, "Curve25519")) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else if (!strcmp (curve, "X448")) + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags comp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + else /* Should we use the compressed format? Check smartcard support. */ + { + keyparms1 = xtryasprintf + ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", + strlen (curve), curve, + (((*keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) + && (*keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? + " transient-key" : "")); + } + + if (!keyparms1) + err = gpg_error_from_syserror (); + else + { + err = common_gen (keyparms1, keyparms2, algo, "", + pub_root, timestamp, expireval, is_subkey, + *keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); + xfree (keyparms1); + } + + return err; +} + + /* * Generate an RSA key. */ @@ -1928,7 +2205,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, err = gpg_error_from_syserror (); else { - err = common_gen (keyparms, algo, "ne", + err = common_gen (keyparms, NULL, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, @@ -2350,7 +2627,26 @@ ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, continue; } - if (strlen (answer) != 40 && + if (strlen (answer) == 40+1+40 && answer[40]==',') + { + int algo1, algo2; + + answer[40] = 0; + algo1 = check_keygrip (ctrl, answer); + algo2 = check_keygrip (ctrl, answer+41); + answer[40] = ','; + if (algo1 == PUBKEY_ALGO_ECDH && algo2 == PUBKEY_ALGO_KYBER) + { + algo = PUBKEY_ALGO_KYBER; + break; + } + else if (!algo1 || !algo2) + tty_printf (_("No key with this keygrip\n")); + else + tty_printf ("Invalid combination for dual algo (%d,%d)\n", + algo1, algo2); + } + else if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); @@ -2560,6 +2856,12 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) def=255; break; + case PUBKEY_ALGO_KYBER: + *min = 768; + *max = 1024; + def = 768; + break; + default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; @@ -2575,45 +2877,44 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) static unsigned int fixup_keysize (unsigned int nbits, int algo, int silent) { + unsigned int orig_nbits = nbits; + if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits); } else if (algo == PUBKEY_ALGO_EDDSA) { - if (nbits != 255 && nbits != 441) - { - if (nbits < 256) - nbits = 255; - else - nbits = 441; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 255; + else + nbits = 441; } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { - if (nbits != 256 && nbits != 384 && nbits != 521) - { - if (nbits < 256) - nbits = 256; - else if (nbits < 384) - nbits = 384; - else - nbits = 521; - if (!silent) - tty_printf (_("rounded to %u bits\n"), nbits); - } + if (nbits < 256) + nbits = 256; + else if (nbits < 384) + nbits = 384; + else + nbits = 521; + } + else if (algo == PUBKEY_ALGO_KYBER) + { + /* (in reality the numbers are not bits) */ + if (nbits < 768) + nbits = 768; + else if (nbits > 1024) + nbits = 1024; } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; - if (!silent) - tty_printf (_("rounded up to %u bits\n"), nbits ); } + if (!silent && orig_nbits != nbits) + tty_printf (_("rounded to %u bits\n"), nbits); + return nbits; } @@ -3343,6 +3644,12 @@ do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr, common_gen_cb, common_gen_cb_parm); + else if (algo == PUBKEY_ALGO_KYBER) + err = gen_kyber (algo, nbits, curve, + pub_root, timestamp, expiredate, is_subkey, + keygen_flags, passphrase, + cache_nonce_addr, passwd_nonce_addr, + common_gen_cb, common_gen_cb_parm); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, *keygen_flags, passphrase, @@ -3421,6 +3728,7 @@ parse_key_parameter_part (ctrl_t ctrl, char *keygrip = NULL; u32 keytime = 0; int is_448 = 0; + int is_pqc = 0; if (!string || !*string) return 0; /* Success. */ @@ -3446,6 +3754,7 @@ parse_key_parameter_part (ctrl_t ctrl, ; /* We need the flags before we can figure out the key to use. */ else if (algo) { + /* This is one of the algos parsed above (rsa, dsa, or elg). */ if (!string[3]) size = get_keysize_range (algo, NULL, NULL); else @@ -3455,6 +3764,63 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_INV_VALUE); } } + else if (!ascii_strcasecmp (string, "kyber") + || !ascii_strcasecmp (string, "kyber768")) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP256r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 768; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "kyber1024")) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + curve = openpgp_is_curve_supported ("brainpoolP384r1", &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = 1024; + is_pqc = 1; + } + else if (!ascii_strncasecmp (string, "ky768_", 6) + || !ascii_strncasecmp (string, "ky1024_", 7) + || !ascii_strncasecmp (string, "kyber768_", 9) + || !ascii_strncasecmp (string, "kyber1024_", 10) + ) + { + /* Get the curve and check that it can technically be used + * (i.e. everything except the EdXXXX curves. */ + s = strchr (string, '_'); + log_assert (s); + s++; + curve = openpgp_is_curve_supported (s, &algo, NULL); + if (!curve || algo == PUBKEY_ALGO_EDDSA) + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + algo = PUBKEY_ALGO_KYBER; + size = strstr (string, "768_")? 768 : 1024; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "dil3")) + { + algo = PUBKEY_ALGO_DIL3_25519; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "dil5")) + { + algo = PUBKEY_ALGO_DIL5_448; + is_pqc = 1; + } + else if (!ascii_strcasecmp (string, "sphinx") + || !ascii_strcasecmp (string, "sphinx_sha2")) + { + algo = PUBKEY_ALGO_SPHINX_SHA2; + is_pqc = 1; + } else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) { if (!algo) @@ -3703,8 +4069,8 @@ parse_key_parameter_part (ctrl_t ctrl, return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } - /* Ed448 and X448 must only be used as v5 keys. */ - if (is_448) + /* Ed448, X448 and the PQC algos must only be used as v5 keys. */ + if (is_448 || is_pqc) { if (keyversion == 4) log_info (_("WARNING: v4 is specified, but overridden by v5.\n")); @@ -3768,6 +4134,8 @@ parse_key_parameter_part (ctrl_t ctrl, * cv25519 := ECDH using curve Curve25519. * cv448 := ECDH using curve X448. * nistp256:= ECDSA or ECDH using curve NIST P-256 + * kyber := Kyber with the default parameters + * ky768_bp384 := Kyber-768 with BrainpoolP256r1 as second algo * * All strings with an unknown prefix are considered an elliptic * curve. Curves which have no implicit algorithm require that FLAGS @@ -5924,9 +6292,12 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, list_keyblock_direct (ctrl, pub_root, 0, 1, opt.fingerprint || opt.with_fingerprint, 1); + /* Note that we ignore errors from the list function + * because that would only be an additional info. It + * has already been remarked that the key has been + * created. */ } - if (!opt.batch && (get_parameter_algo (ctrl, para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA @@ -6137,6 +6508,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; + /* FIXME: Right now the primary key won't be a dual key. But this + * will change */ if (agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) { if (interactive) @@ -6476,12 +6849,14 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, if (curve_is_448 (s_key)) *keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; + pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; + if (algo == PUBKEY_ALGO_RSA) err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) - err = ecckey_from_sexp (pk->pkey, s_key, algo); + err = ecckey_from_sexp (pk->pkey, s_key, NULL, algo, pk->version); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); @@ -6494,7 +6869,6 @@ gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, } pk->timestamp = *timestamp; - pk->version = (*keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; diff --git a/g10/keyid.c b/g10/keyid.c index fab1e3a36..08f684829 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -74,12 +74,18 @@ pubkey_letter( int algo ) is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: - "rsa3072" - RSA with 3072 bit - "elg1024" - Elgamal with 1024 bit - "ed25519" - ECC using the curve Ed25519. - "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". + "rsa3072" - RSA with 3072 bit + "elg1024" - Elgamal with 1024 bit + "ed25519" - EdDSA using the curve Ed25519. + "cv25519" - ECDH using the curve X25519. + "ky768_cv448 - Kyber-768 with X448 as second algo. + "ky1025_bp512 - Kyber-1024 with BrainpoolP256r1 as second algo. + "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". + "unknown_N" - Unknown OpenPGP algorithm N. "E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID. - "unknown_N" - Unknown OpenPGP algorithm N. + + Note that with Kyber we use "bp" as abbreviation for BrainpoolP and + ignore the final r1 part. If the option --legacy-list-mode is active, the output use the legacy format: @@ -97,6 +103,9 @@ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; + int dual = 0; + char *curve; + const char *name; if (opt.legacy_list_mode) { @@ -116,14 +125,34 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: prefix = ""; break; + case PUBKEY_ALGO_KYBER: prefix = "ky"; dual = 1; break; + case PUBKEY_ALGO_DIL3_25519: prefix = "dil3"; break; + case PUBKEY_ALGO_DIL5_448: prefix = "dil5"; break; + case PUBKEY_ALGO_SPHINX_SHA2: prefix = "sphinx_sha2"; break; } + if (prefix && *prefix) - snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); + { + if (dual) + { + curve = openpgp_oid_to_str (pk->pkey[0]); + /* Note that we prefer the abbreviated name of the curve. */ + name = openpgp_oid_to_curve (curve, 2); + if (!name) + name = "unknown"; + + snprintf (buffer, bufsize, "%s%u_%s", + prefix, nbits_from_pk (pk), name); + xfree (curve); + } + else + snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); + } else if (prefix) { - char *curve = openpgp_oid_to_str (pk->pkey[0]); - const char *name = openpgp_oid_to_curve (curve, 0); + curve = openpgp_oid_to_str (pk->pkey[0]); + name = openpgp_oid_to_curve (curve, 0); if (name) snprintf (buffer, bufsize, "%s", name); @@ -303,6 +332,30 @@ do_hash_public_key (gcry_md_hd_t md, PKT_public_key *pk, int use_v5) pp[i] = NULL; nn[i] = 0; } + else if (pk->pubkey_algo == PUBKEY_ALGO_KYBER && i == 2) + { + /* Ugly: We need to re-construct the wire format of the + * key parameter. It would be easier to use a second + * index for pp and nn which we could bump independet of + * i. */ + const char *p; + + p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); + nn[i] = (nbits+7)/8; + pp[i] = xmalloc (4 + nn[i] + 1); + if (p) + { + pp[i][0] = nn[i] >> 24; + pp[i][1] = nn[i] >> 16; + pp[i][2] = nn[i] >> 8; + pp[i][3] = nn[i]; + memcpy (pp[i] + 4 , p, nn[i]); + nn[i] += 4; + } + else + pp[i] = NULL; + n += nn[i]; + } else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const char *p; @@ -800,11 +853,28 @@ namehash_from_uid (PKT_user_id *uid) /* - * Return the number of bits used in PK. + * Return the number of bits used in PK. For Kyber we return the + * octet count of the Kyber part and not of the ECC (thus likely + * values are 768 or 1024). */ unsigned int nbits_from_pk (PKT_public_key *pk) { + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + unsigned int nbits; + if (!gcry_mpi_get_opaque (pk->pkey[2], &nbits)) + return 0; + switch (nbits/8) + { + case 800: nbits = 512; break; + case 1184: nbits = 768; break; + case 1568: nbits = 1024; break; + default: nbits = 0; break; /* Unkown version. */ + } + return nbits; + } + else return pubkey_nbits (pk->pubkey_algo, pk->pkey); } @@ -1230,16 +1300,22 @@ format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) /* Return the so called KEYGRIP which is the SHA-1 hash of the public - key parameters expressed as an canonical encoded S-Exp. ARRAY must - be 20 bytes long. Returns 0 on success or an error code. */ + * key parameters expressed as an canonical encoded S-Exp. ARRAY must + * be 20 bytes long. Returns 0 on success or an error code. If + * GET_SECOND Is one and PK has dual algorithm, the keygrip of the + * second algorithm is return; GPG_ERR_FALSE is returned if the algo + * is not a dual algorithm. */ gpg_error_t -keygrip_from_pk (PKT_public_key *pk, unsigned char *array) +keygrip_from_pk (PKT_public_key *pk, unsigned char *array, int get_second) { gpg_error_t err; gcry_sexp_t s_pkey; if (DBG_PACKET) - log_debug ("get_keygrip for public key\n"); + log_debug ("get_keygrip for public key%s\n", get_second?" (second)":""); + + if (get_second && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + return gpg_error (GPG_ERR_FALSE); switch (pk->pubkey_algo) { @@ -1287,6 +1363,33 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) } break; + case PUBKEY_ALGO_KYBER: + if (get_second) + { + char tmpname[15]; + + snprintf (tmpname, sizeof tmpname, "kyber%u", nbits_from_pk (pk)); + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(%s(p%m)))", + tmpname, pk->pkey[2]); + } + else + { + char *curve = openpgp_oid_to_str (pk->pkey[0]); + if (!curve) + err = gpg_error_from_syserror (); + else + { + err = gcry_sexp_build (&s_pkey, NULL, + openpgp_oid_is_cv25519 (pk->pkey[0]) + ? "(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))" + : "(public-key(ecc(curve%s)(q%m)))", + curve, pk->pkey[1]); + xfree (curve); + } + } + break; + default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; @@ -1319,26 +1422,45 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) /* Store an allocated buffer with the keygrip of PK encoded as a - hexstring at r_GRIP. Returns 0 on success. */ + * hexstring at r_GRIP. Returns 0 on success. For dual algorithms + * the keygrips are delimited by a comma. */ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; + char *buf; unsigned char grip[KEYGRIP_LEN]; + unsigned char grip2[KEYGRIP_LEN]; *r_grip = NULL; - err = keygrip_from_pk (pk, grip); + err = keygrip_from_pk (pk, grip, 0); if (!err) { - char * buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); - if (!buf) - err = gpg_error_from_syserror (); + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + err = keygrip_from_pk (pk, grip2, 1); + if (err) + goto leave; + buf = xtrymalloc (2 * KEYGRIP_LEN * 2 + 1 + 1); + } else + buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); + + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + bin2hex (grip, KEYGRIP_LEN, buf); + if (pk->pubkey_algo == PUBKEY_ALGO_KYBER) { - bin2hex (grip, KEYGRIP_LEN, buf); - *r_grip = buf; + buf[2*KEYGRIP_LEN] = ','; + bin2hex (grip2, KEYGRIP_LEN, buf+2*KEYGRIP_LEN+1); } + *r_grip = buf; } + leave: return err; } diff --git a/g10/keylist.c b/g10/keylist.c index cc1e23d7f..81d6805a5 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -82,7 +82,7 @@ static estream_t attrib_fp; -static void list_keyblock (ctrl_t ctrl, +static gpg_error_t list_keyblock (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx); @@ -745,6 +745,7 @@ list_all (ctrl_t ctrl, int secret, int mark_secret) int any_secret; const char *lastresname, *resname; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) @@ -802,13 +803,13 @@ list_all (ctrl_t ctrl, int secret, int mark_secret) } } merge_keys_and_selfsig (ctrl, keyblock); - list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, - &listctx); + listerr = list_keyblock (ctrl, keyblock, secret, any_secret, + opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } - while (!(rc = keydb_search_next (hd))); + while (!listerr && !(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); @@ -839,6 +840,7 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) @@ -887,12 +889,12 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } - list_keyblock (ctrl, keyblock, secret, any_secret, - opt.fingerprint, &listctx); + listerr = list_keyblock (ctrl, keyblock, secret, any_secret, + opt.fingerprint, &listctx); } release_kbnode (keyblock); } - while (!getkey_next (ctrl, ctx, NULL, &keyblock)); + while (!listerr && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); if (opt.check_sigs && !opt.with_colons) @@ -910,12 +912,13 @@ locate_one (ctrl_t ctrl, strlist_t names, int no_local) GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; + gpg_error_t listerr = 0; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; - for (sl = names; sl; sl = sl->next) + for (sl = names; sl && !listerr; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, no_local? GET_PUBKEY_NO_LOCAL @@ -933,10 +936,11 @@ locate_one (ctrl_t ctrl, strlist_t names, int no_local) { do { - list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); + listerr = list_keyblock (ctrl, keyblock, 0, 0, + opt.fingerprint, &listctx); release_kbnode (keyblock); } - while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); + while (!listerr && ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); ctx = NULL; } @@ -1171,6 +1175,77 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) } +static void +print_keygrip (const char *keygrip) +{ + const char *s; + + s = strchr (keygrip, ','); + if (s) + es_fprintf (es_stdout, " Keygrip = %.*s,\n%*s%s\n", + (int)(s-keygrip), keygrip, 16, "", s+1); + else + es_fprintf (es_stdout, " Keygrip = %s\n", keygrip); +} + + +/* If PK is given the output is written to a new file instead of + * stdout. */ +static void +print_x509_notations (struct notation *nots, PKT_public_key *pk) +{ + gpg_error_t err; + gpgrt_b64state_t state = NULL; + char hexfpr[2*4 + 1 + 2*MAX_FINGERPRINT_LEN+4+1]; + char sha1[20]; + estream_t fp; + + for (; nots; nots = nots->next) + { + if (pk) + { + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1, nots->bdat, nots->blen); + bin2hex (sha1+16, 4, hexfpr); + hexfpr[2*4] = '-'; + hexfingerprint (pk, hexfpr + 2*4+1, 2*MAX_FINGERPRINT_LEN); + strcat (hexfpr, ".pem"); + fp = es_fopen (hexfpr, "w"); + if (!fp) + { + err = gpg_err_code_from_syserror (); + goto b64fail; + } + } + else + fp = es_stdout; + state = gpgrt_b64enc_start (fp, "CERTIFICATE"); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto b64fail; + } + err = gpgrt_b64enc_write (state, nots->bdat, nots->blen); + if (err) + goto b64fail; + err = gpgrt_b64enc_finish (state); + if (err) + goto b64fail; + if (fp != es_stdout) + { + es_fclose (fp); + fp = NULL; + } + } + return; + + b64fail: + log_error ("error writing base64 encoded notation: %s\n", gpg_strerror (err)); + gpgrt_b64enc_finish (state); + if (fp && fp != es_stdout) + gpgrt_fcancel (fp); +} + + /* Order two signatures. We first order by keyid and then by creation * time. */ int @@ -1220,170 +1295,186 @@ cmp_signodes (const void *av, const void *bv) * NODFLG_MARK_B to indicate self-signatures. */ static void list_signature_print (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, - struct keylist_context *listctx) + struct keylist_context *listctx, PKT_public_key *lastpk) { - /* (extra indentation to keep the diff history short) */ - PKT_signature *sig = node->pkt->pkt.signature; - int rc, sigrc; - char *sigstr; - char *reason_text = NULL; - char *reason_comment = NULL; - size_t reason_commentlen; - int reason_code = 0; + PKT_signature *sig = node->pkt->pkt.signature; + int rc, sigrc; + char *sigstr; + char *reason_text = NULL; + char *reason_comment = NULL; + size_t reason_commentlen; + int reason_code = 0; + + if (listctx->check_sigs) + { + rc = check_key_signature (ctrl, keyblock, node, NULL); + switch (gpg_err_code (rc)) + { + case 0: + listctx->good_sigs++; + sigrc = '!'; + break; + case GPG_ERR_BAD_SIGNATURE: + listctx->inv_sigs++; + sigrc = '-'; + break; + case GPG_ERR_NO_PUBKEY: + case GPG_ERR_UNUSABLE_PUBKEY: + listctx->no_key++; + return; + case GPG_ERR_DIGEST_ALGO: + case GPG_ERR_PUBKEY_ALGO: + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)) + return; + /* fallthru. */ + default: + listctx->oth_err++; + sigrc = '%'; + break; + } - if (listctx->check_sigs) - { - rc = check_key_signature (ctrl, keyblock, node, NULL); - switch (gpg_err_code (rc)) - { - case 0: - listctx->good_sigs++; - sigrc = '!'; - break; - case GPG_ERR_BAD_SIGNATURE: - listctx->inv_sigs++; - sigrc = '-'; - break; - case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: - listctx->no_key++; - return; - case GPG_ERR_DIGEST_ALGO: - case GPG_ERR_PUBKEY_ALGO: - if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS)) - return; - /* fallthru. */ - default: - listctx->oth_err++; - sigrc = '%'; - break; - } + /* TODO: Make sure a cached sig record here still has + the pk that issued it. See also + keyedit.c:print_and_check_one_sig */ + } + else + { + if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS) + && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo) + == GPG_ERR_PUBKEY_ALGO) + || gpg_err_code (openpgp_md_test_algo (sig->digest_algo) + == GPG_ERR_DIGEST_ALGO) + || (sig->digest_algo == DIGEST_ALGO_SHA1 + && !(node->flag & NODFLG_MARK_B) /*no selfsig*/ + && !opt.flags.allow_weak_key_signatures))) + return; + rc = 0; + sigrc = ' '; + } - /* TODO: Make sure a cached sig record here still has - the pk that issued it. See also - keyedit.c:print_and_check_one_sig */ - } - else - { - if (!(opt.list_options & LIST_SHOW_UNUSABLE_SIGS) - && (gpg_err_code (openpgp_pk_test_algo (sig->pubkey_algo) - == GPG_ERR_PUBKEY_ALGO) - || gpg_err_code (openpgp_md_test_algo (sig->digest_algo) - == GPG_ERR_DIGEST_ALGO) - || (sig->digest_algo == DIGEST_ALGO_SHA1 - && !(node->flag & NODFLG_MARK_B) /*no selfsig*/ - && !opt.flags.allow_weak_key_signatures))) - return; - rc = 0; - sigrc = ' '; - } + if (IS_KEY_REV (sig) || IS_SUBKEY_REV (sig) || IS_UID_REV (sig)) + { + sigstr = "rev"; + reason_code = get_revocation_reason (sig, &reason_text, + &reason_comment, + &reason_commentlen); + } + else if (IS_UID_SIG (sig)) + sigstr = "sig"; + else if (IS_SUBKEY_SIG (sig)) + sigstr = "sig"; + else if (IS_KEY_SIG (sig)) + sigstr = "sig"; + else + { + es_fprintf (es_stdout, "sig " + "[unexpected signature class 0x%02x]\n", + sig->sig_class); + return; + } - if (sig->sig_class == 0x20 || sig->sig_class == 0x28 - || sig->sig_class == 0x30) - { - sigstr = "rev"; - reason_code = get_revocation_reason (sig, &reason_text, - &reason_comment, - &reason_commentlen); - } - else if ((sig->sig_class & ~3) == 0x10) - sigstr = "sig"; - else if (sig->sig_class == 0x18) - sigstr = "sig"; - else if (sig->sig_class == 0x1F) - sigstr = "sig"; - else - { - es_fprintf (es_stdout, "sig " - "[unexpected signature class 0x%02x]\n", - sig->sig_class); - return; - } + es_fputs (sigstr, es_stdout); + es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", + sigrc, (sig->sig_class - 0x10 > 0 && + sig->sig_class - 0x10 < + 4) ? '0' + sig->sig_class - 0x10 : ' ', + sig->flags.exportable ? ' ' : 'L', + sig->flags.revocable ? ' ' : 'R', + sig->flags.policy_url ? 'P' : ' ', + sig->flags.notation ? 'N' : ' ', + sig->flags.expired ? 'X' : ' ', + (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > + 0) ? '0' + + sig->trust_depth : ' ', keystr (sig->keyid), + datestr_from_sig (sig)); + if (opt.list_options & LIST_SHOW_SIG_EXPIRE) + es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); + es_fprintf (es_stdout, " "); + if (sigrc == '%') + es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); + else if (sigrc == '?') + ; + else if ((node->flag & NODFLG_MARK_B)) + es_fputs (_("[self-signature]"), es_stdout); + else if (!opt.fast_list_mode ) + { + size_t n; + char *p = get_user_id (ctrl, sig->keyid, &n, NULL); + print_utf8_buffer (es_stdout, p, n); + xfree (p); + } + es_putc ('\n', es_stdout); - es_fputs (sigstr, es_stdout); - es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", - sigrc, (sig->sig_class - 0x10 > 0 && - sig->sig_class - 0x10 < - 4) ? '0' + sig->sig_class - 0x10 : ' ', - sig->flags.exportable ? ' ' : 'L', - sig->flags.revocable ? ' ' : 'R', - sig->flags.policy_url ? 'P' : ' ', - sig->flags.notation ? 'N' : ' ', - sig->flags.expired ? 'X' : ' ', - (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > - 0) ? '0' + - sig->trust_depth : ' ', keystr (sig->keyid), - datestr_from_sig (sig)); - if (opt.list_options & LIST_SHOW_SIG_EXPIRE) - es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); - es_fprintf (es_stdout, " "); - if (sigrc == '%') - es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); - else if (sigrc == '?') - ; - else if ((node->flag & NODFLG_MARK_B)) - es_fputs (_("[self-signature]"), es_stdout); - else if (!opt.fast_list_mode ) - { - size_t n; - char *p = get_user_id (ctrl, sig->keyid, &n, NULL); - print_utf8_buffer (es_stdout, p, n); - xfree (p); - } - es_putc ('\n', es_stdout); + if (sig->flags.policy_url + && (opt.list_options & LIST_SHOW_POLICY_URLS)) + show_policy_url (sig, 3, 0); + + if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) + show_notation (sig, 3, 0, + ((opt. + list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + + + ((opt. + list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : + 0)); + + if (sig->flags.notation + && (opt.list_options + & (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS))) + { + struct notation *nots; - if (sig->flags.policy_url - && (opt.list_options & LIST_SHOW_POLICY_URLS)) - show_policy_url (sig, 3, 0); + if ((IS_KEY_SIG (sig) || IS_SUBKEY_SIG (sig)) + && (nots = search_sig_notations (sig, + "[email protected]"))) + { + if ((opt.list_options & LIST_STORE_X509_NOTATIONS)) + print_x509_notations (nots, lastpk); + else + print_x509_notations (nots, NULL); + free_notation (nots); + } + } - if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) - show_notation (sig, 3, 0, - ((opt. - list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) - + - ((opt. - list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : - 0)); + if (sig->flags.pref_ks + && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) + show_keyserver_url (sig, 3, 0); - if (sig->flags.pref_ks - && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) - show_keyserver_url (sig, 3, 0); + if (reason_text && (reason_code || reason_comment)) + { + es_fprintf (es_stdout, " %s%s\n", + _("reason for revocation: "), reason_text); + if (reason_comment) + { + const byte *s, *s_lf; + size_t n, n_lf; - if (reason_text && (reason_code || reason_comment)) + s = reason_comment; + n = reason_commentlen; + s_lf = NULL; + do { - es_fprintf (es_stdout, " %s%s\n", - _("reason for revocation: "), reason_text); - if (reason_comment) + /* We don't want any empty lines, so we skip them. */ + for (;n && *s == '\n'; s++, n--) + ; + if (n) { - const byte *s, *s_lf; - size_t n, n_lf; - - s = reason_comment; - n = reason_commentlen; - s_lf = NULL; - do - { - /* We don't want any empty lines, so we skip them. */ - for (;n && *s == '\n'; s++, n--) - ; - if (n) - { - s_lf = memchr (s, '\n', n); - n_lf = s_lf? s_lf - s : n; - es_fprintf (es_stdout, " %s", - _("revocation comment: ")); - es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); - es_putc ('\n', es_stdout); - s += n_lf; n -= n_lf; - } - } while (s_lf); + s_lf = memchr (s, '\n', n); + n_lf = s_lf? s_lf - s : n; + es_fprintf (es_stdout, " %s", + _("revocation comment: ")); + es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); + es_putc ('\n', es_stdout); + s += n_lf; n -= n_lf; } - } + } while (s_lf); + } + } - xfree (reason_text); - xfree (reason_comment); + xfree (reason_text); + xfree (reason_comment); - /* fixme: check or list other sigs here */ + /* fixme: check or list other sigs here */ } @@ -1394,6 +1485,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, int rc; kbnode_t node; PKT_public_key *pk; + PKT_public_key *lastpk; u32 *mainkid; int skip_sigs = 0; char *hexgrip = NULL; @@ -1410,6 +1502,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, pk = node->pkt->pkt.public_key; mainkid = pk_keyid (pk); + lastpk = pk; if (secret || opt.with_keygrip) { @@ -1437,7 +1530,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_fingerprint (ctrl, NULL, pk, 0); if (opt.with_keygrip && hexgrip) - es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); + print_keygrip (hexgrip); if (serialno) print_card_serialno (serialno); @@ -1558,6 +1651,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, { PKT_public_key *pk2 = node->pkt->pkt.public_key; + lastpk = pk2; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { @@ -1593,13 +1687,15 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) - es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); + print_keygrip (hexgrip); if (opt.with_key_data) print_key_data (pk2); if (opt.with_key_screening) print_pk_screening (pk2, 0); } - else if (opt.list_sigs + else if ((opt.list_sigs + || (opt.list_options + & (LIST_SHOW_X509_NOTATIONS|LIST_STORE_X509_NOTATIONS))) && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { kbnode_t n; @@ -1627,7 +1723,8 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, qsort (sigarray, sigcount, sizeof *sigarray, cmp_signodes); for (idx=0; idx < sigcount; idx++) - list_signature_print (ctrl, keyblock, sigarray[idx], listctx); + list_signature_print (ctrl, keyblock, sigarray[idx], listctx, + lastpk); xfree (sigarray); } } @@ -2232,11 +2329,18 @@ reorder_keyblock (KBNODE keyblock) } -static void +/* Note: If this function returns an error the caller is expected to + * honor this and stop all further processing. Any error returned + * will be a write error (to stdout) and a diagnostics is always + * printed using log_error. */ +static gpg_error_t list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { + gpg_error_t err = 0; + + es_clearerr (es_stdout); reorder_keyblock (keyblock); if (list_filter.selkey) @@ -2254,7 +2358,7 @@ list_keyblock (ctrl_t ctrl, } } if (!selected) - return; /* Skip this one. */ + return 0; /* Skip this one. */ } if (opt.with_colons) @@ -2268,24 +2372,34 @@ list_keyblock (ctrl_t ctrl, else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); - if (secret) - es_fflush (es_stdout); + if (es_ferror (es_stdout)) + err = gpg_error_from_syserror (); + + if (secret && es_fflush (es_stdout) && !err) + err = gpg_error_from_syserror (); + + if (err) + log_error (_("error writing to stdout: %s\n"), gpg_strerror (err)); + + return err; } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ -void +gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; + gpg_error_t err; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; - list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); + err = list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); + return err; } diff --git a/g10/keyring.c b/g10/keyring.c index baddf5d54..0fe8bcd9c 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1148,7 +1148,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, if (need_keyid) keyid_from_pk (pk, aki); if (need_grip) - keygrip_from_pk (pk, grip); + keygrip_from_pk (pk, grip, 0); if (use_key_present_hash && !key_present_hash_ready diff --git a/g10/main.h b/g10/main.h index 2482fbde2..2443aa7fe 100644 --- a/g10/main.h +++ b/g10/main.h @@ -108,7 +108,7 @@ char *make_radix64_string( const byte *data, size_t len ); void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); -int is_secured_file (int fd); +int is_secured_file (gnupg_fd_t fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( const byte *p, unsigned n ); @@ -243,9 +243,9 @@ aead_algo_t use_aead (pk_list_t pk_list, int algo); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); -int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, +int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, - int outputfd); + gnupg_fd_t outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, @@ -341,7 +341,7 @@ gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); -int open_outfile (int out_fd, const char *iname, int mode, +int open_outfile (gnupg_fd_t out_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); @@ -470,8 +470,8 @@ void secret_key_list (ctrl_t ctrl, strlist_t list ); gpg_error_t parse_and_set_list_filter (const char *string); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); -void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, - int has_secret, int fpr, int no_validity); +gpg_error_t list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, + int has_secret, int fpr, int no_validity); int cmp_signodes (const void *av, const void *bv); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); @@ -493,20 +493,22 @@ void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); -int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); +int gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd, + estream_t out_fp); void check_assert_signer_list (const char *mainpkhex, const char *pkhex); void check_assert_pubkey_algo (const char *algostr, const char *pkhex); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); -gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); +gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, + gnupg_fd_t output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); -int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, - int textmode ); +int hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, + gnupg_fd_t data_fd, int textmode); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ diff --git a/g10/mainproc.c b/g10/mainproc.c index e722618ca..91ababbb6 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -81,7 +81,7 @@ struct mainproc_context struct { /* A file descriptor of the signed data. Only used if not -1. */ - int data_fd; + gnupg_fd_t data_fd; /* A list of filenames with the data files or NULL. This is only used if DATA_FD is -1. */ strlist_t data_names; @@ -143,6 +143,8 @@ release_list( CTX c ) mpi_release (c->pkenc_list->data[0]); mpi_release (c->pkenc_list->data[1]); + mpi_release (c->pkenc_list->data[2]); + mpi_release (c->pkenc_list->data[3]); xfree (c->pkenc_list); c->pkenc_list = tmp; } @@ -527,11 +529,14 @@ proc_pubkey_enc (CTX c, PACKET *pkt) x->keyid[1] = enc->keyid[1]; x->pubkey_algo = enc->pubkey_algo; x->result = -1; - x->data[0] = x->data[1] = NULL; + x->seskey_algo = enc->seskey_algo; + x->data[0] = x->data[1] = x->data[2] = x->data[3] = NULL; if (enc->data[0]) { x->data[0] = mpi_copy (enc->data[0]); x->data[1] = mpi_copy (enc->data[1]); + x->data[2] = mpi_copy (enc->data[2]); + x->data[3] = mpi_copy (enc->data[3]); } x->next = c->pkenc_list; c->pkenc_list = x; @@ -573,6 +578,10 @@ print_pkenc_list (ctrl_t ctrl, struct pubkey_enc_list *list) openpgp_pk_algo_name (list->pubkey_algo), keystr(list->keyid)); + if (opt.flags.require_pqc_encryption + && pk->pubkey_algo != PUBKEY_ALGO_KYBER) + log_info (_("WARNING: key is not quantum-resistant\n")); + free_public_key (pk); } } @@ -1097,7 +1106,7 @@ static int proc_compressed_cb (iobuf_t a, void *info) { if ( ((CTX)info)->signed_data.used - && ((CTX)info)->signed_data.data_fd != -1) + && ((CTX)info)->signed_data.data_fd != GNUPG_INVALID_FD) return proc_signature_packets_by_fd (((CTX)info)->ctrl, info, a, ((CTX)info)->signed_data.data_fd); else @@ -1519,7 +1528,7 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, c->anchor = anchor; c->sigs_only = 1; - c->signed_data.data_fd = -1; + c->signed_data.data_fd = GNUPG_INVALID_FD; c->signed_data.data_names = signedfiles; c->signed_data.used = !!signedfiles; @@ -1549,8 +1558,8 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, int -proc_signature_packets_by_fd (ctrl_t ctrl, - void *anchor, iobuf_t a, int signed_data_fd ) +proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a, + gnupg_fd_t signed_data_fd) { int rc; CTX c; @@ -1565,7 +1574,7 @@ proc_signature_packets_by_fd (ctrl_t ctrl, c->signed_data.data_fd = signed_data_fd; c->signed_data.data_names = NULL; - c->signed_data.used = (signed_data_fd != -1); + c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD); rc = do_proc_packets (c, a); @@ -2549,8 +2558,6 @@ check_sig_and_print (CTX c, kbnode_t node) release_kbnode( keyblock ); if (rc) g10_errors_seen = 1; - if (opt.batch && rc) - g10_exit (1); } else /* Error checking the signature. (neither Good nor Bad). */ { @@ -2636,7 +2643,8 @@ proc_tree (CTX c, kbnode_t node) /* Ask for file and hash it. */ if (c->sigs_only) { - if (c->signed_data.used && c->signed_data.data_fd != -1) + if (c->signed_data.used + && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, NULL, c->signed_data.data_fd, use_textmode); @@ -2667,7 +2675,8 @@ proc_tree (CTX c, kbnode_t node) } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) - check_sig_and_print (c, n1); + if (check_sig_and_print (c, n1) && opt.batch) + break; } else if (node->pkt->pkttype == PKT_GPG_CONTROL @@ -2686,8 +2695,8 @@ proc_tree (CTX c, kbnode_t node) } for (n1 = node; (n1 = find_next_kbnode (n1, PKT_SIGNATURE));) - check_sig_and_print (c, n1); - + if (check_sig_and_print (c, n1) && opt.batch) + break; } else if (node->pkt->pkttype == PKT_SIGNATURE) { @@ -2779,7 +2788,8 @@ proc_tree (CTX c, kbnode_t node) if (c->sigs_only) { - if (c->signed_data.used && c->signed_data.data_fd != -1) + if (c->signed_data.used + && c->signed_data.data_fd != GNUPG_INVALID_FD) rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2, c->signed_data.data_fd, (sig->sig_class == 0x01)); @@ -2814,7 +2824,8 @@ proc_tree (CTX c, kbnode_t node) if (multiple_ok) { for (n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE))) - check_sig_and_print (c, n1); + if (check_sig_and_print (c, n1) && opt.batch) + break; } else check_sig_and_print (c, node); diff --git a/g10/mdfilter.c b/g10/mdfilter.c index f3318f15c..a655d6d72 100644 --- a/g10/mdfilter.c +++ b/g10/mdfilter.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <npth.h> #include "gpg.h" #include "../common/status.h" @@ -71,3 +72,297 @@ free_md_filter_context( md_filter_context_t *mfx ) mfx->md2 = NULL; mfx->maxbuf_size = 0; } + + +/**************** + * Threaded implementation for hashing. + */ + +struct md_thd_filter_context { + gcry_md_hd_t md; + npth_t thd; + /**/ + npth_mutex_t mutex; + npth_cond_t cond; + size_t bufsize; + unsigned int produce : 1; + unsigned int consume : 1; + ssize_t written0; + ssize_t written1; + unsigned char buf[1]; +}; + + +static void +lock_md (struct md_thd_filter_context *mfx) +{ + int rc = npth_mutex_lock (&mfx->mutex); + if (rc) + log_fatal ("%s: failed to acquire mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + + +static void +unlock_md (struct md_thd_filter_context * mfx) +{ + int rc = npth_mutex_unlock (&mfx->mutex); + if (rc) + log_fatal ("%s: failed to release mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + +static int +get_buffer_to_hash (struct md_thd_filter_context *mfx, + unsigned char **r_buf, size_t *r_len) +{ + int rc = 0; + + lock_md (mfx); + + if ((mfx->consume == 0 && mfx->written0 < 0) + || (mfx->consume != 0 && mfx->written1 < 0)) + { + rc = npth_cond_wait (&mfx->cond, &mfx->mutex); + if (rc) + { + unlock_md (mfx); + return -1; + } + } + + if (mfx->consume == 0) + { + *r_buf = mfx->buf; + *r_len = mfx->written0; + } + else + { + *r_buf = mfx->buf + mfx->bufsize; + *r_len = mfx->written1; + } + + unlock_md (mfx); + + return 0; +} + +static int +put_buffer_to_recv (struct md_thd_filter_context *mfx) +{ + int rc = 0; + + lock_md (mfx); + if (mfx->consume == 0) + { + mfx->written0 = -1; + mfx->consume = 1; + } + else + { + mfx->written1 = -1; + mfx->consume = 0; + } + + rc = npth_cond_signal (&mfx->cond); + if (rc) + { + unlock_md (mfx); + return -1; + } + + unlock_md (mfx); + return 0; +} + +static int +get_buffer_to_fill (struct md_thd_filter_context *mfx, + unsigned char **r_buf, size_t len) +{ + lock_md (mfx); + + if (len > mfx->bufsize) + { + unlock_md (mfx); + return GPG_ERR_BUFFER_TOO_SHORT; + } + + if ((mfx->produce == 0 && mfx->written0 >= 0) + || (mfx->produce != 0 && mfx->written1 >= 0)) + { + int rc = npth_cond_wait (&mfx->cond, &mfx->mutex); + if (rc) + { + unlock_md (mfx); + return gpg_error_from_errno (rc); + } + } + + if (mfx->produce == 0) + *r_buf = mfx->buf; + else + *r_buf = mfx->buf + mfx->bufsize; + unlock_md (mfx); + return 0; +} + +static int +put_buffer_to_send (struct md_thd_filter_context *mfx, size_t len) +{ + int rc; + + lock_md (mfx); + if (mfx->produce == 0) + { + mfx->written0 = len; + mfx->produce = 1; + } + else + { + mfx->written1 = len; + mfx->produce = 0; + } + + rc = npth_cond_signal (&mfx->cond); + if (rc) + { + unlock_md (mfx); + return gpg_error_from_errno (rc); + } + + unlock_md (mfx); + + /* Yield to the md_thread to let it compute the hash in parallel */ + npth_usleep (0); + return 0; +} + + +static void * +md_thread (void *arg) +{ + struct md_thd_filter_context *mfx = arg; + + while (1) + { + unsigned char *buf; + size_t len; + + if (get_buffer_to_hash (mfx, &buf, &len) < 0) + /* Error */ + return NULL; + + if (len == 0) + break; + + npth_unprotect (); + gcry_md_write (mfx->md, buf, len); + npth_protect (); + + if (put_buffer_to_recv (mfx) < 0) + /* Error */ + return NULL; + } + + return NULL; +} + +int +md_thd_filter (void *opaque, int control, + IOBUF a, byte *buf, size_t *ret_len) +{ + size_t size = *ret_len; + struct md_thd_filter_context **r_mfx = opaque; + struct md_thd_filter_context *mfx = *r_mfx; + int rc=0; + + if (control == IOBUFCTRL_INIT) + { + npth_attr_t tattr; + size_t n; + + n = 2 * iobuf_set_buffer_size (0) * 1024; + mfx = xtrymalloc (n + offsetof (struct md_thd_filter_context, buf)); + if (!mfx) + return gpg_error_from_syserror (); + *r_mfx = mfx; + mfx->bufsize = n / 2; + mfx->consume = mfx->produce = 0; + mfx->written0 = -1; + mfx->written1 = -1; + + rc = npth_mutex_init (&mfx->mutex, NULL); + if (rc) + { + return gpg_error_from_errno (rc); + } + rc = npth_cond_init (&mfx->cond, NULL); + if (rc) + { + npth_mutex_destroy (&mfx->mutex); + return gpg_error_from_errno (rc); + } + rc = npth_attr_init (&tattr); + if (rc) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + return gpg_error_from_errno (rc); + } + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + rc = npth_create (&mfx->thd, &tattr, md_thread, mfx); + if (rc) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + npth_attr_destroy (&tattr); + return gpg_error_from_errno (rc); + } + npth_attr_destroy (&tattr); + } + else if (control == IOBUFCTRL_UNDERFLOW) + { + int i; + unsigned char *md_buf = NULL; + + i = iobuf_read (a, buf, size); + if (i == -1) + i = 0; + + rc = get_buffer_to_fill (mfx, &md_buf, i); + if (rc) + return rc; + + if (i) + memcpy (md_buf, buf, i); + + rc = put_buffer_to_send (mfx, i); + if (rc) + return rc; + + if (i == 0) + { + npth_join (mfx->thd, NULL); + rc = -1; /* eof */ + } + + *ret_len = i; + } + else if (control == IOBUFCTRL_FREE) + { + npth_cond_destroy (&mfx->cond); + npth_mutex_destroy (&mfx->mutex); + xfree (mfx); + *r_mfx = NULL; + } + else if (control == IOBUFCTRL_DESC) + mem2str (buf, "md_thd_filter", *ret_len); + + return rc; +} + +void +md_thd_filter_set_md (struct md_thd_filter_context *mfx, gcry_md_hd_t md) +{ + mfx->md = md; +} diff --git a/g10/misc.c b/g10/misc.c index 2f4b452dd..c52091830 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -160,7 +160,7 @@ unregister_secured_file (const char *fname) /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int -is_secured_file (int fd) +is_secured_file (gnupg_fd_t fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; @@ -750,6 +750,8 @@ openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) ga = GCRY_PK_ELG; break; + case PUBKEY_ALGO_KYBER: ga = GCRY_PK_KEM; break; + default: break; } @@ -799,6 +801,18 @@ openpgp_pk_algo_usage ( int algo ) case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; + break; + + case PUBKEY_ALGO_KYBER: + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; + break; + + case PUBKEY_ALGO_DIL3_25519: + case PUBKEY_ALGO_DIL5_448: + case PUBKEY_ALGO_SPHINX_SHA2: + use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; + break; + default: break; } @@ -822,6 +836,7 @@ openpgp_pk_algo_name (pubkey_algo_t algo) case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; + case PUBKEY_ALGO_KYBER: return "Kyber"; default: return "?"; } } @@ -1711,6 +1726,7 @@ pubkey_get_npkey (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; + case PUBKEY_ALGO_KYBER: return 3; default: return 0; } } @@ -1731,6 +1747,7 @@ pubkey_get_nskey (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; + case PUBKEY_ALGO_KYBER: return 5; default: return 0; } } @@ -1770,6 +1787,7 @@ pubkey_get_nenc (pubkey_algo_t algo) case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; + case PUBKEY_ALGO_KYBER: return 3; default: return 0; } } diff --git a/g10/openfile.c b/g10/openfile.c index 5ca168a13..01f323399 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -179,13 +179,13 @@ ask_outfile_name( const char *name, size_t namelen ) * be closed if the returned IOBUF is closed. This is used for gpg's * --server mode. */ int -open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, - iobuf_t *a) +open_outfile (gnupg_fd_t out_fd, const char *iname, int mode, + int restrictedperm, iobuf_t *a) { int rc = 0; *a = NULL; - if (out_fd != -1) + if (out_fd != GNUPG_INVALID_FD) { char xname[64]; @@ -193,12 +193,12 @@ open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, if (!*a) { rc = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", out_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { - snprintf (xname, sizeof xname, "[fd %d]", out_fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (out_fd)); log_info (_("writing to '%s'\n"), xname); } } diff --git a/g10/options.h b/g10/options.h index 50fa4ad86..ae429fcc1 100644 --- a/g10/options.h +++ b/g10/options.h @@ -283,6 +283,7 @@ struct /* Fail if an operation can't be done in the requested compliance * mode. */ unsigned int require_compliance:1; + unsigned int require_pqc_encryption:1; } flags; /* Linked list of ways to find a key if the key isn't on the local @@ -378,7 +379,8 @@ 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_PARALLELIZED 1 /* Use threaded hashing for signatures. */ +#define COMPAT_T7014_OLD 2 /* Use initial T7014 test data. */ /* Compliance test macors. */ @@ -442,6 +444,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SHOW_PREF (1<<14) #define LIST_SHOW_PREF_VERBOSE (1<<15) #define LIST_SHOW_UNUSABLE_SIGS (1<<16) +#define LIST_SHOW_X509_NOTATIONS (1<<17) +#define LIST_STORE_X509_NOTATIONS (1<<18) #define LIST_SHOW_OWNERTRUST (1<<19) #define VERIFY_SHOW_PHOTOS (1<<0) diff --git a/g10/packet.h b/g10/packet.h index 39dab96c9..459e38dda 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -137,6 +137,8 @@ typedef struct { byte version; /* The algorithm used for the public key encryption scheme. */ byte pubkey_algo; + /* The session key algorithm used by some pubkey algos. */ + byte seskey_algo; /* Whether to hide the key id. This value is not directly serialized. */ byte throw_keyid; @@ -151,6 +153,7 @@ struct pubkey_enc_list struct pubkey_enc_list *next; u32 keyid[2]; int pubkey_algo; + int seskey_algo; int result; gcry_mpi_t data[PUBKEY_MAX_NENC]; }; @@ -342,11 +345,11 @@ struct revoke_info /* Information pertaining to secret keys. */ struct seckey_info { - int is_protected:1; /* The secret info is protected and must */ + unsigned int is_protected:1; /* The secret info is protected and must */ /* be decrypted before use, the protected */ /* MPIs are simply (void*) pointers to memory */ /* and should never be passed to a mpi_xxx() */ - int sha1chk:1; /* SHA1 is used instead of a 16 bit checksum */ + unsigned int sha1chk:1; /* SHA1 is used instead of a 16 bit checksum */ u16 csum; /* Checksum for old protection modes. */ byte algo; /* Cipher used to protect the secret information. */ STRING2KEY s2k; /* S2K parameter. */ @@ -602,8 +605,8 @@ struct notation /* Sometimes we want to %-expand the value. In these cases, we save that transformed value here. */ char *altvalue; - /* If the notation is not human readable, then the value is stored - here. */ + /* If the notation is not human readable or the function does not + want to distinguish that, then the value is stored here. */ unsigned char *bdat; /* The amount of data stored in BDAT. @@ -634,8 +637,8 @@ void reset_literals_seen(void); int proc_packets (ctrl_t ctrl, void *ctx, iobuf_t a ); int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a, strlist_t signedfiles, const char *sigfile ); -int proc_signature_packets_by_fd (ctrl_t ctrl, - void *anchor, IOBUF a, int signed_data_fd ); +int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a, + gnupg_fd_t signed_data_fd); int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a); int list_packets( iobuf_t a ); @@ -863,7 +866,9 @@ gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf); int build_packet (iobuf_t out, PACKET *pkt); gpg_error_t build_packet_and_meta (iobuf_t out, PACKET *pkt); gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a, unsigned int *t_nwritten); -gpg_error_t gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write_opaque_nohdr (iobuf_t out, gcry_mpi_t a); +gpg_error_t gpg_mpi_write_opaque_32 (iobuf_t out, gcry_mpi_t a, + unsigned int *r_nwritten); u32 calc_packet_length( PACKET *pkt ); void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type, const byte *buffer, size_t buflen ); @@ -877,7 +882,8 @@ struct notation *string_to_notation(const char *string,int is_utf8); struct notation *blob_to_notation(const char *name, const char *data, size_t len); struct notation *sig_to_notation(PKT_signature *sig); -void free_notation(struct notation *notation); +struct notation *search_sig_notations (PKT_signature *sig, const char *name); +void free_notation (struct notation *notation); /*-- free-packet.c --*/ void free_symkey_enc( PKT_symkey_enc *enc ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index aa6bac9da..8bd283b4b 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -188,6 +188,109 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure) } +/* If NLENGTH is zero read an octet string of length NBYTES from INP + * and return it at R_DATA. + * + * If NLENGTH is either 1, 2, or 4 and NLENGTH is zero read an + * NLENGTH-octet count and use this count number octets from INP and + * return it at R_DATA. + * + * On error return an error code and store NULL at R_DATA. PKTLEN + * shall give the current length of the packet and is updated with + * each read. If SECURE is true, the integer is stored in secure + * memory (allocated using gcry_xmalloc_secure). + */ +static gpg_error_t +read_octet_string (iobuf_t inp, unsigned long *pktlen, + unsigned int nlength, unsigned int nbytes, + int secure, gcry_mpi_t *r_data) +{ + gpg_error_t err; + int c, i; + byte *buf = NULL; + byte *p; + + *r_data = NULL; + + if ((nbytes && nlength) + || (!nbytes && !(nlength == 1 || nlength == 2 || nlength == 4))) + { + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + if (nlength) + { + for (i = 0; i < nlength; i++) + { + if (!*pktlen) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + c = iobuf_readbyte (inp); + if (c < 0) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + --*pktlen; + nbytes <<= 8; + nbytes |= c; + } + + if (!nbytes) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + } + + if (nbytes*8 > (nbytes==4? MAX_EXTERN_KEYPARM_BITS:MAX_EXTERN_MPI_BITS) + || (nbytes*8 < nbytes)) + { + log_error ("octet string too large (%u octets)\n", nbytes); + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + if (nbytes > *pktlen) + { + log_error ("octet string larger than packet (%u octets)\n", nbytes); + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + buf = secure ? gcry_malloc_secure (nbytes) : gcry_malloc (nbytes); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + p = buf; + for (i = 0; i < nbytes; i++) + { + c = iobuf_get (inp); + if (c == -1) + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + p[i] = c; + --*pktlen; + } + + *r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8); + gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2); + return 0; + + leave: + gcry_free (buf); + return err; +} + + /* Read an external representation of an SOS and return the opaque MPI with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned value stored in network byte order giving information for the @@ -1102,32 +1205,29 @@ read_rest (IOBUF inp, size_t pktlen) /* Read a special size+body from INP. On success store an opaque MPI - with it at R_DATA. On error return an error code and store NULL at - R_DATA. Even in the error case store the number of read bytes at - R_NREAD. The caller shall pass the remaining size of the packet in - PKTLEN. */ + * with it at R_DATA. The caller shall store the remaining size of + * the packet at PKTLEN. On error return an error code and store NULL + * at R_DATA. Even in the error case store the number of read bytes + * at PKTLEN is updated. */ static gpg_error_t -read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, - gcry_mpi_t *r_data) +read_sized_octet_string (iobuf_t inp, unsigned long *pktlen, gcry_mpi_t *r_data) { char buffer[256]; char *tmpbuf; int i, c, nbytes; - *r_nread = 0; *r_data = NULL; - if (!pktlen) + if (!*pktlen) return gpg_error (GPG_ERR_INV_PACKET); c = iobuf_readbyte (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); - pktlen--; - ++*r_nread; + --*pktlen; nbytes = c; if (nbytes < 2 || nbytes > 254) return gpg_error (GPG_ERR_INV_PACKET); - if (nbytes > pktlen) + if (nbytes > *pktlen) return gpg_error (GPG_ERR_INV_PACKET); buffer[0] = nbytes; @@ -1137,7 +1237,7 @@ read_size_body (iobuf_t inp, int pktlen, size_t *r_nread, c = iobuf_get (inp); if (c < 0) return gpg_error (GPG_ERR_INV_PACKET); - ++*r_nread; + --*pktlen; buffer[1+i] = c; } @@ -1344,12 +1444,14 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } +/* Parse a public key encrypted packet (Tag 1). */ static int parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet) { int rc = 0; int i, ndata; + unsigned int n; PKT_pubkey_enc *k; k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc); @@ -1392,46 +1494,78 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, unknown_pubkey_warning (k->pubkey_algo); k->data[0] = NULL; /* No need to store the encrypted data. */ } + else if (k->pubkey_algo == PUBKEY_ALGO_ECDH) + { + log_assert (ndata == 2); + /* Get the ephemeral public key. */ + n = pktlen; + k->data[0] = sos_read (inp, &n, 0); + pktlen -= n; + if (!k->data[0]) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + /* Get the wrapped symmetric key. */ + rc = read_sized_octet_string (inp, &pktlen, k->data + 1); + if (rc) + goto leave; + } + else if (k->pubkey_algo == PUBKEY_ALGO_KYBER) + { + log_assert (ndata == 3); + /* Get the ephemeral public key. */ + n = pktlen; + k->data[0] = sos_read (inp, &n, 0); + pktlen -= n; + if (!k->data[0]) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + /* Get the Kyber ciphertext. */ + rc = read_octet_string (inp, &pktlen, 4, 0, 0, k->data + 1); + if (rc) + goto leave; + /* Get the algorithm id for the session key. */ + if (!pktlen) + { + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + k->seskey_algo = iobuf_get_noeof (inp); + pktlen--; + /* Get the encrypted symmetric key. */ + rc = read_octet_string (inp, &pktlen, 1, 0, 0, k->data + 2); + if (rc) + goto leave; + } else { for (i = 0; i < ndata; i++) { - if (k->pubkey_algo == PUBKEY_ALGO_ECDH) - { - if (i == 1) - { - size_t n; - rc = read_size_body (inp, pktlen, &n, k->data+i); - pktlen -= n; - } - else - { - int n = pktlen; - k->data[i] = sos_read (inp, &n, 0); - pktlen -= n; - if (!k->data[i]) - rc = gpg_error (GPG_ERR_INV_PACKET); - } - } - else - { - int n = pktlen; - k->data[i] = mpi_read (inp, &n, 0); - pktlen -= n; - if (!k->data[i]) - rc = gpg_error (GPG_ERR_INV_PACKET); - } - if (rc) - goto leave; - if (list_mode) - { - es_fprintf (listfp, "\tdata: "); - mpi_print (listfp, k->data[i], mpi_print_mode); - es_putc ('\n', listfp); - } + n = pktlen; + k->data[i] = mpi_read (inp, &n, 0); + pktlen -= n; + if (!k->data[i]) + rc = gpg_error (GPG_ERR_INV_PACKET); + } + if (rc) + goto leave; + } + if (list_mode) + { + if (k->seskey_algo) + es_fprintf (listfp, "\tsession key algo: %d\n", k->seskey_algo); + for (i = 0; i < ndata; i++) + { + es_fprintf (listfp, "\tdata: "); + mpi_print (listfp, k->data[i], mpi_print_mode); + es_putc ('\n', listfp); } } + leave: iobuf_skip_rest (inp, pktlen, 0); return rc; @@ -2598,19 +2732,25 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, { if ( (algorithm == PUBKEY_ALGO_ECDSA && (i == 0)) || (algorithm == PUBKEY_ALGO_EDDSA && (i == 0)) - || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) + || (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)) + || (algorithm == PUBKEY_ALGO_KYBER && (i == 0))) { /* Read the OID (i==0) or the KDF params (i==2). */ - size_t n; - err = read_size_body (inp, pktlen, &n, pk->pkey+i); - pktlen -= n; + err = read_sized_octet_string (inp, &pktlen, pk->pkey+i); + } + else if (algorithm == PUBKEY_ALGO_KYBER && i == 2) + { + /* Read the four-octet count prefixed Kyber public key. */ + err = read_octet_string (inp, &pktlen, 4, 0, 0, pk->pkey+i); } else { + /* Read MPI or SOS. */ unsigned int n = pktlen; if (algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) pk->pkey[i] = sos_read (inp, &n, 0); else pk->pkey[i] = mpi_read (inp, &n, 0); @@ -2626,7 +2766,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, mpi_print (listfp, pk->pkey[i], mpi_print_mode); if ((algorithm == PUBKEY_ALGO_ECDSA || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) && i==0) + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) && i==0) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); @@ -2963,21 +3104,32 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, /* Not encrypted. */ for (i = npkey; i < nskey; i++) { - unsigned int n; if (pktlen < 2) /* At least two bytes for the length. */ { err = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - n = pktlen; - if (algorithm == PUBKEY_ALGO_ECDSA - || algorithm == PUBKEY_ALGO_EDDSA - || algorithm == PUBKEY_ALGO_ECDH) - pk->pkey[i] = sos_read (inp, &n, 0); + if (algorithm == PUBKEY_ALGO_KYBER && i == npkey+1) + { + err = read_octet_string (inp, &pktlen, 4, 0, 1, pk->pkey+i); + if (err) + goto leave; + } else - pk->pkey[i] = mpi_read (inp, &n, 0); - pktlen -= n; + { + unsigned int n = pktlen; + + if (algorithm == PUBKEY_ALGO_ECDSA + || algorithm == PUBKEY_ALGO_EDDSA + || algorithm == PUBKEY_ALGO_ECDH + || algorithm == PUBKEY_ALGO_KYBER) + pk->pkey[i] = sos_read (inp, &n, 0); + else + pk->pkey[i] = mpi_read (inp, &n, 0); + pktlen -= n; + } + if (list_mode) { es_fprintf (listfp, "\tskey[%d]: ", i); diff --git a/g10/photoid.c b/g10/photoid.c index fc8866121..8cc7e3a20 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -27,9 +27,6 @@ # include <winsock2.h> # endif # include <windows.h> -# ifndef VER_PLATFORM_WIN32_WINDOWS -# define VER_PLATFORM_WIN32_WINDOWS 1 -# endif #endif #include "gpg.h" @@ -95,8 +92,15 @@ w32_system (const char *command) return -1; } if (DBG_EXTPROG) - log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", - see.hProcess, (int)see.hInstApp); + { + /* hInstApp has HINSTANCE type. The documentations says + that it's not a true HINSTANCE and it can be cast only to + an int. */ + int hinstance = (intptr_t)see.hInstApp; + + log_debug ("ShellExecuteEx succeeded (hProcess=%p,hInstApp=%d)\n", + see.hProcess, hinstance); + } if (!see.hProcess) { @@ -381,16 +385,7 @@ static const char * get_default_photo_command(void) { #if defined(_WIN32) - OSVERSIONINFO osvi; - - memset(&osvi,0,sizeof(osvi)); - osvi.dwOSVersionInfoSize=sizeof(osvi); - GetVersionEx(&osvi); - - if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) - return "start /w %i"; - else - return "!ShellExecute 400 %i"; + return "!ShellExecute 400 %i"; #elif defined(__APPLE__) /* OS X. This really needs more than just __APPLE__. */ return "open %I"; @@ -600,34 +595,35 @@ run_with_pipe (struct spawn_info *info, const void *image, u32 len) " external programs\n")); return; #else /* !EXEC_TEMPFILE_ONLY */ - int to[2]; - pid_t pid; gpg_error_t err; const char *argv[4]; - - err = gnupg_create_pipe (to); - if (err) - return; + gnupg_process_t proc; fill_command_argv (argv, info->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, to[0], -1, -1, &pid); - - close (to[0]); - + err = gnupg_process_spawn (argv[0], argv+1, GNUPG_PROCESS_STDIN_PIPE, + NULL, NULL, &proc); if (err) - { - log_error (_("unable to execute shell '%s': %s\n"), - argv[0], gpg_strerror (err)); - close (to[1]); - } + log_error (_("unable to execute shell '%s': %s\n"), + argv[0], gpg_strerror (err)); else { - write (to[1], image, len); - close (to[1]); + int fd_in; + + err = gnupg_process_get_fds (proc, 0, &fd_in, NULL, NULL); + if (err) + log_error ("unable to get pipe connection '%s': %s\n", + argv[2], gpg_strerror (err)); + else + { + write (fd_in, image, len); + close (fd_in); + } - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_wait (proc, 1); if (err) log_error (_("unnatural exit of external program\n")); + + gnupg_process_release (proc); } #endif /* !EXEC_TEMPFILE_ONLY */ } @@ -695,14 +691,11 @@ show_photo (const char *command, const char *name, const void *image, u32 len) log_error (_("system error while calling external program: %s\n"), strerror (errno)); #else - pid_t pid; gpg_error_t err; const char *argv[4]; fill_command_argv (argv, spawn->command); - err = gnupg_spawn_process_fd (argv[0], argv+1, -1, -1, -1, &pid); - if (!err) - err = gnupg_wait_process (argv[0], pid, 1, NULL); + err = gnupg_process_spawn (argv[0], argv+1, 0, NULL, NULL, NULL); if (err) log_error (_("unnatural exit of external program\n")); #endif diff --git a/g10/pkglue.c b/g10/pkglue.c index f18313913..f4efa8fc5 100644 --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -1,6 +1,7 @@ /* pkglue.c - public key operations glue code * Copyright (C) 2000, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch + * Copyright (C) 2024 g10 Code GmbH. * * This file is part of GnuPG. * @@ -16,6 +17,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> @@ -30,6 +32,12 @@ #include "main.h" #include "options.h" + +/* Maximum buffer sizes required for ECC KEM. */ +#define ECC_POINT_LEN_MAX (1+2*64) +#define ECC_HASH_LEN_MAX 64 + + /* FIXME: Better change the function name because mpi_ is used by gcrypt macros. */ gcry_mpi_t @@ -415,140 +423,505 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash, } +#if GCRY_KEM_MLKEM1024_ENCAPS_LEN < GCRY_KEM_MLKEM768_ENCAPS_LEN \ + || GCRY_KEM_MLKEM1024_SHARED_LEN < GCRY_KEM_MLKEM768_SHARED_LEN +# error Bad Kyber constants in Libgcrypt +#endif - -/**************** - * Emulate our old PK interface here - sometime in the future we might - * change the internal design to directly fit to libgcrypt. - * PK is only required to compute the fingerprint for ECDH. - */ -int -pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, - PKT_public_key *pk, gcry_mpi_t *pkey) +/* Core of the encryption for KEM algorithms. See pk_decrypt for a + * description of the arguments. */ +static gpg_error_t +do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr) { - gcry_sexp_t s_ciph = NULL; + gpg_error_t err; + int i; + unsigned int nbits, n; gcry_sexp_t s_data = NULL; - gcry_sexp_t s_pkey = NULL; - int rc; - - /* Make a sexp from pkey. */ - if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) - { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(elg(p%m)(g%m)(y%m)))", - pkey[0], pkey[1], pkey[2]); - /* Put DATA into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", data); - } - else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) + gcry_cipher_hd_t hd = NULL; + char *ecc_oid = NULL; + enum gcry_kem_algos kyber_algo, ecc_algo; + + const unsigned char *ecc_pubkey; + size_t ecc_pubkey_len; + const unsigned char *kyber_pubkey; + size_t kyber_pubkey_len; + const unsigned char *seskey; + size_t seskey_len; + unsigned char *enc_seskey = NULL; + size_t enc_seskey_len; + int ecc_hash_algo; + + unsigned char ecc_ct[ECC_POINT_LEN_MAX]; + unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; + unsigned char ecc_ss[ECC_HASH_LEN_MAX]; + size_t ecc_ct_len, ecc_ecdh_len, ecc_ss_len; + + unsigned char kyber_ct[GCRY_KEM_MLKEM1024_ENCAPS_LEN]; + unsigned char kyber_ss[GCRY_KEM_MLKEM1024_SHARED_LEN]; + size_t kyber_ct_len, kyber_ss_len; + + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + unsigned char kek[32]; /* AES-256 is mandatory. */ + size_t kek_len = 32; + + /* For later error checking we make sure the array is cleared. */ + resarr[0] = resarr[1] = resarr[2] = NULL; + + /* As of now we use KEM only for the combined Kyber and thus a + * second public key is expected. Right now we take the keys + * directly from the PK->data elements. */ + + ecc_oid = openpgp_oid_to_str (pk->pkey[0]); + if (!ecc_oid) { - rc = gcry_sexp_build (&s_pkey, NULL, - "(public-key(rsa(n%m)(e%m)))", - pkey[0], pkey[1]); - /* Put DATA into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", data); + err = gpg_error_from_syserror (); + log_error ("%s: error getting OID for ECC key\n", __func__); + goto leave; } - else if (algo == PUBKEY_ALGO_ECDH) + ecc_algo = openpgp_oid_to_kem_algo (ecc_oid); + if (ecc_algo == GCRY_KEM_RAW_X25519) { - gcry_mpi_t k; - - rc = pk_ecdh_generate_ephemeral_key (pkey, &k); - if (!rc) + if (!strcmp (ecc_oid, "1.3.6.1.4.1.3029.1.5.1")) + log_info ("Warning: " + "legacy OID for cv25519 accepted during develpment\n"); + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) { - char *curve; - - curve = openpgp_oid_to_str (pkey[0]); - if (!curve) - rc = gpg_error_from_syserror (); - else - { - int with_djb_tweak_flag = openpgp_oid_is_cv25519 (pkey[0]); - - /* Now use the ephemeral secret to compute the shared point. */ - rc = gcry_sexp_build (&s_pkey, NULL, - with_djb_tweak_flag ? - "(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))" - : "(public-key(ecdh(curve%s)(q%m)))", - curve, pkey[1]); - xfree (curve); - /* Put K into a simplified S-expression. */ - if (!rc) - rc = gcry_sexp_build (&s_data, NULL, "%m", k); - } - gcry_mpi_release (k); + ecc_pubkey++; /* Remove the 0x40 prefix. */ + ecc_pubkey_len--; } - } - else - rc = gpg_error (GPG_ERR_PUBKEY_ALGO); - - /* Pass it to libgcrypt. */ - if (!rc) - rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); - - gcry_sexp_release (s_data); - gcry_sexp_release (s_pkey); - - if (rc) - ; - else if (algo == PUBKEY_ALGO_ECDH) - { - gcry_mpi_t public, result; - byte fp[MAX_FINGERPRINT_LEN]; - byte *shared; - size_t nshared; - - /* Get the shared point and the ephemeral public key. */ - shared = get_data_from_sexp (s_ciph, "s", &nshared); - if (!shared) + if (ecc_pubkey_len != 32) { - rc = gpg_error_from_syserror (); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); goto leave; } - rc = sexp_extract_param_sos (s_ciph, "e", &public); - gcry_sexp_release (s_ciph); - s_ciph = NULL; - if (DBG_CRYPTO) + ecc_ct_len = ecc_ecdh_len = 32; + ecc_ss_len = 32; + ecc_hash_algo = GCRY_MD_SHA3_256; + } + else if (ecc_algo == GCRY_KEM_RAW_X448) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 56) { - log_debug ("ECDH ephemeral key:"); - gcry_mpi_dump (public); - log_printf ("\n"); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - - result = NULL; - fingerprint_from_pk (pk, fp, NULL); - - if (!rc) + ecc_ct_len = ecc_ecdh_len = 56; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; + } + else if (ecc_algo == GCRY_KEM_RAW_BP256) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 65) { - unsigned int nbits; - byte *p = gcry_mpi_get_opaque (data, &nbits); - rc = pk_ecdh_encrypt_with_shared_point (shared, nshared, fp, p, - (nbits+7)/8, pkey, &result); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - xfree (shared); - if (!rc) + ecc_ct_len = ecc_ecdh_len = 65; + ecc_ss_len = 32; + ecc_hash_algo = GCRY_MD_SHA3_256; + } + else if (ecc_algo == GCRY_KEM_RAW_BP384) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 97) { - resarr[0] = public; - resarr[1] = result; + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } - else + ecc_ct_len = ecc_ecdh_len = 97; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; + } + else if (ecc_algo == GCRY_KEM_RAW_BP512) + { + ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != 129) { - gcry_mpi_release (public); - gcry_mpi_release (result); + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; } + ecc_ct_len = ecc_ecdh_len = 129; + ecc_ss_len = 64; + ecc_hash_algo = GCRY_MD_SHA3_512; } - else /* Elgamal or RSA case. */ - { /* Fixme: Add better error handling or make gnupg use - S-expressions directly. */ - resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); - if (!is_RSA (algo)) - resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); + else + { + if (opt.verbose) + log_info ("%s: ECC curve %s not supported\n", __func__, ecc_oid); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + + + if (DBG_CRYPTO) + { + log_debug ("ECC curve: %s\n", ecc_oid); + log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); } + err = gcry_kem_encap (ecc_algo, + ecc_pubkey, ecc_pubkey_len, + ecc_ct, ecc_ct_len, + ecc_ecdh, ecc_ecdh_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", + __func__, ecc_oid); + goto leave; + } + if (DBG_CRYPTO) + { + log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); + log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); + } + err = gnupg_ecc_kem_kdf (ecc_ss, ecc_ss_len, + ecc_hash_algo, + ecc_ecdh, ecc_ecdh_len, + ecc_ct, ecc_ct_len, + ecc_pubkey, ecc_pubkey_len); + if (err) + { + if (opt.verbose) + log_info ("%s: kdf for ECC failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (ecc_ss, ecc_ss_len, "ECC shared:"); + + kyber_pubkey = gcry_mpi_get_opaque (pk->pkey[2], &nbits); + kyber_pubkey_len = (nbits+7)/8; + if (kyber_pubkey_len == GCRY_KEM_MLKEM768_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM768; + kyber_ct_len = GCRY_KEM_MLKEM768_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM768_SHARED_LEN; + } + else if (kyber_pubkey_len == GCRY_KEM_MLKEM1024_PUBKEY_LEN) + { + kyber_algo = GCRY_KEM_MLKEM1024; + kyber_ct_len = GCRY_KEM_MLKEM1024_ENCAPS_LEN; + kyber_ss_len = GCRY_KEM_MLKEM1024_SHARED_LEN; + } + else + { + if (opt.verbose) + log_info ("%s: Kyber public key length invalid (%zu)\n", + __func__, kyber_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kyber_pubkey, kyber_pubkey_len, "|!trunc|Kyber pubkey:"); + + err = gcry_kem_encap (kyber_algo, + kyber_pubkey, kyber_pubkey_len, + kyber_ct, kyber_ct_len, + kyber_ss, kyber_ss_len, + NULL, 0); + if (err) + { + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC failed\n", __func__); + goto leave; + } + + if (DBG_CRYPTO) + { + log_printhex (kyber_ct, kyber_ct_len, "|!trunc|Kyber ephem:"); + log_printhex (kyber_ss, kyber_ss_len, "Kyber shared:"); + } + + + fixedinfo[0] = seskey_algo; + v5_fingerprint_from_pk (pk, fixedinfo+1, NULL); + fixedlen = 33; + + err = gnupg_kem_combiner (kek, kek_len, + ecc_ss, ecc_ss_len, ecc_ct, ecc_ct_len, + kyber_ss, kyber_ss_len, kyber_ct, kyber_ct_len, + fixedinfo, fixedlen); + if (err) + { + if (opt.verbose) + log_info ("%s: KEM combiner failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (kek, kek_len, "KEK:"); + + err = gcry_cipher_open (&hd, GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_AESWRAP, 0); + if (!err) + err = gcry_cipher_setkey (hd, kek, kek_len); + if (err) + { + if (opt.verbose) + log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, + gpg_strerror (err)); + goto leave; + } + + err = gcry_sexp_build (&s_data, NULL, "%m", data); + if (err) + goto leave; + + n = gcry_cipher_get_algo_keylen (seskey_algo); + seskey = gcry_mpi_get_opaque (data, &nbits); + seskey_len = (nbits+7)/8; + if (seskey_len != n) + { + if (opt.verbose) + log_info ("%s: session key length %zu" + " does not match the length for algo %d\n", + __func__, seskey_len, seskey_algo); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (seskey, seskey_len, "seskey:"); + + enc_seskey_len = 1 + seskey_len + 8; + enc_seskey = xtrymalloc (enc_seskey_len); + if (!enc_seskey || enc_seskey_len > 254) + { + err = gpg_error_from_syserror (); + goto leave; + } + + enc_seskey[0] = enc_seskey_len - 1; + err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1, + seskey, seskey_len); + if (err) + { + log_error ("%s: wrapping session key failed\n", __func__); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:"); + + resarr[0] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len); + if (resarr[0]) + resarr[1] = gcry_mpi_set_opaque_copy (NULL, kyber_ct, 8 * kyber_ct_len); + if (resarr[1]) + resarr[2] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len); + if (!resarr[0] || !resarr[1] || !resarr[2]) + { + err = gpg_error_from_syserror (); + for (i=0; i < 3; i++) + gcry_mpi_release (resarr[i]), resarr[i] = NULL; + } + + leave: + wipememory (ecc_ct, sizeof ecc_ct); + wipememory (ecc_ecdh, sizeof ecc_ecdh); + wipememory (ecc_ss, sizeof ecc_ss); + wipememory (kyber_ct, sizeof kyber_ct); + wipememory (kyber_ss, sizeof kyber_ss); + wipememory (kek, kek_len); + xfree (enc_seskey); + gcry_cipher_close (hd); + xfree (ecc_oid); + return err; +} + + +/* Core of the encryption for the ECDH algorithms. See pk_decrypt for + * a description of the arguments. */ +static gpg_error_t +do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) +{ + gcry_mpi_t *pkey = pk->pkey; + gcry_sexp_t s_ciph = NULL; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_pkey = NULL; + gpg_error_t err; + gcry_mpi_t k = NULL; + char *curve = NULL; + int with_djb_tweak_flag; + gcry_mpi_t public = NULL; + gcry_mpi_t result = NULL; + byte fp[MAX_FINGERPRINT_LEN]; + byte *shared = NULL; + byte *p; + size_t nshared; + unsigned int nbits; + + err = pk_ecdh_generate_ephemeral_key (pkey, &k); + if (err) + goto leave; + + curve = openpgp_oid_to_str (pkey[0]); + if (!curve) + { + err = gpg_error_from_syserror (); + goto leave; + } + + with_djb_tweak_flag = openpgp_oid_is_cv25519 (pkey[0]); + + /* Now use the ephemeral secret to compute the shared point. */ + err = gcry_sexp_build (&s_pkey, NULL, + with_djb_tweak_flag ? + "(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))" + : "(public-key(ecdh(curve%s)(q%m)))", + curve, pkey[1]); + if (err) + goto leave; + + /* Put K into a simplified S-expression. */ + err = gcry_sexp_build (&s_data, NULL, "%m", k); + if (err) + goto leave; + + /* Run encryption. */ + err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + if (err) + goto leave; + + gcry_sexp_release (s_data); s_data = NULL; + gcry_sexp_release (s_pkey); s_pkey = NULL; + + + /* Get the shared point and the ephemeral public key. */ + shared = get_data_from_sexp (s_ciph, "s", &nshared); + if (!shared) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = sexp_extract_param_sos (s_ciph, "e", &public); + gcry_sexp_release (s_ciph); s_ciph = NULL; + if (DBG_CRYPTO) + { + log_debug ("ECDH ephemeral key:"); + gcry_mpi_dump (public); + log_printf ("\n"); + } + + fingerprint_from_pk (pk, fp, NULL); + + p = gcry_mpi_get_opaque (data, &nbits); + result = NULL; + err = pk_ecdh_encrypt_with_shared_point (shared, nshared, fp, p, + (nbits+7)/8, pkey, &result); + if (err) + goto leave; + + resarr[0] = public; public = NULL; + resarr[1] = result; result = NULL; + leave: + gcry_mpi_release (public); + gcry_mpi_release (result); + xfree (shared); gcry_sexp_release (s_ciph); - return rc; + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + xfree (curve); + gcry_mpi_release (k); + return err; +} + + +/* Core of the encryption for RSA and Elgamal algorithms. See + * pk_decrypt for a description of the arguments. */ +static gpg_error_t +do_encrypt_rsa_elg (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr) +{ + pubkey_algo_t algo = pk->pubkey_algo; + gcry_mpi_t *pkey = pk->pkey; + gcry_sexp_t s_ciph = NULL; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_pkey = NULL; + gpg_error_t err; + + if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(elg(p%m)(g%m)(y%m)))", + pkey[0], pkey[1], pkey[2]); + else + err = gcry_sexp_build (&s_pkey, NULL, + "(public-key(rsa(n%m)(e%m)))", + pkey[0], pkey[1]); + if (err) + goto leave; + + err = gcry_sexp_build (&s_data, NULL, "%m", data); + if (err) + goto leave; + + err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + if (err) + goto leave; + + gcry_sexp_release (s_data); s_data = NULL; + gcry_sexp_release (s_pkey); s_pkey = NULL; + + resarr[0] = get_mpi_from_sexp (s_ciph, "a", GCRYMPI_FMT_USG); + if (!is_RSA (algo)) + resarr[1] = get_mpi_from_sexp (s_ciph, "b", GCRYMPI_FMT_USG); + + leave: + gcry_sexp_release (s_data); + gcry_sexp_release (s_pkey); + gcry_sexp_release (s_ciph); + return err; +} + + +/* + * Emulate our old PK interface here - sometime in the future we might + * change the internal design to directly fit to libgcrypt. PK is is + * the OpenPGP public key packet, DATA is an MPI with the to be + * encrypted data, and RESARR receives the encrypted data. RESARRAY + * is expected to be an two item array which will be filled with newly + * allocated MPIs. SESKEY_ALGO is required for public key algorithms + * which do not encode it in DATA. + */ +gpg_error_t +pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr) +{ + pubkey_algo_t algo = pk->pubkey_algo; + + if (algo == PUBKEY_ALGO_KYBER) + return do_encrypt_kem (pk, data, seskey_algo, resarr); + else if (algo == PUBKEY_ALGO_ECDH) + return do_encrypt_ecdh (pk, data, resarr); + else if (algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) + return do_encrypt_rsa_elg (pk, data, resarr); + else if (algo == PUBKEY_ALGO_RSA || algo == PUBKEY_ALGO_RSA_E) + return do_encrypt_rsa_elg (pk, data, resarr); + else + return gpg_error (GPG_ERR_PUBKEY_ALGO); } diff --git a/g10/pkglue.h b/g10/pkglue.h index abeddb811..2b5c8b143 100644 --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -31,8 +31,8 @@ gpg_error_t sexp_extract_param_sos_nlz (gcry_sexp_t sexp, const char *param, int pk_verify (pubkey_algo_t algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey); -int pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data, - PKT_public_key *pk, gcry_mpi_t *pkey); +gpg_error_t pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo, + gcry_mpi_t *resarr); int pk_check_secret_key (pubkey_algo_t algo, gcry_mpi_t *skey); diff --git a/g10/plaintext.c b/g10/plaintext.c index 5c21dd7f6..a96214994 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -111,20 +111,20 @@ get_output_file (const byte *embedded_name, int embedded_namelen, { /* Special file name, no filename, or "-" given; write to the * file descriptor or to stdout. */ - int fd; + gnupg_fd_t fd; char xname[64]; - fd = check_special_filename (fname, 1, 0); - if (fd == -1) + fd = gnupg_check_special_filename (fname); + if (fd == GNUPG_INVALID_FD) { /* Not a special filename, thus we want stdout. */ fp = es_stdout; es_set_binary (fp); } - else if (!(fp = es_fdopen_nc (fd, "wb"))) + else if (!(fp = open_stream_nc (fd, "wb"))) { err = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", fd); + snprintf (xname, sizeof xname, "[fd %d]", FD_DBG (fd)); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err)); goto leave; } @@ -137,8 +137,7 @@ get_output_file (const byte *embedded_name, int embedded_namelen, if (!tmp || !*tmp) { xfree (tmp); - /* FIXME: Below used to be GPG_ERR_CREATE_FILE */ - err = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_EEXIST); goto leave; } xfree (fname); @@ -146,13 +145,7 @@ get_output_file (const byte *embedded_name, int embedded_namelen, } } - if (opt.outfp && is_secured_file (es_fileno (opt.outfp))) - { - err = gpg_error (GPG_ERR_EPERM); - log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err)); - goto leave; - } - else if (fp || nooutput) + if (fp || nooutput) ; else if (is_secured_filename (fname)) { @@ -729,8 +722,8 @@ hash_datafiles (gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, /* Hash the data from file descriptor DATA_FD and append the hash to hash contexts MD and MD2. */ int -hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, - int textmode) +hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, + gnupg_fd_t data_fd, int textmode) { progress_filter_context_t *pfx = new_progress_context (); iobuf_t fp; @@ -747,7 +740,7 @@ hash_datafile_by_fd (gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, { int rc = gpg_error_from_syserror (); log_error (_("can't open signed data fd=%d: %s\n"), - data_fd, strerror (errno)); + FD_DBG (data_fd), strerror (errno)); release_progress_context (pfx); return rc; } diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index 6e1b0898e..563077803 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -117,6 +117,7 @@ get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek) { if (!(k->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E || k->pubkey_algo == PUBKEY_ALGO_ECDH + || k->pubkey_algo == PUBKEY_ALGO_KYBER || k->pubkey_algo == PUBKEY_ALGO_RSA || k->pubkey_algo == PUBKEY_ALGO_RSA_E || k->pubkey_algo == PUBKEY_ALGO_ELGAMAL)) @@ -193,7 +194,7 @@ get_it (ctrl_t ctrl, { gpg_error_t err; byte *frame = NULL; - unsigned int n; + unsigned int frameidx; size_t nframe; u16 csum, csum2; int padding; @@ -237,6 +238,32 @@ get_it (ctrl_t ctrl, err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))", enc->data[1], enc->data[0]); } + else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + char fixedinfo[1+MAX_FINGERPRINT_LEN]; + int fixedlen; + + if ((opt.compat_flags & COMPAT_T7014_OLD)) + { + /* Temporary use for tests with original test vectors. */ + fixedinfo[0] = 0x69; + fixedlen = 1; + } + else + { + fixedinfo[0] = enc->seskey_algo; + v5_fingerprint_from_pk (sk, fixedinfo+1, NULL); + fixedlen = 33; + } + + if (!enc->data[0] || !enc->data[1] || !enc->data[2]) + err = gpg_error (GPG_ERR_BAD_MPI); + else + err = gcry_sexp_build (&s_data, NULL, + "(enc-val(pqc(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))", + enc->data[0], enc->data[1], enc->data[2], + enc->seskey_algo, fixedlen, fixedinfo); + } else err = gpg_error (GPG_ERR_BUG); @@ -248,6 +275,7 @@ get_it (ctrl_t ctrl, /* Decrypt. */ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1); + err = agent_pkdecrypt (NULL, keygrip, desc, sk->keyid, sk->main_keyid, sk->pubkey_algo, s_data, &frame, &nframe, &padding); @@ -275,9 +303,22 @@ get_it (ctrl_t ctrl, */ if (DBG_CRYPTO) log_printhex (frame, nframe, "DEK frame:"); - n = 0; + frameidx = 0; - if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + if (sk->pubkey_algo == PUBKEY_ALGO_KYBER) + { + /* We expect a 32 byte session key. We should not see this + * error here because due to the KEM mode the agent_pkdecrypt + * should have already failed. */ + if (nframe != 32) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + dek->keylen = nframe; + dek->algo = enc->seskey_algo; + } + else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) { gcry_mpi_t decoded; @@ -301,13 +342,21 @@ get_it (ctrl_t ctrl, goto leave; } nframe -= frame[nframe-1]; /* Remove padding. */ - log_assert (!n); /* (used just below) */ + if (4 > nframe) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + + dek->keylen = nframe - 3; + dek->algo = frame[0]; + frameidx = 1; } else { if (padding) { - if (n + 7 > nframe) + if (7 > nframe) { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; @@ -319,34 +368,38 @@ get_it (ctrl_t ctrl, * using a Smartcard we are doing it the right way and * therefore we have to skip the zero. This should be fixed * in gpg-agent of course. */ - if (!frame[n]) - n++; + frameidx = 0; + if (!frame[frameidx]) + frameidx++; - if (frame[n] == 1 && frame[nframe - 1] == 2) + if (frame[frameidx] == 1 && frame[nframe - 1] == 2) { log_info (_("old encoding of the DEK is not supported\n")); err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } - if (frame[n] != 2) /* Something went wrong. */ + if (frame[frameidx] != 2) /* Something went wrong. */ { err = gpg_error (GPG_ERR_WRONG_SECKEY); goto leave; } - for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */ + /* Skip the random bytes. */ + for (frameidx++; frameidx < nframe && frame[frameidx]; frameidx++) ; - n++; /* Skip the zero byte. */ + frameidx++; /* Skip the zero byte. */ } - } - if (n + 4 > nframe) - { - err = gpg_error (GPG_ERR_WRONG_SECKEY); - goto leave; + if (frameidx + 4 > nframe) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } + + dek->keylen = nframe - (frameidx + 1) - 2; + dek->algo = frame[frameidx++]; } - dek->keylen = nframe - (n + 1) - 2; - dek->algo = frame[n++]; + /* Check whether we support the ago. */ err = openpgp_cipher_test_algo (dek->algo); if (err) { @@ -365,16 +418,21 @@ get_it (ctrl_t ctrl, goto leave; } - /* Copy the key to DEK and compare the checksum. */ - csum = buf16_to_u16 (frame+nframe-2); - memcpy (dek->key, frame + n, dek->keylen); - for (csum2 = 0, n = 0; n < dek->keylen; n++) - csum2 += dek->key[n]; - if (csum != csum2) + /* Copy the key to DEK and compare the checksum if needed. */ + /* We use the frameidx as flag for the need of a checksum. */ + memcpy (dek->key, frame + frameidx, dek->keylen); + if (frameidx) { - err = gpg_error (GPG_ERR_WRONG_SECKEY); - goto leave; + csum = buf16_to_u16 (frame+nframe-2); + for (csum2 = 0, frameidx = 0; frameidx < dek->keylen; frameidx++) + csum2 += dek->key[frameidx]; + if (csum != csum2) + { + err = gpg_error (GPG_ERR_WRONG_SECKEY); + goto leave; + } } + if (DBG_CLOCK) log_clock ("decryption ready"); if (DBG_CRYPTO) @@ -399,6 +457,9 @@ get_it (ctrl_t ctrl, log_info (_("WARNING: cipher algorithm %s not found in recipient" " preferences\n"), openpgp_cipher_algo_name (dek->algo)); + /* if (!err && 25519 && openpgp_oidbuf_is_ed25519 (curve, len)) */ + /* log_info ("Note: legacy OID was used for cv25519\n"); */ + if (!err) { kbnode_t k; diff --git a/g10/revoke.c b/g10/revoke.c index d6cbf93cb..ef5bb4d78 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -333,7 +333,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) if( !opt.armor ) tty_printf(_("ASCII armored output forced.\n")); - if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) + if( (rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, 1, &out )) ) goto leave; afx->what = 1; @@ -464,7 +464,7 @@ create_revocation (ctrl_t ctrl, afx = new_armor_context (); - if ((rc = open_outfile (-1, filename, suffix, 1, &out))) + if ((rc = open_outfile (GNUPG_INVALID_FD, filename, suffix, 1, &out))) goto leave; if (leadintext ) diff --git a/g10/server.c b/g10/server.c index 60b447c41..24e525e7f 100644 --- a/g10/server.c +++ b/g10/server.c @@ -265,7 +265,7 @@ cmd_encrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - int inp_fd, out_fd; + gnupg_fd_t inp_fd, out_fd; (void)line; /* LINE is not used. */ @@ -276,14 +276,14 @@ cmd_encrypt (assuan_context_t ctx, char *line) goto leave; } - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) { err = set_error (GPG_ERR_ASS_NO_INPUT, NULL); goto leave; } - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) { err = set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); goto leave; @@ -327,15 +327,15 @@ cmd_decrypt (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - int inp_fd, out_fd; + gnupg_fd_t inp_fd, out_fd; (void)line; /* LINE is not used. */ - inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0); - if (inp_fd == -1) + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_INPUT, NULL); - out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); - if (out_fd == -1) + out_fd = assuan_get_output_fd (ctx); + if (out_fd == GNUPG_INVALID_FD) return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); glo_ctrl.lasterr = 0; @@ -388,16 +388,7 @@ cmd_verify (assuan_context_t ctx, char *line) if (out_fd != GNUPG_INVALID_FD) { - es_syshd_t syshd; - -#ifdef HAVE_W32_SYSTEM - syshd.type = ES_SYSHD_HANDLE; - syshd.u.handle = out_fd; -#else - syshd.type = ES_SYSHD_FD; - syshd.u.fd = out_fd; -#endif - out_fp = es_sysopen_nc (&syshd, "w"); + out_fp = open_stream_nc (fd, "w"); if (!out_fp) return set_error (gpg_err_code_from_syserror (), "fdopen() failed"); } diff --git a/g10/seskey.c b/g10/seskey.c index 15c210b78..2fe8e9de7 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -86,6 +86,22 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) if (DBG_CRYPTO) log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); + if (openpgp_pk_algo == PUBKEY_ALGO_KYBER) + { + /* Straightforward encoding w/o extra checksum as used by ECDH. */ + nframe = dek->keylen; + log_assert (nframe > 4); /*(for the log_debug)*/ + frame = xmalloc_secure (nframe); + memcpy (frame, dek->key, nframe); + if (DBG_CRYPTO) + log_debug ("encode_session_key: " + "[%d] %02x %02x %02x ... %02x %02x %02x\n", + (int) dek->keylen, frame[0], frame[1], frame[2], + frame[nframe-3], frame[nframe-2], frame[nframe-1]); + + return gcry_mpi_set_opaque (NULL, frame, 8*nframe); + } + csum = 0; for (p = dek->key, i=0; i < dek->keylen; i++) csum += *p++; diff --git a/g10/sign.c b/g10/sign.c index b00bdfefd..67ea5a038 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -495,6 +495,7 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, gcry_sexp_t s_sigval; desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1); + /* FIXME: Eventually support dual keys. */ err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc, pksk->keyid, pksk->main_keyid, pksk->pubkey_algo, dp, gcry_md_get_algo_dlen (mdalgo), mdalgo, @@ -580,6 +581,7 @@ openpgp_card_v1_p (PKT_public_key *pk) { char *hexgrip; + /* Note: No need to care about dual keys for non-RSA keys. */ err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { @@ -1022,7 +1024,9 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, const char *fname; armor_filter_context_t *afx; compress_filter_context_t zfx; + gcry_md_hd_t md = NULL; md_filter_context_t mfx; + md_thd_filter_context_t mfx2 = NULL; text_filter_context_t tfx; progress_filter_context_t *pfx; encrypt_filter_context_t efx; @@ -1115,7 +1119,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, else if (opt.verbose) log_info (_("writing to '%s'\n"), outfile); } - else if ((rc = open_outfile (-1, fname, + else if ((rc = open_outfile (GNUPG_INVALID_FD, fname, opt.armor? 1 : detached? 2 : 0, 0, &out))) { goto leave; @@ -1128,10 +1132,10 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, iobuf_push_filter (inp, text_filter, &tfx); } - if (gcry_md_open (&mfx.md, 0, 0)) + if (gcry_md_open (&md, 0, 0)) BUG (); if (DBG_HASHING) - gcry_md_debug (mfx.md, "sign"); + gcry_md_debug (md, "sign"); /* If we're encrypting and signing, it is reasonable to pick the * hash algorithm to use out of the recipient key prefs. This is @@ -1228,10 +1232,21 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, } for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) - gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); + gcry_md_enable (md, hash_for (sk_rover->pk)); if (!multifile) - iobuf_push_filter (inp, md_filter, &mfx); + { + if (encryptflag && (opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } + } if (detached && !encryptflag) afx->what = 2; @@ -1294,7 +1309,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; } - write_status_begin_signing (mfx.md); + write_status_begin_signing (md); /* Setup the inner packet. */ if (detached) @@ -1334,7 +1349,16 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, memset (&tfx, 0, sizeof tfx); iobuf_push_filter (inp, text_filter, &tfx); } - iobuf_push_filter (inp, md_filter, &mfx); + if (encryptflag && (opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } while (iobuf_read (inp, NULL, iobuf_size) != -1) ; iobuf_close (inp); @@ -1363,7 +1387,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; /* Write the signatures. */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, + rc = write_signature_packets (ctrl, sk_list, out, md, extrahash, opt.textmode && !outfile? 0x01 : 0x00, 0, duration, detached ? 'D':'S', NULL); if (rc) @@ -1380,7 +1404,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, write_status (STATUS_END_ENCRYPTION); } iobuf_close (inp); - gcry_md_close (mfx.md); + gcry_md_close (md); release_sk_list (sk_list); release_pk_list (pk_list); recipient_digest_algo = 0; @@ -1461,7 +1485,7 @@ clearsign_file (ctrl_t ctrl, log_info (_("writing to '%s'\n"), outfile); } - else if ((rc = open_outfile (-1, fname, 1, 0, &out))) + else if ((rc = open_outfile (GNUPG_INVALID_FD, fname, 1, 0, &out))) { goto leave; } @@ -1563,6 +1587,8 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) progress_filter_context_t *pfx; compress_filter_context_t zfx; md_filter_context_t mfx; + md_thd_filter_context_t mfx2 = NULL; + gcry_md_hd_t md = NULL; text_filter_context_t tfx; cipher_filter_context_t cfx; iobuf_t inp = NULL; @@ -1639,22 +1665,32 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) /**/ : "CFB"); /* Now create the outfile. */ - rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out); + rc = open_outfile (GNUPG_INVALID_FD, fname, opt.armor? 1:0, 0, &out); if (rc) goto leave; /* Prepare to calculate the MD over the input. */ if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); - if (gcry_md_open (&mfx.md, 0, 0)) + if (gcry_md_open (&md, 0, 0)) BUG (); if (DBG_HASHING) - gcry_md_debug (mfx.md, "symc-sign"); + gcry_md_debug (md, "symc-sign"); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) - gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); + gcry_md_enable (md, hash_for (sk_rover->pk)); + + if ((opt.compat_flags & COMPAT_PARALLELIZED)) + { + iobuf_push_filter (inp, md_thd_filter, &mfx2); + md_thd_filter_set_md (mfx2, md); + } + else + { + iobuf_push_filter (inp, md_filter, &mfx); + mfx.md = md; + } - iobuf_push_filter (inp, md_filter, &mfx); /* Push armor output filter */ if (opt.armor) @@ -1696,7 +1732,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) if (rc) goto leave; - write_status_begin_signing (mfx.md); + write_status_begin_signing (md); /* Pipe data through all filters; i.e. write the signed stuff. */ /* (current filters: zip - encrypt - armor) */ @@ -1708,7 +1744,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) /* Write the signatures. */ /* (current filters: zip - encrypt - armor) */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, + rc = write_signature_packets (ctrl, sk_list, out, md, extrahash, opt.textmode? 0x01 : 0x00, 0, duration, 'S', NULL); if (rc) @@ -1725,7 +1761,7 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) } iobuf_close (inp); release_sk_list (sk_list); - gcry_md_close (mfx.md); + gcry_md_close (md); xfree (cfx.dek); xfree (s2k); release_progress_context (pfx); diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 058ab5cf6..99f135678 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -141,19 +141,16 @@ import_ownertrust (ctrl_t ctrl, const char *fname ) fname = "[stdin]"; is_stdin = 1; } + else if (is_secured_filename (fname)) { + gpg_err_set_errno (EPERM); + log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); + return; + } else if( !(fp = es_fopen( fname, "r" )) ) { log_error ( _("can't open '%s': %s\n"), fname, strerror(errno) ); return; } - if (is_secured_file (es_fileno (fp))) - { - es_fclose (fp); - gpg_err_set_errno (EPERM); - log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); - return; - } - while (es_fgets (line, DIM(line)-1, fp)) { TRUSTREC rec; diff --git a/g10/verify.c b/g10/verify.c index 1c3de767c..c2c63255c 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -240,7 +240,8 @@ verify_files (ctrl_t ctrl, int nfiles, char **files ) FIXME: OUTFP is not yet implemented. */ int -gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp) +gpg_verify (ctrl_t ctrl, gnupg_fd_t sig_fd, gnupg_fd_t data_fd, + estream_t out_fp) { int rc; iobuf_t fp; @@ -260,7 +261,8 @@ gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp) if (!fp) { rc = gpg_error_from_syserror (); - log_error (_("can't open fd %d: %s\n"), sig_fd, strerror (errno)); + log_error (_("can't open fd %d: %s\n"), FD_DBG (sig_fd), + strerror (errno)); goto leave; } |