diff options
Diffstat (limited to 'g10')
-rw-r--r-- | g10/Makefile.am | 3 | ||||
-rw-r--r-- | g10/build-packet.c | 67 | ||||
-rw-r--r-- | g10/call-agent.c | 10 | ||||
-rw-r--r-- | g10/card-util.c | 40 | ||||
-rw-r--r-- | g10/cipher-aead.c | 448 | ||||
-rw-r--r-- | g10/cipher-cfb.c (renamed from g10/cipher.c) | 7 | ||||
-rw-r--r-- | g10/decrypt-data.c | 777 | ||||
-rw-r--r-- | g10/dek.h | 25 | ||||
-rw-r--r-- | g10/ecdh.c | 18 | ||||
-rw-r--r-- | g10/encrypt.c | 342 | ||||
-rw-r--r-- | g10/filter.h | 63 | ||||
-rw-r--r-- | g10/getkey.c | 25 | ||||
-rw-r--r-- | g10/gpg.c | 114 | ||||
-rw-r--r-- | g10/gpg.h | 13 | ||||
-rw-r--r-- | g10/gpgcompose.c | 12 | ||||
-rw-r--r-- | g10/import.c | 19 | ||||
-rw-r--r-- | g10/kbnode.c | 5 | ||||
-rw-r--r-- | g10/keydb.h | 1 | ||||
-rw-r--r-- | g10/keyedit.c | 32 | ||||
-rw-r--r-- | g10/keygen.c | 131 | ||||
-rw-r--r-- | g10/keyid.c | 50 | ||||
-rw-r--r-- | g10/keylist.c | 43 | ||||
-rw-r--r-- | g10/main.h | 24 | ||||
-rw-r--r-- | g10/mainproc.c | 162 | ||||
-rw-r--r-- | g10/misc.c | 126 | ||||
-rw-r--r-- | g10/openfile.c | 22 | ||||
-rw-r--r-- | g10/options.h | 8 | ||||
-rw-r--r-- | g10/packet.h | 42 | ||||
-rw-r--r-- | g10/parse-packet.c | 147 | ||||
-rw-r--r-- | g10/pkclist.c | 33 | ||||
-rw-r--r-- | g10/pubkey-enc.c | 4 | ||||
-rw-r--r-- | g10/sig-check.c | 6 | ||||
-rw-r--r-- | g10/sign.c | 20 | ||||
-rw-r--r-- | g10/tdbdump.c | 14 | ||||
-rw-r--r-- | g10/tofu.c | 20 |
35 files changed, 2473 insertions, 400 deletions
diff --git a/g10/Makefile.am b/g10/Makefile.am index cc4ef5cb6..b8b92d702 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -131,7 +131,8 @@ gpg_sources = server.c \ passphrase.c \ decrypt.c \ decrypt-data.c \ - cipher.c \ + cipher-cfb.c \ + cipher-aead.c \ encrypt.c \ sign.c \ verify.c \ diff --git a/g10/build-packet.c b/g10/build-packet.c index d4a1d6a53..b4e03d007 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -42,6 +42,7 @@ static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); @@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt) break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED: @@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt) case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); break; + case PKT_ENCRYPTED_AEAD: + rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted); + break; case PKT_COMPRESSED: rc = do_compressed (out, ctb, pkt->pkt.compressed); break; @@ -612,11 +617,8 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) IOBUF a = iobuf_temp(); log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC); + log_assert (enc->version == 4 || enc->version == 5); - /* The only acceptable version. */ - log_assert( enc->version == 4 ); - - /* RFC 4880, Section 3.7. */ switch (enc->s2k.mode) { case 0: /* Simple S2K. */ @@ -627,23 +629,26 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) default: log_bug ("do_symkey_enc: s2k=%d\n", enc->s2k.mode); } - iobuf_put( a, enc->version ); - iobuf_put( a, enc->cipher_algo ); - iobuf_put( a, enc->s2k.mode ); - iobuf_put( a, enc->s2k.hash_algo ); - if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { - iobuf_write(a, enc->s2k.salt, 8 ); - if( enc->s2k.mode == 3 ) - iobuf_put(a, enc->s2k.count); + iobuf_put (a, enc->version); + iobuf_put (a, enc->cipher_algo); + if (enc->version == 5) + iobuf_put (a, enc->aead_algo); + iobuf_put (a, enc->s2k.mode); + iobuf_put (a, enc->s2k.hash_algo); + if (enc->s2k.mode == 1 || enc->s2k.mode == 3) + { + iobuf_write (a, enc->s2k.salt, 8); + if (enc->s2k.mode == 3) + iobuf_put (a, enc->s2k.count); } - if( enc->seskeylen ) - iobuf_write(a, enc->seskey, enc->seskeylen ); + if (enc->seskeylen) + iobuf_write (a, enc->seskey, enc->seskeylen); - write_header(out, ctb, iobuf_get_temp_length(a) ); - rc = iobuf_write_temp( out, a ); + write_header (out, ctb, iobuf_get_temp_length(a)); + rc = iobuf_write_temp (out, a); - iobuf_close(a); - return rc; + iobuf_close (a); + return rc; } @@ -812,6 +817,32 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) } +/* Serialize the symmetrically AEAD encrypted data packet + * (rfc4880bis-03, Section 5.16) described by ED and write it to OUT. + * + * Note: this only writes only packet's header. The caller must then + * follow up and write the actual encrypted data. This should be done + * by pushing the the cipher_filter_aead. */ +static int +do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed) +{ + u32 n; + + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD); + + n = ed->len ? (ed->len + ed->extralen + 4) : 0; + write_header (out, ctb, n ); + iobuf_writebyte (out, 1); /* Version. */ + iobuf_writebyte (out, ed->cipher_algo); + iobuf_writebyte (out, ed->aead_algo); + iobuf_writebyte (out, ed->chunkbyte); + + /* This is all. The caller has to write the encrypted data */ + + return 0; +} + + /* Serialize the compressed packet (RFC 4880, Section 5.6) described by CD and write it to OUT. diff --git a/g10/call-agent.c b/g10/call-agent.c index ea530e7ac..b1f589bbc 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -195,7 +195,7 @@ warn_version_mismatch (assuan_context_t ctx, const char *servername, int mode) err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_log (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED? - GPGRT_LOG_INFO : GPGRT_LOG_ERROR, + GPGRT_LOGLVL_INFO : GPGRT_LOGLVL_ERROR, _("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) @@ -1493,7 +1493,7 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) char *p; kbnode_t kbctx, node; int nkeys; - unsigned char grip[20]; + unsigned char grip[KEYGRIP_LEN]; err = start_agent (ctrl, 0); if (err) @@ -1873,10 +1873,16 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce, snprintf (line, sizeof line, "PKSIGN%s%s", cache_nonce? " -- ":"", cache_nonce? cache_nonce:""); + + if (DBG_CLOCK) + log_clock ("enter signing"); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); + if (DBG_CLOCK) + log_clock ("leave signing"); + if (err) xfree (get_membuf (&data, NULL)); else diff --git a/g10/card-util.c b/g10/card-util.c index bda4e83b9..f8a1bb831 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -552,9 +552,9 @@ current_card_status (ctrl_t ctrl, estream_t fp, print_isoname (fp, "Name of cardholder: ", "name", info.disp_name); print_name (fp, "Language prefs ...: ", info.disp_lang); - tty_fprintf (fp, "Sex ..............: %s\n", - info.disp_sex == 1? _("male"): - info.disp_sex == 2? _("female") : _("unspecified")); + tty_fprintf (fp, "Salutation .......: %s\n", + info.disp_sex == 1? _("Mr."): + info.disp_sex == 2? _("Mrs.") : ""); print_name (fp, "URL of public key : ", info.pubkey_url); print_name (fp, "Login data .......: ", info.login_data); if (info.private_do[0]) @@ -1118,7 +1118,7 @@ change_sex (void) int rc; data = cpr_get ("cardedit.change_sex", - _("Sex ((M)ale, (F)emale or space): ")); + _("Salutation (M = Mr., F = Mrs., or space): ")); if (!data) return -1; trim_spaces (data); @@ -1139,7 +1139,7 @@ change_sex (void) rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL ); if (rc) - log_error ("error setting sex: %s\n", gpg_strerror (rc)); + log_error ("error setting salutation: %s\n", gpg_strerror (rc)); xfree (data); write_sc_op_status (rc); return rc; @@ -1152,7 +1152,8 @@ change_cafpr (int fprno) char *data; const char *s; int i, c, rc; - unsigned char fpr[20]; + unsigned char fpr[MAX_FINGERPRINT_LEN]; + int fprlen; data = cpr_get ("cardedit.change_cafpr", _("CA fingerprint: ")); if (!data) @@ -1160,7 +1161,7 @@ change_cafpr (int fprno) trim_spaces (data); cpr_kill_prompt (); - for (i=0, s=data; i < 20 && *s; ) + for (i=0, s=data; i < MAX_FINGERPRINT_LEN && *s; ) { while (spacep(s)) s++; @@ -1174,8 +1175,9 @@ change_cafpr (int fprno) fpr[i++] = c; s += 2; } + fprlen = i; xfree (data); - if (i != 20 || *s) + if ((fprlen != 20 && fprlen != 32) || *s) { tty_printf (_("Error: invalid formatted fingerprint.\n")); return -1; @@ -1183,7 +1185,7 @@ change_cafpr (int fprno) rc = agent_scd_setattr (fprno==1?"CA-FPR-1": fprno==2?"CA-FPR-2": - fprno==3?"CA-FPR-3":"x", fpr, 20, NULL ); + fprno==3?"CA-FPR-3":"x", fpr, fprlen, NULL ); if (rc) log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); write_sc_op_status (rc); @@ -1345,12 +1347,11 @@ show_keysize_warning (void) return; shown = 1; tty_printf - (_("Note: There is no guarantee that the card " - "supports the requested size.\n" - " If the key generation does not succeed, " - "please check the\n" - " documentation of your card to see what " - "sizes are allowed.\n")); + (_("Note: There is no guarantee that the card supports the requested\n" + " key type or size. If the key generation does not succeed,\n" + " please check the documentation of your card to see which\n" + " key types and sizes are supported.\n") + ); } @@ -1410,8 +1411,12 @@ ask_card_keyattr (int keyno, unsigned int nbits) } else { + char name[30]; + + snprintf (name, sizeof name, "rsa%u", req_nbits); tty_printf (_("The card will now be re-configured" - " to generate a key of %u bits\n"), req_nbits); + " to generate a key of type: %s\n"), + name); show_keysize_warning (); return req_nbits; } @@ -1916,7 +1921,8 @@ static struct { "fetch" , cmdFETCH , 0, N_("fetch the key specified in the card URL")}, { "login" , cmdLOGIN , 1, N_("change the login name")}, { "lang" , cmdLANG , 1, N_("change the language preferences")}, - { "sex" , cmdSEX , 1, N_("change card holder's sex")}, + { "salutation",cmdSEX , 1, N_("change card holder's salutation")}, + { "sex" ,cmdSEX , 1, NULL }, /* Backward compatibility. */ { "cafpr" , cmdCAFPR , 1, N_("change a CA fingerprint")}, { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")}, { "generate", cmdGENERATE, 1, N_("generate new keys")}, diff --git a/g10/cipher-aead.c b/g10/cipher-aead.c new file mode 100644 index 000000000..f9a996c80 --- /dev/null +++ b/g10/cipher-aead.c @@ -0,0 +1,448 @@ +/* cipher-aead.c - Enciphering filter for AEAD modes + * Copyright (C) 2018 Werner koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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+ + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "gpg.h" +#include "../common/status.h" +#include "../common/iobuf.h" +#include "../common/util.h" +#include "filter.h" +#include "packet.h" +#include "options.h" +#include "main.h" + + +/* The size of the buffer we allocate to encrypt the data. This must + * be a multiple of the OCB blocksize (16 byte). */ +#define AEAD_ENC_BUFFER_SIZE (64*1024) + + +/* Wrapper around iobuf_write to make sure that a proper error code is + * always returned. */ +static gpg_error_t +my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen) +{ + if (iobuf_write (a, buffer, buflen)) + { + gpg_error_t err = iobuf_error (a); + if (!err || !gpg_err_code (err)) /* (The latter should never happen) */ + err = gpg_error (GPG_ERR_EIO); + return err; + } + return 0; +} + + +/* Set the nonce and the additional data for the current chunk. If + * FINAL is set the final AEAD chunk is processed. This also reset + * the encryption machinery so that the handle can be used for a new + * chunk. */ +static gpg_error_t +set_nonce_and_ad (cipher_filter_context_t *cfx, int final) +{ + gpg_error_t err; + unsigned char nonce[16]; + unsigned char ad[21]; + int i; + + switch (cfx->dek->use_aead) + { + case AEAD_ALGO_OCB: + memcpy (nonce, cfx->startiv, 15); + i = 7; + break; + + case AEAD_ALGO_EAX: + memcpy (nonce, cfx->startiv, 16); + i = 8; + break; + + default: + BUG (); + } + + nonce[i++] ^= cfx->chunkindex >> 56; + nonce[i++] ^= cfx->chunkindex >> 48; + nonce[i++] ^= cfx->chunkindex >> 40; + nonce[i++] ^= cfx->chunkindex >> 32; + nonce[i++] ^= cfx->chunkindex >> 24; + nonce[i++] ^= cfx->chunkindex >> 16; + nonce[i++] ^= cfx->chunkindex >> 8; + nonce[i++] ^= cfx->chunkindex; + + if (DBG_CRYPTO) + log_printhex (nonce, 15, "nonce:"); + err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i); + if (err) + return err; + + ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); + ad[1] = 1; + ad[2] = cfx->dek->algo; + ad[3] = cfx->dek->use_aead; + ad[4] = cfx->chunkbyte; + ad[5] = cfx->chunkindex >> 56; + ad[6] = cfx->chunkindex >> 48; + ad[7] = cfx->chunkindex >> 40; + ad[8] = cfx->chunkindex >> 32; + ad[9] = cfx->chunkindex >> 24; + ad[10]= cfx->chunkindex >> 16; + ad[11]= cfx->chunkindex >> 8; + ad[12]= cfx->chunkindex; + if (final) + { + ad[13] = cfx->total >> 56; + ad[14] = cfx->total >> 48; + ad[15] = cfx->total >> 40; + ad[16] = cfx->total >> 32; + ad[17] = cfx->total >> 24; + ad[18] = cfx->total >> 16; + ad[19] = cfx->total >> 8; + ad[20] = cfx->total; + } + if (DBG_CRYPTO) + log_printhex (ad, final? 21 : 13, "authdata:"); + return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13); +} + + +static gpg_error_t +write_header (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + PACKET pkt; + PKT_encrypted ed; + unsigned int blocksize; + unsigned int startivlen; + enum gcry_cipher_modes ciphermode; + + log_assert (cfx->dek->use_aead); + + blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo); + if (blocksize != 16 ) + log_fatal ("unsupported blocksize %u for AEAD\n", blocksize); + + err = openpgp_aead_algo_info (cfx->dek->use_aead, &ciphermode, &startivlen); + if (err) + goto leave; + + log_assert (opt.chunk_size >= 6 && opt.chunk_size <= 62); + cfx->chunkbyte = opt.chunk_size - 6; + cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6); + cfx->chunklen = 0; + cfx->bufsize = AEAD_ENC_BUFFER_SIZE; + cfx->buflen = 0; + cfx->buffer = xtrymalloc (cfx->bufsize); + if (!cfx->buffer) + return gpg_error_from_syserror (); + + memset (&ed, 0, sizeof ed); + ed.new_ctb = 1; /* (Is anyway required for the packet type). */ + ed.len = 0; /* fixme: cfx->datalen */ + ed.extralen = startivlen + 16; /* (16 is the taglen) */ + ed.cipher_algo = cfx->dek->algo; + ed.aead_algo = cfx->dek->use_aead; + ed.chunkbyte = cfx->chunkbyte; + + init_packet (&pkt); + pkt.pkttype = PKT_ENCRYPTED_AEAD; + pkt.pkt.encrypted = &ed; + + if (DBG_FILTER) + log_debug ("aead packet: len=%lu extralen=%d\n", + (unsigned long)ed.len, ed.extralen); + + write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d", + cfx->dek->algo, ed.aead_algo); + print_cipher_algo_note (cfx->dek->algo); + + if (build_packet( a, &pkt)) + log_bug ("build_packet(ENCRYPTED_AEAD) failed\n"); + + log_assert (sizeof cfx->startiv >= startivlen); + gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM); + err = my_iobuf_write (a, cfx->startiv, startivlen); + if (err) + goto leave; + + err = openpgp_cipher_open (&cfx->cipher_hd, + cfx->dek->algo, + ciphermode, + GCRY_CIPHER_SECURE); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:"); + err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen); + if (err) + return err; + + cfx->wrote_header = 1; + + leave: + return err; +} + + +/* Get and write the auth tag to stream A. */ +static gpg_error_t +write_auth_tag (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char tag[16]; + + err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16); + if (err) + goto leave; + err = my_iobuf_write (a, tag, 16); + if (err) + goto leave; + + leave: + if (err) + log_error ("write_auth_tag failed: %s\n", gpg_strerror (err)); + return err; +} + + +/* Write the final chunk to stream A. */ +static gpg_error_t +write_final_chunk (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char dummy[1]; + + err = set_nonce_and_ad (cfx, 1); + if (err) + goto leave; + + gcry_cipher_final (cfx->cipher_hd); + + /* Encrypt an empty string. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0); + if (err) + goto leave; + + err = write_auth_tag (cfx, a); + + leave: + return err; +} + + +/* The core of the flush sub-function of cipher_filter_aead. */ +static gpg_error_t +do_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size) +{ + gpg_error_t err = 0; + int finalize = 0; + size_t n; + + /* Put the data into a buffer, flush and encrypt as needed. */ + if (DBG_FILTER) + log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen); + do + { + if (cfx->buflen + size < cfx->bufsize) + n = size; + else + n = cfx->bufsize - cfx->buflen; + + if (cfx->chunklen + cfx->buflen + n >= cfx->chunksize) + { + size_t n1 = cfx->chunksize - (cfx->chunklen + cfx->buflen); + finalize = 1; + if (DBG_FILTER) + log_debug ("chunksize %ju reached;" + " cur buflen=%zu using %zu of %zu\n", + (uintmax_t)cfx->chunksize, (uintmax_t)cfx->buflen, + n1, n); + n = n1; + } + + memcpy (cfx->buffer + cfx->buflen, buf, n); + cfx->buflen += n; + buf += n; + size -= n; + + if (cfx->buflen == cfx->bufsize || finalize) + { + if (DBG_FILTER) + log_debug ("encrypting: size=%zu buflen=%zu %s n=%zu\n", + size, cfx->buflen, finalize?"(finalize)":"", n); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + if (finalize) + gcry_cipher_final (cfx->cipher_hd); + if (DBG_FILTER) + { + if (finalize) + log_printhex (cfx->buffer, cfx->buflen, "plain(1):"); + else if (cfx->buflen > 32) + log_printhex (cfx->buffer + cfx->buflen - 32, 32, + "plain(last32):"); + } + + /* Take care: even with a buflen of zero an encrypt needs to + * be called after gcry_cipher_final and before + * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB + * mode. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, + NULL, 0); + if (err) + goto leave; + if (finalize && DBG_FILTER) + log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):"); + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + cfx->buflen = 0; + + if (finalize) + { + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_auth_tag (cfx, a); + if (err) + goto leave; + + cfx->chunkindex++; + cfx->chunklen = 0; + finalize = 0; + } + } + } + while (size); + + leave: + return err; +} + + +/* The core of the free sub-function of cipher_filter_aead. */ +static gpg_error_t +do_free (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err = 0; + + if (DBG_FILTER) + log_debug ("do_free: buflen=%zu\n", cfx->buflen); + + if (cfx->buflen) + { + if (DBG_FILTER) + log_debug ("encrypting last %zu bytes of the last chunk\n",cfx->buflen); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + gcry_cipher_final (cfx->cipher_hd); + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, + NULL, 0); + if (err) + goto leave; + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */ + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + + /* Get and write the authentication tag. */ + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_auth_tag (cfx, a); + if (err) + goto leave; + cfx->chunkindex++; + cfx->chunklen = 0; + } + + /* Write the final chunk. */ + if (DBG_FILTER) + log_debug ("creating final chunk\n"); + err = write_final_chunk (cfx, a); + + leave: + xfree (cfx->buffer); + cfx->buffer = NULL; + gcry_cipher_close (cfx->cipher_hd); + cfx->cipher_hd = NULL; + return err; +} + + +/* + * This filter is used to encrypt data with an AEAD algorithm + */ +int +cipher_filter_aead (void *opaque, int control, + iobuf_t a, byte *buf, size_t *ret_len) +{ + cipher_filter_context_t *cfx = opaque; + size_t size = *ret_len; + int rc = 0; + + if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ + { + rc = -1; /* not used */ + } + else if (control == IOBUFCTRL_FLUSH) /* encrypt */ + { + if (!cfx->wrote_header && (rc=write_header (cfx, a))) + ; + else + rc = do_flush (cfx, a, buf, size); + } + else if (control == IOBUFCTRL_FREE) + { + rc = do_free (cfx, a); + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "cipher_filter_aead", *ret_len); + } + + return rc; +} diff --git a/g10/cipher.c b/g10/cipher-cfb.c index b950d0c3f..79b21bd8a 100644 --- a/g10/cipher.c +++ b/g10/cipher-cfb.c @@ -1,4 +1,4 @@ -/* cipher.c - En-/De-ciphering filter +/* cipher-cfb.c - Enciphering filter for the old CFB mode. * Copyright (C) 1998-2003, 2006, 2009 Free Software Foundation, Inc. * Copyright (C) 1998-2003, 2006, 2009, 2017 Werner koch * @@ -117,7 +117,8 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) * This filter is used to en/de-cipher data with a symmetric algorithm */ int -cipher_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) +cipher_filter_cfb (void *opaque, int control, + iobuf_t a, byte *buf, size_t *ret_len) { cipher_filter_context_t *cfx = opaque; size_t size = *ret_len; @@ -177,7 +178,7 @@ cipher_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) } else if (control == IOBUFCTRL_DESC) { - mem2str (buf, "cipher_filter", *ret_len); + mem2str (buf, "cipher_filter_cfb", *ret_len); } return rc; diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 736534d75..a3151b5ed 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -1,6 +1,6 @@ /* decrypt-data.c - Decrypt an encrypted data packet - * Copyright (C) 1998, 1999, 2000, 2001, 2005, - * 2006, 2009 Free Software Foundation, Inc. + * Copyright (C) 1998-2001, 2005-2006, 2009 Free Software Foundation, Inc. + * Copyright (C) 1998-2001, 2005-2006, 2009, 2018 Werner Koch * * This file is part of GnuPG. * @@ -32,22 +32,70 @@ #include "../common/compliance.h" +static int aead_decode_filter (void *opaque, int control, iobuf_t a, + byte *buf, size_t *ret_len); static int mdc_decode_filter ( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); static int decode_filter ( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len); -typedef struct decode_filter_context_s +/* Our context object. */ +struct decode_filter_context_s { + /* Recounter (max value is 2). We need it becuase we do not know + * whether the iobuf or the outer control code frees this object + * first. */ + int refcount; + + /* The cipher handle. */ gcry_cipher_hd_t cipher_hd; + + /* The hash handle for use in MDC mode. */ gcry_md_hd_t mdc_hash; - char defer[22]; - int defer_filled; - int eof_seen; - int refcount; - int partial; /* Working on a partial length packet. */ - size_t length; /* If !partial: Remaining bytes in the packet. */ -} *decode_filter_ctx_t; + + /* The start IV for AEAD encryption. */ + byte startiv[16]; + + /* The holdback buffer and its used length. For AEAD we need 32+1 + * bytes but we use 48 byte. For MDC we need 22 bytes. */ + char holdback[48]; + unsigned int holdbacklen; + + /* Working on a partial length packet. */ + unsigned int partial : 1; + + /* EOF indicator with these true values: + * 1 = normal EOF + * 2 = premature EOF (tag incomplete) + * 3 = premature EOF (general) */ + unsigned int eof_seen : 2; + + /* The actually used cipher algo for AEAD. */ + byte cipher_algo; + + /* The AEAD algo. */ + byte aead_algo; + + /* The encoded chunk byte for AEAD. */ + byte chunkbyte; + + /* The decoded CHUNKBYTE. */ + uint64_t chunksize; + + /* The chunk index for AEAD. */ + uint64_t chunkindex; + + /* The number of bytes in the current chunk. */ + uint64_t chunklen; + + /* The total count of decrypted plaintext octets. */ + uint64_t total; + + /* Remaining bytes in the packet according to the packet header. + * Not used if PARTIAL is true. */ + size_t length; +}; +typedef struct decode_filter_context_s *decode_filter_ctx_t; /* Helper to release the decode context. */ @@ -69,6 +117,99 @@ release_dfx_context (decode_filter_ctx_t dfx) } +/* Set the nonce and the additional data for the current chunk. This + * also reset the decryption machinery * so that the handle can be + * used for a new chunk. If FINAL is set the final AEAD chunk is + * processed. */ +static gpg_error_t +aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) +{ + gpg_error_t err; + unsigned char ad[21]; + unsigned char nonce[16]; + int i; + + switch (dfx->aead_algo) + { + case AEAD_ALGO_OCB: + memcpy (nonce, dfx->startiv, 15); + i = 7; + break; + + case AEAD_ALGO_EAX: + memcpy (nonce, dfx->startiv, 16); + i = 8; + break; + + default: + BUG (); + } + nonce[i++] ^= dfx->chunkindex >> 56; + nonce[i++] ^= dfx->chunkindex >> 48; + nonce[i++] ^= dfx->chunkindex >> 40; + nonce[i++] ^= dfx->chunkindex >> 32; + nonce[i++] ^= dfx->chunkindex >> 24; + nonce[i++] ^= dfx->chunkindex >> 16; + nonce[i++] ^= dfx->chunkindex >> 8; + nonce[i++] ^= dfx->chunkindex; + + if (DBG_CRYPTO) + log_printhex (nonce, i, "nonce:"); + err = gcry_cipher_setiv (dfx->cipher_hd, nonce, i); + if (err) + return err; + + ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); + ad[1] = 1; + ad[2] = dfx->cipher_algo; + ad[3] = dfx->aead_algo; + ad[4] = dfx->chunkbyte; + ad[5] = dfx->chunkindex >> 56; + ad[6] = dfx->chunkindex >> 48; + ad[7] = dfx->chunkindex >> 40; + ad[8] = dfx->chunkindex >> 32; + ad[9] = dfx->chunkindex >> 24; + ad[10]= dfx->chunkindex >> 16; + ad[11]= dfx->chunkindex >> 8; + ad[12]= dfx->chunkindex; + if (final) + { + ad[13] = dfx->total >> 56; + ad[14] = dfx->total >> 48; + ad[15] = dfx->total >> 40; + ad[16] = dfx->total >> 32; + ad[17] = dfx->total >> 24; + ad[18] = dfx->total >> 16; + ad[19] = dfx->total >> 8; + ad[20] = dfx->total; + } + if (DBG_CRYPTO) + log_printhex (ad, final? 21 : 13, "authdata:"); + return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13); +} + + +/* Helper to check the 16 byte tag in TAGBUF. The FINAL flag is only + * for debug messages. */ +static gpg_error_t +aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf) +{ + gpg_error_t err; + + if (DBG_FILTER) + log_printhex (tagbuf, 16, "tag:"); + err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16); + if (err) + { + log_error ("gcry_cipher_checktag%s failed: %s\n", + final? " (final)":"", gpg_strerror (err)); + return err; + } + if (DBG_FILTER) + log_debug ("%stag is valid\n", final?"final ":""); + return 0; +} + /**************** * Decrypt the data, specified by ED with the key DEK. @@ -80,8 +221,8 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) byte *p; int rc=0, c, i; byte temp[32]; - unsigned blocksize; - unsigned nprefix; + unsigned int blocksize; + unsigned int nprefix; dfx = xtrycalloc (1, sizeof *dfx); if (!dfx) @@ -91,8 +232,10 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) if ( opt.verbose && !dek->algo_info_printed ) { if (!openpgp_cipher_test_algo (dek->algo)) - log_info (_("%s encrypted data\n"), - openpgp_cipher_algo_name (dek->algo)); + log_info (_("%s.%s encrypted data\n"), + openpgp_cipher_algo_name (dek->algo), + ed->aead_algo? openpgp_aead_algo_name (ed->aead_algo) + /**/ : "CFB"); else log_info (_("encrypted with unknown algorithm %d\n"), dek->algo ); dek->algo_info_printed = 1; @@ -109,19 +252,18 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) goto leave; } - { - char buf[20]; - - snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo); - write_status_text (STATUS_DECRYPTION_INFO, buf); - } + write_status_printf (STATUS_DECRYPTION_INFO, "%d %d %d", + ed->mdc_method, dek->algo, ed->aead_algo); if (opt.show_session_key) { - char numbuf[25]; + char numbuf[30]; char *hexbuf; - snprintf (numbuf, sizeof numbuf, "%d:", dek->algo); + if (ed->aead_algo) + snprintf (numbuf, sizeof numbuf, "%d.%u:", dek->algo, ed->aead_algo); + else + snprintf (numbuf, sizeof numbuf, "%d:", dek->algo); hexbuf = bin2hex (dek->key, dek->keylen, NULL); if (!hexbuf) { @@ -139,95 +281,188 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) blocksize = openpgp_cipher_get_algo_blklen (dek->algo); if ( !blocksize || blocksize > 16 ) log_fatal ("unsupported blocksize %u\n", blocksize ); - nprefix = blocksize; - if ( ed->len && ed->len < (nprefix+2) ) + if (ed->aead_algo) { - /* An invalid message. We can't check that during parsing - because we may not know the used cipher then. */ - rc = gpg_error (GPG_ERR_INV_PACKET); - goto leave; - } + enum gcry_cipher_modes ciphermode; + unsigned int startivlen; - if ( ed->mdc_method ) - { - if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 )) - BUG (); - if ( DBG_HASHING ) - gcry_md_debug (dfx->mdc_hash, "checkmdc"); - } + if (blocksize != 16) + { + rc = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } - rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo, - GCRY_CIPHER_MODE_CFB, - (GCRY_CIPHER_SECURE - | ((ed->mdc_method || dek->algo >= 100)? - 0 : GCRY_CIPHER_ENABLE_SYNC))); - if (rc) - { - /* We should never get an error here cause we already checked - * that the algorithm is available. */ - BUG(); - } + rc = openpgp_aead_algo_info (ed->aead_algo, &ciphermode, &startivlen); + if (rc) + goto leave; + log_assert (startivlen <= sizeof dfx->startiv); + if (ed->chunkbyte > 56) + { + log_error ("invalid AEAD chunkbyte %u\n", ed->chunkbyte); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } - /* log_hexdump( "thekey", dek->key, dek->keylen );*/ - rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); - if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) - { - log_info(_("WARNING: message was encrypted with" - " a weak key in the symmetric cipher.\n")); - rc=0; - } - else if( rc ) - { - log_error("key setup failed: %s\n", gpg_strerror (rc) ); - goto leave; - } + /* Read the Start-IV. */ + if (ed->len) + { + for (i=0; i < startivlen && ed->len; i++, ed->len--) + { + if ((c=iobuf_get (ed->buf)) == -1) + break; + dfx->startiv[i] = c; + } + } + else + { + for (i=0; i < startivlen; i++ ) + if ( (c=iobuf_get (ed->buf)) == -1 ) + break; + else + dfx->startiv[i] = c; + } + if (i != startivlen) + { + log_error ("Start-IV in AEAD packet too short (%d/%u)\n", + i, startivlen); + rc = gpg_error (GPG_ERR_TOO_SHORT); + goto leave; + } - if (!ed->buf) - { - log_error(_("problem handling encrypted packet\n")); - goto leave; - } + dfx->cipher_algo = ed->cipher_algo; + dfx->aead_algo = ed->aead_algo; + dfx->chunkbyte = ed->chunkbyte; + dfx->chunksize = (uint64_t)1 << (dfx->chunkbyte + 6); - gcry_cipher_setiv (dfx->cipher_hd, NULL, 0); + if (dek->algo != dfx->cipher_algo) + log_info ("Note: different cipher algorithms used (%s/%s)\n", + openpgp_cipher_algo_name (dek->algo), + openpgp_cipher_algo_name (dfx->cipher_algo)); - if ( ed->len ) - { - for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) + rc = openpgp_cipher_open (&dfx->cipher_hd, + dfx->cipher_algo, + ciphermode, + GCRY_CIPHER_SECURE); + if (rc) + goto leave; /* Should never happen. */ + + if (DBG_CRYPTO) + log_printhex (dek->key, dek->keylen, "thekey:"); + rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); + if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { - if ( (c=iobuf_get(ed->buf)) == -1 ) - break; - else - temp[i] = c; + log_info (_("WARNING: message was encrypted with" + " a weak key in the symmetric cipher.\n")); + rc = 0; } + else if (rc) + { + log_error("key setup failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (!ed->buf) + { + log_error(_("problem handling encrypted packet\n")); + goto leave; + } + } - else + else /* CFB encryption. */ { - for (i=0; i < (nprefix+2); i++ ) - if ( (c=iobuf_get(ed->buf)) == -1 ) - break; - else - temp[i] = c; - } + nprefix = blocksize; + if ( ed->len && ed->len < (nprefix+2) ) + { + /* An invalid message. We can't check that during parsing + because we may not know the used cipher then. */ + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } - gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0); - gcry_cipher_sync (dfx->cipher_hd); - p = temp; - /* log_hexdump( "prefix", temp, nprefix+2 ); */ - if (dek->symmetric - && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) - { - rc = gpg_error (GPG_ERR_BAD_KEY); - goto leave; - } + if ( ed->mdc_method ) + { + if (gcry_md_open (&dfx->mdc_hash, ed->mdc_method, 0 )) + BUG (); + if ( DBG_HASHING ) + gcry_md_debug (dfx->mdc_hash, "checkmdc"); + } - if ( dfx->mdc_hash ) - gcry_md_write (dfx->mdc_hash, temp, nprefix+2); + rc = openpgp_cipher_open (&dfx->cipher_hd, dek->algo, + GCRY_CIPHER_MODE_CFB, + (GCRY_CIPHER_SECURE + | ((ed->mdc_method || dek->algo >= 100)? + 0 : GCRY_CIPHER_ENABLE_SYNC))); + if (rc) + { + /* We should never get an error here cause we already checked + * that the algorithm is available. */ + BUG(); + } + + + /* log_hexdump( "thekey", dek->key, dek->keylen );*/ + rc = gcry_cipher_setkey (dfx->cipher_hd, dek->key, dek->keylen); + if ( gpg_err_code (rc) == GPG_ERR_WEAK_KEY ) + { + log_info(_("WARNING: message was encrypted with" + " a weak key in the symmetric cipher.\n")); + rc=0; + } + else if( rc ) + { + log_error("key setup failed: %s\n", gpg_strerror (rc) ); + goto leave; + } + + if (!ed->buf) + { + log_error(_("problem handling encrypted packet\n")); + goto leave; + } + + gcry_cipher_setiv (dfx->cipher_hd, NULL, 0); + + if ( ed->len ) + { + for (i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) + { + if ( (c=iobuf_get(ed->buf)) == -1 ) + break; + else + temp[i] = c; + } + } + else + { + for (i=0; i < (nprefix+2); i++ ) + if ( (c=iobuf_get(ed->buf)) == -1 ) + break; + else + temp[i] = c; + } + + gcry_cipher_decrypt (dfx->cipher_hd, temp, nprefix+2, NULL, 0); + gcry_cipher_sync (dfx->cipher_hd); + p = temp; + /* log_hexdump( "prefix", temp, nprefix+2 ); */ + if (dek->symmetric + && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) ) + { + rc = gpg_error (GPG_ERR_BAD_KEY); + goto leave; + } + + if ( dfx->mdc_hash ) + gcry_md_write (dfx->mdc_hash, temp, nprefix+2); + } dfx->refcount++; - dfx->partial = ed->is_partial; + dfx->partial = !!ed->is_partial; dfx->length = ed->len; - if ( ed->mdc_method ) + if (ed->aead_algo) + iobuf_push_filter ( ed->buf, aead_decode_filter, dfx ); + else if (ed->mdc_method) iobuf_push_filter ( ed->buf, mdc_decode_filter, dfx ); else iobuf_push_filter ( ed->buf, decode_filter, dfx ); @@ -287,16 +522,16 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) log_assert (dfx->cipher_hd); log_assert (dfx->mdc_hash); - gcry_cipher_decrypt (dfx->cipher_hd, dfx->defer, 22, NULL, 0); - gcry_md_write (dfx->mdc_hash, dfx->defer, 2); + gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 22, NULL, 0); + gcry_md_write (dfx->mdc_hash, dfx->holdback, 2); gcry_md_final (dfx->mdc_hash); - if ( dfx->defer[0] != '\xd3' - || dfx->defer[1] != '\x14' + if ( dfx->holdback[0] != '\xd3' + || dfx->holdback[1] != '\x14' || datalen != 20 - || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->defer+2, datalen)) + || memcmp (gcry_md_read (dfx->mdc_hash, 0), dfx->holdback+2, datalen)) rc = gpg_error (GPG_ERR_BAD_SIGNATURE); - /* log_printhex("MDC message:", dfx->defer, 22); */ + /* log_printhex("MDC message:", dfx->holdback, 22); */ /* log_printhex("MDC calc:", gcry_md_read (dfx->mdc_hash,0), datalen); */ } @@ -307,6 +542,329 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) } +/* Fill BUFFER with up to NBYTES-OFFSET from STREAM utilizing + * information from the context DFX. Returns the new offset which is + * the number of bytes read plus the original offset. On EOF the + * respective flag in DFX is set. */ +static size_t +fill_buffer (decode_filter_ctx_t dfx, iobuf_t stream, + byte *buffer, size_t nbytes, size_t offset) +{ + size_t nread = offset; + int c; + + if (dfx->partial) + { + for (; nread < nbytes; nread++ ) + { + if ((c = iobuf_get (stream)) == -1) + { + dfx->eof_seen = 1; /* Normal EOF. */ + break; + } + buffer[nread] = c; + } + } + else + { + for (; nread < nbytes && dfx->length; nread++, dfx->length--) + { + c = iobuf_get (stream); + if (c == -1) + { + dfx->eof_seen = 3; /* Premature EOF. */ + break; + } + buffer[nread] = c; + } + if (!dfx->length) + dfx->eof_seen = 1; /* Normal EOF. */ + } + + return nread; +} + + +/* The core of the AEAD decryption. This is the underflow function of + * the aead_decode_filter. */ +static gpg_error_t +aead_underflow (decode_filter_ctx_t dfx, iobuf_t a, byte *buf, size_t *ret_len) +{ + const size_t size = *ret_len; /* The allocated size of BUF. */ + gpg_error_t err; + size_t totallen = 0; /* The number of bytes to return on success or EOF. */ + size_t off = 0; /* The offset into the buffer. */ + size_t len; /* The current number of bytes in BUF+OFF. */ + + log_assert (size > 48); /* Our code requires at least this size. */ + + /* Copy the rest from the last call of this function into BUF. */ + len = dfx->holdbacklen; + dfx->holdbacklen = 0; + memcpy (buf, dfx->holdback, len); + + if (DBG_FILTER) + log_debug ("aead_underflow: size=%zu len=%zu%s%s\n", size, len, + dfx->partial? " partial":"", dfx->eof_seen? " eof":""); + + /* Read and fill up BUF. We need to watch out for an EOF so that we + * can detect the last chunk which is commonly shorter than the + * chunksize. After the last data byte from the last chunk 32 more + * bytes are expected for the last chunk's tag and the following + * final chunk's tag. To detect the EOF we need to try reading at least + * one further byte; however we try to read 16 extra bytes to avoid + * single byte reads in some lower layers. The outcome is that we + * have up to 48 extra extra octets which we will later put into the + * holdback buffer for the next invocation (which handles the EOF + * case). */ + len = fill_buffer (dfx, a, buf, size, len); + if (len < 32) + { + /* Not enough data for the last two tags. */ + err = gpg_error (GPG_ERR_TRUNCATED); + goto leave; + } + if (dfx->eof_seen) + { + /* If have seen an EOF we copy only the last two auth tags into + * the holdback buffer. */ + dfx->holdbacklen = 32; + memcpy (dfx->holdback, buf+len-32, 32); + len -= 32; + } + else + { + /* If have not seen an EOF we copy the entire extra 48 bytes + * into the holdback buffer for processing at the next call of + * this function. */ + dfx->holdbacklen = len > 48? 48 : len; + memcpy (dfx->holdback, buf+len-dfx->holdbacklen, dfx->holdbacklen); + len -= dfx->holdbacklen; + } + /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */ + + /* Decrypt the buffer. This first requires a loop to handle the + * case when a chunk ends within the buffer. */ + if (DBG_FILTER) + log_debug ("decrypt: chunklen=%ju total=%ju size=%zu len=%zu%s\n", + (uintmax_t)dfx->chunklen, (uintmax_t)dfx->total, size, len, + dfx->eof_seen? " eof":""); + + while (len && dfx->chunklen + len >= dfx->chunksize) + { + size_t n = dfx->chunksize - dfx->chunklen; + byte tagbuf[16]; + + if (DBG_FILTER) + log_debug ("chunksize will be reached: n=%zu\n", n); + + if (!dfx->chunklen) + { + /* First data for this chunk - prepare. */ + err = aead_set_nonce_and_ad (dfx, 0); + if (err) + goto leave; + } + + /* log_printhex (buf, n, "ciph:"); */ + gcry_cipher_final (dfx->cipher_hd); + err = gcry_cipher_decrypt (dfx->cipher_hd, buf+off, n, NULL, 0); + if (err) + { + log_error ("gcry_cipher_decrypt failed (1): %s\n", + gpg_strerror (err)); + goto leave; + } + /* log_printhex (buf, n, "plai:"); */ + totallen += n; + dfx->chunklen += n; + dfx->total += n; + off += n; + len -= n; + + if (DBG_FILTER) + log_debug ("ndecrypted: %zu (nchunk=%zu) bytes left: %zu at off=%zu\n", + totallen, dfx->chunklen, len, off); + + /* Check the tag. */ + if (len < 16) + { + /* The tag is not entirely in the buffer. Read the rest of + * the tag from the holdback buffer. Then shift the holdback + * buffer and fill it up again. */ + memcpy (tagbuf, buf+off, len); + memcpy (tagbuf + len, dfx->holdback, 16 - len); + dfx->holdbacklen -= 16-len; + memmove (dfx->holdback, dfx->holdback + (16-len), dfx->holdbacklen); + + if (dfx->eof_seen) + { + /* We should have the last chunk's tag in TAGBUF and the + * final tag in HOLDBACKBUF. */ + if (len || dfx->holdbacklen != 16) + { + /* Not enough data for the last two tags. */ + err = gpg_error (GPG_ERR_TRUNCATED); + goto leave; + } + } + else + { + len = 0; + dfx->holdbacklen = fill_buffer (dfx, a, dfx->holdback, 48, + dfx->holdbacklen); + if (dfx->holdbacklen < 32) + { + /* Not enough data for the last two tags. */ + err = gpg_error (GPG_ERR_TRUNCATED); + goto leave; + } + } + } + else /* We already have the full tag. */ + { + memcpy (tagbuf, buf+off, 16); + /* Remove that tag from the output. */ + memmove (buf + off, buf + off + 16, len - 16); + len -= 16; + } + err = aead_checktag (dfx, 0, tagbuf); + if (err) + goto leave; + dfx->chunklen = 0; + dfx->chunkindex++; + + continue; + } + + /* The bulk decryption of our buffer. */ + if (len) + { + if (!dfx->chunklen) + { + /* First data for this chunk - prepare. */ + err = aead_set_nonce_and_ad (dfx, 0); + if (err) + goto leave; + } + + if (dfx->eof_seen) + { + /* This is the last block of the last chunk. Its length may + * not be a multiple of the block length. */ + gcry_cipher_final (dfx->cipher_hd); + } + err = gcry_cipher_decrypt (dfx->cipher_hd, buf + off, len, NULL, 0); + if (err) + { + log_error ("gcry_cipher_decrypt failed (2): %s\n", + gpg_strerror (err)); + goto leave; + } + totallen += len; + dfx->chunklen += len; + dfx->total += len; + if (DBG_FILTER) + log_debug ("ndecrypted: %zu (nchunk=%zu)\n", totallen, dfx->chunklen); + } + + if (dfx->eof_seen) + { + + if (dfx->chunklen) + { + if (DBG_FILTER) + log_debug ("eof seen: holdback has the last and final tag\n"); + log_assert (dfx->holdbacklen >= 32); + err = aead_checktag (dfx, 0, dfx->holdback); + if (err) + goto leave; + dfx->chunklen = 0; + dfx->chunkindex++; + off = 16; + } + else + { + if (DBG_FILTER) + log_debug ("eof seen: holdback has the final tag\n"); + log_assert (dfx->holdbacklen >= 16); + off = 0; + } + + /* Check the final chunk. */ + err = aead_set_nonce_and_ad (dfx, 1); + if (err) + goto leave; + gcry_cipher_final (dfx->cipher_hd); + /* Decrypt an empty string (using HOLDBACK as a dummy). */ + err = gcry_cipher_decrypt (dfx->cipher_hd, dfx->holdback, 0, NULL, 0); + if (err) + { + log_error ("gcry_cipher_decrypt failed (final): %s\n", + gpg_strerror (err)); + goto leave; + } + err = aead_checktag (dfx, 1, dfx->holdback+off); + if (err) + goto leave; + err = gpg_error (GPG_ERR_EOF); + } + + leave: + if (DBG_FILTER) + log_debug ("aead_underflow: returning %zu (%s)\n", + totallen, gpg_strerror (err)); + + /* In case of an auth error we map the error code to the same as + * used by the MDC decryption. */ + if (gpg_err_code (err) == GPG_ERR_CHECKSUM) + err = gpg_error (GPG_ERR_BAD_SIGNATURE); + + /* In case of an error we better wipe out the buffer than to convey + * partly decrypted data. */ + if (err && gpg_err_code (err) != GPG_ERR_EOF) + memset (buf, 0, size); + + *ret_len = totallen; + + return err; +} + + +/* The IOBUF filter used to decrypt AEAD encrypted data. */ +static int +aead_decode_filter (void *opaque, int control, IOBUF a, + byte *buf, size_t *ret_len) +{ + decode_filter_ctx_t dfx = opaque; + int rc = 0; + + if ( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen ) + { + *ret_len = 0; + rc = -1; + } + else if ( control == IOBUFCTRL_UNDERFLOW ) + { + log_assert (a); + + rc = aead_underflow (dfx, a, buf, ret_len); + if (gpg_err_code (rc) == GPG_ERR_EOF) + rc = -1; /* We need to use the old convention in the filter. */ + + } + else if ( control == IOBUFCTRL_FREE ) + { + release_dfx_context (dfx); + } + else if ( control == IOBUFCTRL_DESC ) + { + mem2str (buf, "aead_decode_filter", *ret_len); + } + + return rc; +} + static int mdc_decode_filter (void *opaque, int control, IOBUF a, @@ -357,15 +915,16 @@ mdc_decode_filter (void *opaque, int control, IOBUF a, } if (n == 44) { - /* We have enough stuff - flush the deferred stuff. */ - if ( !dfx->defer_filled ) /* First time. */ + /* We have enough stuff - flush the holdback buffer. */ + if ( !dfx->holdbacklen ) /* First time. */ { memcpy (buf, buf+22, 22); n = 22; } else { - memcpy (buf, dfx->defer, 22); + + memcpy (buf, dfx->holdback, 22); } /* Fill up the buffer. */ if (dfx->partial) @@ -396,13 +955,13 @@ mdc_decode_filter (void *opaque, int control, IOBUF a, dfx->eof_seen = 1; /* Normal EOF. */ } - /* Move the trailing 22 bytes back to the defer buffer. We + /* Move the trailing 22 bytes back to the holdback buffer. We have at least 44 bytes thus a memmove is not needed. */ n -= 22; - memcpy (dfx->defer, buf+n, 22 ); - dfx->defer_filled = 1; + memcpy (dfx->holdback, buf+n, 22 ); + dfx->holdbacklen = 22; } - else if ( !dfx->defer_filled ) /* EOF seen but empty defer buffer. */ + else if ( !dfx->holdbacklen ) /* EOF seen but empty holdback. */ { /* This is bad because it means an incomplete hash. */ n -= 22; @@ -411,9 +970,9 @@ mdc_decode_filter (void *opaque, int control, IOBUF a, } else /* EOF seen (i.e. read less than 22 bytes). */ { - memcpy (buf, dfx->defer, 22 ); + memcpy (buf, dfx->holdback, 22 ); n -= 22; - memcpy (dfx->defer, buf+n, 22 ); + memcpy (dfx->holdback, buf+n, 22 ); dfx->eof_seen = 1; /* Normal EOF. */ } @@ -1,5 +1,5 @@ /* dek.h - The data encryption key structure. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 2014, 2017 Werner Koch * * This file is part of GnuPG. * @@ -26,14 +26,25 @@ typedef struct int algo; /* The length of the key (in bytes). */ int keylen; + /* Whether we've already printed information about this key. This - is currently only used in decrypt_data() and only if we are in - verbose mode. */ - int algo_info_printed; - int use_mdc; + * is currently only used in decrypt_data() and only if we are in + * verbose mode. */ + int algo_info_printed : 1; + + /* AEAD shall be used. The value is the AEAD algo. */ + int use_aead : 4; + + /* MDC shall be used. */ + int use_mdc : 1; + /* This key was read from a SK-ESK packet (see proc_symkey_enc). */ - int symmetric; - byte key[32]; /* This is the largest used keylen (256 bit). */ + int symmetric : 1; + + /* This is the largest used keylen (256 bit). */ + byte key[32]; + + /* The cacheid for the S2K. */ char s2k_cacheid[1+16+1]; } DEK; diff --git a/g10/ecdh.c b/g10/ecdh.c index 6c2a56b84..6587cc4b4 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -76,7 +76,7 @@ pk_ecdh_default_params (unsigned int qbits) } log_assert (i < DIM (kek_params_table)); if (DBG_CRYPTO) - log_printhex ("ECDH KEK params are", kek_params, sizeof(kek_params) ); + log_printhex (kek_params, sizeof(kek_params), "ECDH KEK params are"); return gcry_mpi_set_opaque (NULL, kek_params, 4 * 8); } @@ -159,7 +159,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, memset (secret_x+secret_x_size, 0, nbytes-secret_x_size); if (DBG_CRYPTO) - log_printhex ("ECDH shared secret X is:", secret_x, secret_x_size ); + log_printhex (secret_x, secret_x_size, "ECDH shared secret X is:"); } /*** We have now the shared secret bytes in secret_x. ***/ @@ -179,7 +179,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, kek_params_size = (nbits+7)/8; if (DBG_CRYPTO) - log_printhex ("ecdh KDF params:", kek_params, kek_params_size); + log_printhex (kek_params, kek_params_size, "ecdh KDF params:"); /* Expect 4 bytes 03 01 hash_alg symm_alg. */ if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1) @@ -236,7 +236,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, } if(DBG_CRYPTO) - log_printhex ("ecdh KDF message params are:", message, message_size); + log_printhex (message, message_size, "ecdh KDF message params are:"); } /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. */ @@ -272,7 +272,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* We could have allocated more, so clean the tail before returning. */ memset (secret_x+secret_x_size, 0, old_size - secret_x_size); if (DBG_CRYPTO) - log_printhex ("ecdh KEK is:", secret_x, secret_x_size ); + log_printhex (secret_x, secret_x_size, "ecdh KEK is:"); } /* And, finally, aeswrap with key secret_x. */ @@ -338,7 +338,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, } if (DBG_CRYPTO) - log_printhex ("ecdh encrypting :", in, data_buf_size ); + log_printhex (in, data_buf_size, "ecdh encrypting :"); err = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, in, data_buf_size); @@ -354,7 +354,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, data_buf[0] = data_buf_size+8; if (DBG_CRYPTO) - log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] ); + log_printhex (data_buf+1, data_buf[0], "ecdh encrypted to:"); result = gcry_mpi_set_opaque (NULL, data_buf, 8 * (1+data_buf[0])); if (!result) @@ -391,7 +391,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, data_buf_size = data_buf[0]; if (DBG_CRYPTO) - log_printhex ("ecdh decrypting :", data_buf+1, data_buf_size); + log_printhex (data_buf+1, data_buf_size, "ecdh decrypting :"); err = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, data_buf_size); @@ -407,7 +407,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, data_buf_size -= 8; if (DBG_CRYPTO) - log_printhex ("ecdh decrypted to :", in, data_buf_size); + log_printhex (in, data_buf_size, "ecdh decrypted to :"); /* Padding is removed later. */ /* if (in[data_buf_size-1] > 8 ) */ diff --git a/g10/encrypt.c b/g10/encrypt.c index c68d6d5d1..c6c9e3a03 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -47,12 +47,12 @@ static int write_pubkey_enc_from_list (ctrl_t ctrl, /**************** * Encrypt FILENAME with only the symmetric cipher. Take input from - * stdin if FILENAME is NULL. + * stdin if FILENAME is NULL. If --force-aead is used we use an SKESK. */ int encrypt_symmetric (const char *filename) { - return encrypt_simple( filename, 1, 0 ); + return encrypt_simple( filename, 1, opt.force_aead); } @@ -67,45 +67,173 @@ encrypt_store (const char *filename) } -/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and - the algorithm that will be used to encrypt the contents of the SED - packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random - session key that is appropriate for DEK->ALGO is generated and - stored there. - - Encrypt that session key using DEK and store the result in ENCKEY, - which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */ -void -encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) +/* Encrypt a session key using DEK and store a pointer to the result + * at R_ENCKEY and its length at R_ENCKEYLEN. + * + * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and + * the algorithm that will be used to encrypt the contents of the + * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random + * session key that is appropriate for DEK->ALGO is generated and + * stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm + * is used for encryption. + */ +gpg_error_t +encrypt_seskey (DEK *dek, aead_algo_t aead_algo, + DEK **r_seskey, void **r_enckey, size_t *r_enckeylen) { - gcry_cipher_hd_t hd; - byte buf[33]; + gpg_error_t err; + gcry_cipher_hd_t hd = NULL; + byte *buf = NULL; + DEK *seskey; + + *r_enckey = NULL; + *r_enckeylen = 0; - log_assert ( dek->keylen <= 32 ); - if (!*seskey) + if (*r_seskey) + seskey = *r_seskey; + else { - *seskey=xmalloc_clear(sizeof(DEK)); - (*seskey)->algo=dek->algo; - make_session_key(*seskey); + seskey = xtrycalloc (1, sizeof(DEK)); + if (!seskey) + { + err = gpg_error_from_syserror (); + goto leave; + } + seskey->algo = dek->algo; + make_session_key (seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } - /* The encrypted session key is prefixed with a one-octet algorithm id. */ - buf[0] = (*seskey)->algo; - memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); - - /* We only pass already checked values to the following function, - thus we consider any failure as fatal. */ - if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG (); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG (); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + + if (aead_algo) + { + unsigned int noncelen; + enum gcry_cipher_modes ciphermode; + byte ad[4]; + + err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen); + if (err) + goto leave; + + /* Allocate space for the nonce, the key, and the authentication + * tag (16). */ + buf = xtrymalloc_secure (noncelen + seskey->keylen + 16); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM); + + err = openpgp_cipher_open (&hd, dek->algo, + ciphermode, GCRY_CIPHER_SECURE); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, buf, noncelen); + if (err) + goto leave; + + ad[0] = (0xc0 | PKT_SYMKEY_ENC); + ad[1] = 5; + ad[2] = dek->algo; + ad[3] = aead_algo; + err = gcry_cipher_authenticate (hd, ad, 4); + if (err) + goto leave; + + memcpy (buf + noncelen, seskey->key, seskey->keylen); + gcry_cipher_final (hd); + err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0); + if (err) + goto leave; + err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16); + if (err) + goto leave; + *r_enckeylen = noncelen + seskey->keylen + 16; + *r_enckey = buf; + buf = NULL; + } + else + { + /* In the old version 4 SKESK the encrypted session key is + * prefixed with a one-octet algorithm id. */ + buf = xtrymalloc_secure (1 + seskey->keylen); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + buf[0] = seskey->algo; + memcpy (buf + 1, seskey->key, seskey->keylen); + + err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, NULL, 0); + if (!err) + err = gcry_cipher_encrypt (hd, buf, seskey->keylen + 1, NULL, 0); + if (err) + goto leave; + *r_enckeylen = seskey->keylen + 1; + *r_enckey = buf; + buf = NULL; + } + + /* Return the session key in case we allocated it. */ + *r_seskey = seskey; + seskey = NULL; + + leave: gcry_cipher_close (hd); + if (seskey != *r_seskey) + xfree (seskey); + xfree (buf); + return err; +} + + +/* Return the AEAD algo if we shall use AEAD mode. Returns 0 if AEAD + * shall not be used. */ +aead_algo_t +use_aead (pk_list_t pk_list, int algo) +{ + int can_use; + + if (!opt.flags.rfc4880bis) + { + if (opt.force_aead) + log_info ("Warning: Option %s currently requires option '%s'\n", + "--force-aead", "--rfc4880bis"); + return 0; + } + + can_use = openpgp_cipher_get_algo_blklen (algo) == 16; + + /* With --force-mdc we clearly do not want AEAD. */ + if (opt.force_mdc) + return 0; + + /* However with --force-aead we want AEAD. */ + if (opt.force_aead) + { + if (!can_use) + { + log_info ("Warning: request to use AEAD ignored for cipher '%s'\n", + openpgp_cipher_algo_name (algo)); + return 0; + } + return default_aead_algo (); + } - memcpy( enckey, buf, (*seskey)->keylen + 1 ); - wipememory( buf, sizeof buf ); /* burn key */ + /* AEAD does only work with 128 bit cipher blocklength. */ + if (!can_use) + return 0; + + /* If all keys support AEAD we can use it. */ + return select_aead_from_pklist (pk_list); } @@ -174,9 +302,9 @@ encrypt_simple (const char *filename, int mode, int use_seskey) PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; - byte enckey[33]; + void *enckey = NULL; + size_t enckeylen = 0; int rc = 0; - int seskeylen = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; @@ -229,6 +357,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( mode ) { int canceled; + aead_algo_t aead_algo; s2k = xmalloc_clear( sizeof *s2k ); s2k->mode = opt.s2k_mode; @@ -252,23 +381,42 @@ encrypt_simple (const char *filename, int mode, int use_seskey) "due to the S2K mode\n")); } + /* See whether we want to use AEAD. */ + aead_algo = use_aead (NULL, cfx.dek->algo); + if ( use_seskey ) { DEK *dek = NULL; - seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); - encrypt_seskey( cfx.dek, &dek, enckey ); - xfree( cfx.dek ); cfx.dek = dek; + rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + { + xfree (cfx.dek); + xfree (s2k); + iobuf_close (inp); + release_progress_context (pfx); + return rc; + } + /* Replace key in DEK. */ + xfree (cfx.dek); + cfx.dek = dek; } - if (opt.verbose) - log_info(_("using cipher %s\n"), - openpgp_cipher_algo_name (cfx.dek->algo)); + if (aead_algo) + cfx.dek->use_aead = aead_algo; + else + cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); - cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); + if (opt.verbose) + log_info(_("using cipher %s.%s\n"), + openpgp_cipher_algo_name (cfx.dek->algo), + cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) + /**/ : "CFB"); } - if (do_compress && cfx.dek && cfx.dek->use_mdc + if (do_compress + && cfx.dek + && (cfx.dek->use_mdc || cfx.dek->use_aead) && is_file_compressed(filename, &rc)) { if (opt.verbose) @@ -293,20 +441,24 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( s2k ) { - PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); - enc->version = 4; + /* Fixme: This is quite similar to write_symkey_enc. */ + PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen); + enc->version = cfx.dek->use_aead ? 5 : 4; enc->cipher_algo = cfx.dek->algo; + enc->aead_algo = cfx.dek->use_aead; enc->s2k = *s2k; - if ( use_seskey && seskeylen ) + if (enckeylen) { - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy (enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet( out, &pkt ))) log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); xfree (enc); + xfree (enckey); + enckey = NULL; } if (!opt.no_literal) @@ -363,12 +515,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /* Register the cipher filter. */ if (mode) - iobuf_push_filter ( out, cipher_filter, &cfx ); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &cfx ); /* Register the compress filter. */ if ( do_compress ) { - if (cfx.dek && cfx.dek->use_mdc) + if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } @@ -407,6 +562,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if (pt) pt->buf = NULL; free_packet (&pkt, NULL); + xfree (enckey); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); @@ -438,23 +594,33 @@ setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek) static int -write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, - iobuf_t out) +write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, + DEK *symkey_dek, DEK *dek, iobuf_t out) { - int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); - + int rc; + void *enckey; + size_t enckeylen; PKT_symkey_enc *enc; - byte enckey[33]; PACKET pkt; - enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); - encrypt_seskey(symkey_dek,&dek,enckey); + rc = encrypt_seskey (symkey_dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + return rc; + enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen); + if (!enc) + { + rc = gpg_error_from_syserror (); + xfree (enckey); + return rc; + } - enc->version = 4; + enc->version = aead_algo? 5 : 4; enc->cipher_algo = opt.s2k_cipher_algo; + enc->aead_algo = aead_algo; enc->s2k = *symkey_s2k; - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy( enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); + xfree (enckey); pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; @@ -462,7 +628,7 @@ write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, if ((rc=build_packet(out,&pkt))) log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc)); - xfree(enc); + xfree (enc); return rc; } @@ -676,14 +842,17 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, gnupg_status_compliance_flag (CO_DE_VS), NULL); - cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); + cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); + if (!cfx.dek->use_aead) + cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a - MDC. This forces compressed files to be re-compressed if we do - not have a MDC to give some protection against chosen ciphertext - attacks. */ - - if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2)) + * MDC or AEAD. This forces compressed files to be re-compressed if + * we do not have a MDC to give some protection against chosen + * ciphertext attacks. */ + if (do_compress + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && is_file_compressed (filename, &rc2)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); @@ -697,17 +866,18 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, make_session_key (cfx.dek); if (DBG_CRYPTO) - log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen ); + log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: "); rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out); if (rc) goto leave; /* We put the passphrase (if any) after any public keys as this - seems to be the most useful on the recipient side - there is no - point in prompting a user for a passphrase if they have the - secret key needed to decrypt. */ - if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) + * seems to be the most useful on the recipient side - there is no + * point in prompting a user for a passphrase if they have the + * secret key needed to decrypt. */ + if (use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead, + symkey_dek, cfx.dek, out))) goto leave; if (!opt.no_literal) @@ -750,7 +920,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ - iobuf_push_filter (out, cipher_filter, &cfx); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &cfx); /* Register the compress filter. */ if (do_compress) @@ -777,7 +950,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, /* Algo 0 means no compression. */ if (compr_algo) { - if (cfx.dek && cfx.dek->use_mdc) + if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out,&zfx,compr_algo); } @@ -887,11 +1060,13 @@ encrypt_filter (void *opaque, int control, efx->cfx.dek->algo = opt.def_cipher_algo; } - efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); + efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo); + if (!efx->cfx.dek->use_aead) + efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) - log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen); + log_printhex (efx->cfx.dek->key, efx->cfx.dek->keylen, "DEK is: "); rc = write_pubkey_enc_from_list (efx->ctrl, efx->pk_list, efx->cfx.dek, a); @@ -900,13 +1075,16 @@ encrypt_filter (void *opaque, int control, if(efx->symkey_s2k && efx->symkey_dek) { - rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, - efx->cfx.dek,a); - if(rc) + rc = write_symkey_enc (efx->symkey_s2k, efx->cfx.dek->use_aead, + efx->symkey_dek, efx->cfx.dek, a); + if (rc) return rc; } - iobuf_push_filter (a, cipher_filter, &efx->cfx); + iobuf_push_filter (a, + efx->cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &efx->cfx); efx->header_okay = 1; } @@ -967,9 +1145,11 @@ write_pubkey_enc (ctrl_t ctrl, if ( opt.verbose ) { char *ustr = get_user_id_string_native (ctrl, enc->keyid); - log_info (_("%s/%s encrypted for: \"%s\"\n"), + log_info (_("%s/%s.%s encrypted for: \"%s\"\n"), openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), + dek->use_aead? openpgp_aead_algo_name (dek->use_aead) + /**/ : "CFB", ustr ); xfree (ustr); } diff --git a/g10/filter.h b/g10/filter.h index 9e4b1e538..cd177f4a4 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -88,15 +88,52 @@ struct compress_filter_context_s { typedef struct compress_filter_context_s compress_filter_context_t; -typedef struct { - DEK *dek; - u32 datalen; - gcry_cipher_hd_t cipher_hd; - unsigned int wrote_header : 1; - unsigned int short_blklen_warn : 1; - unsigned long short_blklen_count; - gcry_md_hd_t mdc_hash; - byte enchash[20]; +typedef struct +{ + /* Object with the key and algo */ + DEK *dek; + + /* Length of the data to encrypt if known - 32 bit because OpenPGP + * requires partial encoding for a larger data size. */ + u32 datalen; + + /* The current cipher handle. */ + gcry_cipher_hd_t cipher_hd; + + /* Various processing flags. */ + unsigned int wrote_header : 1; + unsigned int short_blklen_warn : 1; + unsigned long short_blklen_count; + + /* The encoded chunk byte for AEAD. */ + byte chunkbyte; + + /* The decoded CHUNKBYTE. */ + uint64_t chunksize; + + /* The chunk index for AEAD. */ + uint64_t chunkindex; + + /* The number of bytes in the current chunk. */ + uint64_t chunklen; + + /* The total count of encrypted plaintext octets. Note that we + * don't care about encrypting more than 16 Exabyte. */ + uint64_t total; + + /* The hash context and a buffer used for MDC. */ + gcry_md_hd_t mdc_hash; + byte enchash[20]; + + /* The start IV for AEAD encryption. */ + byte startiv[16]; + + /* Using a large buffer for encryption makes processing easier and + * also makes sure the data is well aligned. */ + char *buffer; + size_t bufsize; /* Allocated length. */ + size_t buflen; /* Used length. */ + } cipher_filter_context_t; @@ -145,8 +182,12 @@ void push_compress_filter2(iobuf_t out,compress_filter_context_t *zfx, int algo,int rel); /*-- cipher.c --*/ -int cipher_filter( void *opaque, int control, - iobuf_t chain, byte *buf, size_t *ret_len); +int cipher_filter_cfb (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *ret_len); + +/*-- cipher-aead.c --*/ +int cipher_filter_aead (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *ret_len); /*-- textfilter.c --*/ int text_filter( void *opaque, int control, diff --git a/g10/getkey.c b/g10/getkey.c index dabd052e0..a838c3c61 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -2443,8 +2443,8 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; - const byte *p, *sym, *hash, *zip; - size_t n, nsym, nhash, nzip; + const byte *p, *sym, *aead, *hash, *zip; + size_t n, nsym, naead, nhash, nzip; sig->flags.chosen_selfsig = 1;/* We chose this one. */ uid->created = 0; /* Not created == invalid. */ @@ -2499,6 +2499,9 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM, &n); sym = p; nsym = p ? n : 0; + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD, &n); + aead = p; + naead = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH, &n); hash = p; nhash = p ? n : 0; @@ -2507,7 +2510,7 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) nzip = p ? n : 0; if (uid->prefs) xfree (uid->prefs); - n = nsym + nhash + nzip; + n = nsym + naead + nhash + nzip; if (!n) uid->prefs = NULL; else @@ -2519,6 +2522,11 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) uid->prefs[n].type = PREFTYPE_SYM; uid->prefs[n].value = *sym++; } + for (; naead; naead--, n++) + { + uid->prefs[n].type = PREFTYPE_AEAD; + uid->prefs[n].value = *aead++; + } for (; nhash; nhash--, n++) { uid->prefs[n].type = PREFTYPE_HASH; @@ -2539,6 +2547,12 @@ fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) if (p && n && (p[0] & 0x01)) uid->flags.mdc = 1; + /* See whether we have the AEAD feature. */ + uid->flags.aead = 0; + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); + if (p && n && (p[0] & 0x01)) + uid->flags.aead = 1; + /* And the keyserver modify flag. */ uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); @@ -3357,6 +3371,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) PKT_public_key *main_pk; prefitem_t *prefs; unsigned int mdc_feature; + unsigned int aead_feature; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { @@ -3418,7 +3433,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) * all preferences. * Do a similar thing for the MDC feature flag. */ prefs = NULL; - mdc_feature = 0; + mdc_feature = aead_feature = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID @@ -3427,6 +3442,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) { prefs = k->pkt->pkt.user_id->prefs; mdc_feature = k->pkt->pkt.user_id->flags.mdc; + aead_feature = k->pkt->pkt.user_id->flags.aead; break; } } @@ -3440,6 +3456,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) xfree (pk->prefs); pk->prefs = copy_prefs (prefs); pk->flags.mdc = mdc_feature; + pk->flags.aead = aead_feature; } } } @@ -105,6 +105,7 @@ enum cmd_and_opt_values oBatch = 500, oMaxOutput, oInputSizeHint, + oChunkSize, oSigNotation, oCertNotation, oShowNotation, @@ -197,6 +198,7 @@ enum cmd_and_opt_values oWithSubkeyFingerprint, oWithICAOSpelling, oWithKeygrip, + oWithKeyScreening, oWithSecret, oWithWKDHash, oWithColons, @@ -221,6 +223,7 @@ enum cmd_and_opt_values oDebugLevel, oDebugAll, oDebugIOLBF, + oDebugSetIobufSize, oStatusFD, oStatusFile, oAttributeFD, @@ -244,6 +247,7 @@ enum cmd_and_opt_values oRFC2440Text, oNoRFC2440Text, oCipherAlgo, + oAEADAlgo, oDigestAlgo, oCertDigestAlgo, oCompressAlgo, @@ -302,6 +306,7 @@ enum cmd_and_opt_values oNoForceMDC, oDisableMDC, oNoDisableMDC, + oForceAEAD, oS2KMode, oS2KDigest, oS2KCipher, @@ -370,6 +375,7 @@ enum cmd_and_opt_values oDefaultPreferenceList, oDefaultKeyserverURL, oPersonalCipherPreferences, + oPersonalAEADPreferences, oPersonalDigestPreferences, oPersonalCompressPreferences, oAgentProgram, @@ -592,6 +598,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), ARGPARSE_p_u (oMaxOutput, "max-output", "@"), ARGPARSE_s_s (oInputSizeHint, "input-size-hint", "@"), + ARGPARSE_s_i (oChunkSize, "chunk-size", "@"), ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", "@"), @@ -602,6 +609,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oDisableMDC, "disable-mdc", "@"), ARGPARSE_s_n (oNoDisableMDC, "no-disable-mdc", "@"), + ARGPARSE_s_n (oForceAEAD, "force-aead", "@"), + ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"), ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), @@ -634,6 +643,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oDebugLevel, "debug-level", "@"), ARGPARSE_s_n (oDebugAll, "debug-all", "@"), ARGPARSE_s_n (oDebugIOLBF, "debug-iolbf", "@"), + ARGPARSE_s_u (oDebugSetIobufSize, "debug-set-iobuf-size", "@"), ARGPARSE_s_i (oStatusFD, "status-fd", "@"), ARGPARSE_s_s (oStatusFile, "status-file", "@"), ARGPARSE_s_i (oAttributeFD, "attribute-fd", "@"), @@ -667,6 +677,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"), ARGPARSE_s_i (oS2KCount, "s2k-count", "@"), ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), + ARGPARSE_s_s (oAEADAlgo, "aead-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"), @@ -785,6 +796,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"), ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), + ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_n (oWithWKDHash, "with-wkd-hash", "@"), ARGPARSE_s_n (oWithKeyOrigin, "with-key-origin", "@"), @@ -822,6 +834,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oDefaultPreferenceList, "default-preference-list", "@"), ARGPARSE_s_s (oDefaultKeyserverURL, "default-keyserver-url", "@"), ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-preferences","@"), + ARGPARSE_s_s (oPersonalAEADPreferences, "personal-aead-preferences","@"), ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-preferences","@"), ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-preferences", "@"), @@ -833,6 +846,7 @@ static ARGPARSE_OPTS opts[] = { /* Aliases. I constantly mistype these, and assume other people do as well. */ ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-prefs", "@"), + ARGPARSE_s_s (oPersonalAEADPreferences, "personal-aead-prefs", "@"), ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-prefs", "@"), ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-prefs", "@"), @@ -944,6 +958,8 @@ int g10_errors_seen = 0; static int utf8_strings = 0; static int maybe_setuid = 1; +static unsigned int opt_set_iobuf_size; +static unsigned int opt_set_iobuf_size_used; static char *build_list( const char *text, char letter, const char *(*mapf)(int), int (*chkf)(int) ); @@ -1006,6 +1022,18 @@ build_list_cipher_algo_name (int algo) } static int +build_list_aead_test_algo (int algo) +{ + return openpgp_aead_test_algo (algo); +} + +static const char * +build_list_aead_algo_name (int algo) +{ + return openpgp_aead_algo_name (algo); +} + +static int build_list_md_test_algo (int algo) { /* By default we do not accept MD5 based signatures. To avoid @@ -1026,7 +1054,7 @@ build_list_md_algo_name (int algo) static const char * my_strusage( int level ) { - static char *digests, *pubkeys, *ciphers, *zips, *ver_gcry; + static char *digests, *pubkeys, *ciphers, *zips, *aeads, *ver_gcry; const char *p; switch( level ) { @@ -1086,13 +1114,20 @@ my_strusage( int level ) p = ciphers; break; case 36: + if (!aeads) + aeads = build_list ("AEAD: ", 'A', + build_list_aead_algo_name, + build_list_aead_test_algo); + p = aeads; + break; + case 37: if( !digests ) digests = build_list(_("Hash: "), 'H', build_list_md_algo_name, build_list_md_test_algo ); p = digests; break; - case 37: + case 38: if( !zips ) zips = build_list(_("Compression: "),'Z', compress_algo_to_string, @@ -1113,6 +1148,7 @@ build_list (const char *text, char letter, membuf_t mb; int indent; int i, j, len; + int limit; const char *s; char *string; @@ -1123,7 +1159,8 @@ build_list (const char *text, char letter, len = 0; init_membuf (&mb, 512); - for (i=0; i <= 110; i++ ) + limit = (letter == 'A')? 4 : 110; + for (i=0; i <= limit; i++ ) { if (!chkf (i) && (s = mapf (i))) { @@ -1243,6 +1280,10 @@ set_debug (const char *level) if (opt.debug) parse_debug_flag (NULL, &opt.debug, debug_flags); + + if (opt_set_iobuf_size || opt_set_iobuf_size_used) + log_debug ("iobuf buffer size is %uk\n", + iobuf_set_buffer_size (opt_set_iobuf_size)); } @@ -2123,6 +2164,7 @@ set_compliance_option (enum cmd_and_opt_values option) opt.escape_from = 1; opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; + opt.def_aead_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; opt.compress_algo = -1; @@ -2139,6 +2181,7 @@ set_compliance_option (enum cmd_and_opt_values option) opt.escape_from = 0; opt.not_dash_escaped = 0; opt.def_cipher_algo = 0; + opt.def_aead_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; opt.compress_algo = -1; @@ -2155,6 +2198,7 @@ set_compliance_option (enum cmd_and_opt_values option) set_compliance_option (oOpenPGP); opt.compliance = CO_DE_VS; opt.force_mdc = 1; + opt.def_aead_algo = 0; /* Fixme: Change other options. */ break; @@ -2284,12 +2328,14 @@ main (int argc, char **argv) const char *trustdb_name = NULL; #endif /*!NO_TRUST_MODELS*/ char *def_cipher_string = NULL; + char *def_aead_string = NULL; char *def_digest_string = NULL; char *compress_algo_string = NULL; char *cert_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; char *pers_cipher_list = NULL; + char *pers_aead_list = NULL; char *pers_digest_list = NULL; char *pers_compress_list = NULL; int eyes_only=0; @@ -2353,6 +2399,7 @@ main (int argc, char **argv) opt.bz2_compress_level = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; + opt.def_aead_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */ @@ -2632,6 +2679,10 @@ main (int argc, char **argv) opt.input_size_hint = string_to_u64 (pargs.r.ret_str); break; + case oChunkSize: + opt.chunk_size = pargs.r.ret_int; + break; + case oQuiet: opt.quiet = 1; break; case oNoTTY: tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; @@ -2699,6 +2750,11 @@ main (int argc, char **argv) case oDebugIOLBF: break; /* Already set in pre-parse step. */ + case oDebugSetIobufSize: + opt_set_iobuf_size = pargs.r.ret_ulong; + opt_set_iobuf_size_used = 1; + break; + case oStatusFD: set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) ); break; @@ -2737,6 +2793,10 @@ main (int argc, char **argv) opt.with_keygrip = 1; break; + case oWithKeyScreening: + opt.with_key_screening = 1; + break; + case oWithSecret: opt.with_secret = 1; break; @@ -2960,6 +3020,8 @@ main (int argc, char **argv) case oDisableMDC: opt.disable_mdc = 1; break; case oNoDisableMDC: opt.disable_mdc = 0; break; + case oForceAEAD: opt.force_aead = 1; break; + case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break; case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; @@ -3107,6 +3169,9 @@ main (int argc, char **argv) case oCipherAlgo: def_cipher_string = xstrdup(pargs.r.ret_str); break; + case oAEADAlgo: + def_aead_string = xstrdup (pargs.r.ret_str); + break; case oDigestAlgo: def_digest_string = xstrdup(pargs.r.ret_str); break; @@ -3386,6 +3451,9 @@ main (int argc, char **argv) case oPersonalCipherPreferences: pers_cipher_list=pargs.r.ret_str; break; + case oPersonalAEADPreferences: + pers_aead_list = pargs.r.ret_str; + break; case oPersonalDigestPreferences: pers_digest_list=pargs.r.ret_str; break; @@ -3731,6 +3799,13 @@ main (int argc, char **argv) if ( openpgp_cipher_test_algo (opt.def_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } + if (def_aead_string) + { + opt.def_aead_algo = string_to_aead_algo (def_aead_string); + xfree (def_aead_string); def_aead_string = NULL; + if (openpgp_aead_test_algo (opt.def_aead_algo)) + log_error(_("selected AEAD algorithm is invalid\n")); + } if( def_digest_string ) { opt.def_digest_algo = string_to_digest_algo (def_digest_string); xfree(def_digest_string); def_digest_string = NULL; @@ -3790,6 +3865,9 @@ main (int argc, char **argv) keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM)) log_error(_("invalid personal cipher preferences\n")); + if (pers_aead_list && keygen_set_std_prefs (pers_aead_list, PREFTYPE_AEAD)) + log_error(_("invalid personal AEAD preferences\n")); + if(pers_digest_list && keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH)) log_error(_("invalid personal digest preferences\n")); @@ -3798,6 +3876,21 @@ main (int argc, char **argv) keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP)) log_error(_("invalid personal compress preferences\n")); + /* Check chunk size. Please fix also the man page if you chnage + * the default. The limits are given by the specs. */ + if (!opt.chunk_size) + opt.chunk_size = 30; /* Default to 1 GiB chunks. */ + else if (opt.chunk_size < 6) + { + opt.chunk_size = 6; + log_info (_("chunk size invalid - using %d\n"), opt.chunk_size); + } + else if (opt.chunk_size > 62) + { + opt.chunk_size = 62; + log_info (_("chunk size invalid - using %d\n"), opt.chunk_size); + } + /* We don't support all possible commands with multifile yet */ if(multifile) { @@ -3844,7 +3937,7 @@ main (int argc, char **argv) /* Check our chosen algorithms against the list of legal algorithms. */ - if(!GNUPG) + if(!GNUPG && !opt.flags.rfc4880bis) { const char *badalg=NULL; preftype_t badtype=PREFTYPE_NONE; @@ -3855,6 +3948,12 @@ main (int argc, char **argv) badalg = openpgp_cipher_algo_name (opt.def_cipher_algo); badtype = PREFTYPE_SYM; } + else if(opt.def_aead_algo + && !algo_available(PREFTYPE_AEAD, opt.def_aead_algo, NULL)) + { + badalg = openpgp_aead_algo_name (opt.def_aead_algo); + badtype = PREFTYPE_AEAD; + } else if(opt.def_digest_algo && !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL)) { @@ -3884,6 +3983,12 @@ main (int argc, char **argv) badalg, gnupg_compliance_option_string (opt.compliance)); break; + case PREFTYPE_AEAD: + log_info (_("AEAD algorithm '%s'" + " may not be used in %s mode\n"), + badalg, + gnupg_compliance_option_string (opt.compliance)); + break; case PREFTYPE_HASH: log_info (_("digest algorithm '%s'" " may not be used in %s mode\n"), @@ -3909,6 +4014,7 @@ main (int argc, char **argv) * is not. This is us being nice to the user informing her early * that the chosen algorithms are not available. We also check * and enforce this right before the actual operation. */ + /* FIXME: We also need to check the AEAD algo. */ if (opt.def_cipher_algo && ! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr @@ -38,14 +38,15 @@ #define MAX_EXTERN_MPI_BITS 16384 /* 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 still use 20 instead of this macro. */ -#define MAX_FINGERPRINT_LEN 20 + * provide a static buffer and will be increased if we need to support + * longer fingerprints. Warning: At some places we have some + * assumption on a 20 byte fingerprint. + * Watch out for FIXME(fingerprint) */ +#define MAX_FINGERPRINT_LEN 32 /* The maximum length of a formatted fingerprint as returned by - format_hexfingerprint(). */ -#define MAX_FORMATTED_FINGERPRINT_LEN 50 + * format_hexfingerprint(). */ +#define MAX_FORMATTED_FINGERPRINT_LEN 59 /* diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 2b42bfbf9..094bc7614 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -2283,9 +2283,11 @@ sk_esk (const char *option, int argc, char *argv[], void *cookie) DEK *sesdekp = &sesdek; /* Now encrypt the session key (or rather, the algorithm used to - encrypt the SED plus the session key) using ENCKEY. */ - ske->seskeylen = 1 + sesdek.keylen; - encrypt_seskey (&s2kdek, &sesdekp, ske->seskey); + encrypt the SKESK plus the session key) using ENCKEY. */ + err = encrypt_seskey (&s2kdek, 0, &sesdekp, + (void**)&ske->seskey, (size_t *)&ske->seskeylen); + if (err) + log_fatal ("encrypt_seskey failed: %s\n", gpg_strerror (err)); /* Save the session key for later. */ session_key = sesdek; @@ -2573,7 +2575,7 @@ encrypted (const char *option, int argc, char *argv[], void *cookie) cfx->datalen = 0; - filter_push (out, cipher_filter, cfx, PKT_ENCRYPTED, cfx->datalen == 0); + filter_push (out, cipher_filter_cfb, cfx, PKT_ENCRYPTED, cfx->datalen == 0); debug ("Wrote encrypted packet:\n"); @@ -2746,7 +2748,7 @@ literal_name (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; - if (argc <= 1) + if (argc <= 0) log_fatal ("Usage: %s NAME\n", option); if (strlen (argv[0]) > 255) diff --git a/g10/import.c b/g10/import.c index 71e39557c..ed679d5c0 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1113,6 +1113,24 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) problem=1; } } + else if(prefs->type==PREFTYPE_AEAD) + { + if (openpgp_aead_test_algo (prefs->value)) + { + /* FIXME: The test below is wrong. We should + * check if ...algo_name yields a "?" and + * only in that case use NUM. */ + const char *algo = + (openpgp_aead_test_algo (prefs->value) + ? num + : openpgp_aead_algo_name (prefs->value)); + if(!problem) + check_prefs_warning(pk); + log_info(_(" \"%s\": preference for AEAD" + " algorithm %s\n"), user, algo); + problem=1; + } + } else if(prefs->type==PREFTYPE_HASH) { if(openpgp_md_test_algo(prefs->value)) @@ -2255,6 +2273,7 @@ transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, { char countbuf[35]; + /* FIXME: Support AEAD */ /* Note that the IVLEN may be zero if we are working on a dummy key. We can't express that in an S-expression and thus we send dummy data for the IV. */ diff --git a/g10/kbnode.c b/g10/kbnode.c index c2aaacd76..64def0509 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -415,13 +415,14 @@ dump_kbnode (KBNODE node) { PKT_public_key *pk = node->pkt->pkt.public_key; - log_printf (" keyid=%08lX a=%d u=%d %c%c%c%c\n", + log_printf (" keyid=%08lX a=%d u=%d %c%c%c%c%c\n", (ulong)keyid_from_pk( pk, NULL ), pk->pubkey_algo, pk->pubkey_usage, pk->has_expired? 'e':'.', pk->flags.revoked? 'r':'.', pk->flags.valid? 'v':'.', - pk->flags.mdc? 'm':'.'); + pk->flags.mdc? 'm':'.', + pk->flags.aead? 'a':'.'); } else log_printf ("\n"); diff --git a/g10/keydb.h b/g10/keydb.h index 739376838..f2ea8a7d2 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -232,6 +232,7 @@ int algo_available( preftype_t preftype, int algo, int select_algo_from_prefs( PK_LIST pk_list, int preftype, int request, const union pref_hint *hint); int select_mdc_from_pklist (PK_LIST pk_list); +aead_algo_t select_aead_from_pklist (pk_list_t pk_list); void warn_missing_mdc_from_pklist (PK_LIST pk_list); void warn_missing_aes_from_pklist (PK_LIST pk_list); diff --git a/g10/keyedit.c b/g10/keyedit.c index 17cf7d6ea..2c33a29dd 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1142,7 +1142,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock) if (err) log_log ((gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) - ? GPGRT_LOG_INFO : GPGRT_LOG_ERROR, + ? GPGRT_LOGLVL_INFO : GPGRT_LOGLVL_ERROR, _("key %s: error changing passphrase: %s\n"), keystr_with_sub (keyid, subid), gpg_strerror (err)); @@ -3064,6 +3064,23 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); } tty_printf ("\n "); + tty_printf (_("AEAD: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_AEAD) + { + if (any) + tty_printf (", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!openpgp_aead_test_algo (prefs[i].value) + && prefs[i].value < 100) + tty_printf ("%s", openpgp_aead_algo_name (prefs[i].value)); + else + tty_printf ("[%d]", prefs[i].value); + } + } + tty_printf ("\n "); tty_printf (_("Digest: ")); for (i = any = 0; prefs[i].type; i++) { @@ -3118,7 +3135,7 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) } tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); } - if (uid->flags.mdc || !uid->flags.ks_modify) + if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) { tty_printf ("\n "); tty_printf (_("Features: ")); @@ -3128,6 +3145,12 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) tty_printf ("MDC"); any = 1; } + if (!uid->flags.aead) + { + if (any) + tty_printf (", "); + tty_printf ("AEAD"); + } if (!uid->flags.ks_modify) { if (any) @@ -3166,12 +3189,15 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) for (i = 0; prefs[i].type; i++) { tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : + prefs[i].type == PREFTYPE_AEAD ? 'A' : prefs[i].type == PREFTYPE_HASH ? 'H' : prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[i].value); } if (uid->flags.mdc) tty_printf (" [mdc]"); + if (uid->flags.aead) + tty_printf (" [aead]"); if (!uid->flags.ks_modify) tty_printf (" [no-ks-modify]"); tty_printf ("\n"); @@ -3317,6 +3343,8 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) } if (uid->flags.mdc) es_fputs (",mdc", fp); + if (uid->flags.aead) + es_fputs (",aead", fp); if (!uid->flags.ks_modify) es_fputs (",no-ks-modify", fp); } diff --git a/g10/keygen.c b/g10/keygen.c index 8de6538ca..cded87c45 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -46,11 +46,10 @@ #include "../common/mbox-util.h" -/* The default algorithms. If you change them remember to change them - also in gpg.c:gpgconf_list. You should also check that the value +/* The default algorithms. If you change them, you should ensure the value is inside the bounds enforced by ask_keysize and gen_xxx. See also get_keysize_range which encodes the allowed ranges. */ -#define DEFAULT_STD_KEY_PARAM "rsa2048/cert,sign+rsa2048/encr" +#define DEFAULT_STD_KEY_PARAM "rsa3072/cert,sign+rsa3072/encr" #define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr" /* When generating keys using the streamlined key generation dialog, @@ -91,7 +90,7 @@ enum para_name { pHANDLE, pKEYSERVER, pKEYGRIP, - pSUBKEYGRIP, + pSUBKEYGRIP }; struct para_data_s { @@ -128,6 +127,9 @@ struct opaque_data_usage_and_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. */ static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; @@ -135,7 +137,11 @@ static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; -static int mdc_available,ks_modify; +static byte aead_prefs[MAX_PREFS]; +static int naead_prefs; +static int mdc_available; +static int ks_modify; +static int aead_available; static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, @@ -327,6 +333,8 @@ set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); + else if(type==4) + log_info(_("too many AEAD preferences\n")); else BUG(); @@ -347,10 +355,10 @@ set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) int keygen_set_std_prefs (const char *string,int personal) { - byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; - int nsym=0, nhash=0, nzip=0, val, rc=0; + byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS], aead[MAX_PREFS]; + int nsym=0, nhash=0, nzip=0, naead=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ - char dummy_string[20*4+1]; /* Enough for 20 items. */ + char dummy_string[25*4+1]; /* Enough for 25 items. */ if (!string || !ascii_strcasecmp (string, "default")) { @@ -384,6 +392,11 @@ keygen_set_std_prefs (const char *string,int personal) strcat(dummy_string,"S7 "); strcat(dummy_string,"S2 "); /* 3DES */ + if (opt.flags.rfc4880bis && !openpgp_aead_test_algo (AEAD_ALGO_OCB)) + strcat(dummy_string,"A2 "); + if (opt.flags.rfc4880bis && !openpgp_aead_test_algo (AEAD_ALGO_EAX)) + strcat(dummy_string,"A1 "); + if (personal) { /* The default internal hash algo order is: @@ -476,6 +489,11 @@ keygen_set_std_prefs (const char *string,int personal) if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } + else if ((val=string_to_aead_algo (tok))) + { + if (set_one_pref (val, 4, tok, aead, &naead)) + rc = -1; + } else if (ascii_strcasecmp(tok,"mdc")==0) mdc=1; else if (ascii_strcasecmp(tok,"no-mdc")==0) @@ -521,6 +539,29 @@ keygen_set_std_prefs (const char *string,int personal) opt.personal_cipher_prefs[i].value = 0; } } + else if (personal == PREFTYPE_AEAD) + { + xfree(opt.personal_aead_prefs); + + if (!naead) + opt.personal_aead_prefs = NULL; + else + { + int i; + + opt.personal_aead_prefs= + xmalloc(sizeof(prefitem_t *)*(naead+1)); + + for (i=0; i<naead; i++) + { + opt.personal_aead_prefs[i].type = PREFTYPE_AEAD; + opt.personal_aead_prefs[i].value = sym[i]; + } + + opt.personal_aead_prefs[i].type = PREFTYPE_NONE; + opt.personal_aead_prefs[i].value = 0; + } + } else if(personal==PREFTYPE_HASH) { xfree(opt.personal_digest_prefs); @@ -573,7 +614,9 @@ keygen_set_std_prefs (const char *string,int personal) memcpy (sym_prefs, sym, (nsym_prefs=nsym)); memcpy (hash_prefs, hash, (nhash_prefs=nhash)); memcpy (zip_prefs, zip, (nzip_prefs=nzip)); + memcpy (aead_prefs, aead, (naead_prefs=naead)); mdc_available = mdc; + aead_available = !!naead; ks_modify = modify; prefs_initialized = 1; } @@ -582,6 +625,7 @@ keygen_set_std_prefs (const char *string,int personal) return rc; } + /* Return a fake user ID containing the preferences. Caller must free. */ PKT_user_id * @@ -595,8 +639,8 @@ keygen_get_std_prefs(void) uid->ref=1; - uid->prefs=xmalloc((sizeof(prefitem_t *)* - (nsym_prefs+nhash_prefs+nzip_prefs+1))); + uid->prefs = xmalloc ((sizeof(prefitem_t *)* + (nsym_prefs+naead_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;i<nsym_prefs;i++,j++) { @@ -604,6 +648,12 @@ keygen_get_std_prefs(void) uid->prefs[j].value=sym_prefs[i]; } + for (i=0; i < naead_prefs; i++, j++) + { + uid->prefs[j].type = PREFTYPE_AEAD; + uid->prefs[j].value = aead_prefs[i]; + } + for(i=0;i<nhash_prefs;i++,j++) { uid->prefs[j].type=PREFTYPE_HASH; @@ -619,8 +669,9 @@ keygen_get_std_prefs(void) uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; - uid->flags.mdc=mdc_available; - uid->flags.ks_modify=ks_modify; + uid->flags.mdc = mdc_available; + uid->flags.aead = aead_available; + uid->flags.ks_modify = ks_modify; return uid; } @@ -666,6 +717,49 @@ add_feature_mdc (PKT_signature *sig,int enabled) xfree (buf); } + +static void +add_feature_aead (PKT_signature *sig, int enabled) +{ + const byte *s; + size_t n; + int i; + char *buf; + + s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); + if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02)))) + return; /* Already set or cleared */ + + if (!s || !n) + { /* Create a new one */ + n = 1; + buf = xmalloc_clear (n); + } + else + { + buf = xmalloc (n); + memcpy (buf, s, n); + } + + if (enabled) + buf[0] |= 0x02; /* AEAD supported */ + else + buf[0] &= ~0x02; + + /* Are there any bits set? */ + for (i=0; i < n; i++) + if (buf[i]) + break; + + if (i == n) + delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); + else + build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); + + xfree (buf); +} + + static void add_keyserver_modify (PKT_signature *sig,int enabled) { @@ -727,6 +821,14 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } + if (naead_prefs) + build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, aead_prefs, naead_prefs); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD); + } + if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else @@ -745,6 +847,7 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); + add_feature_aead (sig, aead_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); @@ -1648,7 +1751,7 @@ gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, if (nbits < 1024) { - nbits = 2048; + nbits = 3072; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > maxsize) @@ -2117,7 +2220,7 @@ get_keysize_range (int algo, unsigned int *min, unsigned int *max) default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; - def = 2048; + def = 3072; break; } diff --git a/g10/keyid.c b/g10/keyid.c index ba35ec21f..a9034ee46 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -73,7 +73,7 @@ pubkey_letter( int algo ) is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: - "rsa2048" - RSA with 2048 bit + "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". @@ -83,7 +83,7 @@ pubkey_letter( int algo ) If the option --legacy-list-mode is active, the output use the legacy format: - "2048R" - RSA with 2048 bit + "3072R" - RSA with 3072 bit "1024g" - Elgamal with 1024 bit "256E" - ECDSA using a curve with 256 bit @@ -839,8 +839,22 @@ format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) /* Half way through we add a second space. */ + 1); } + else if (hexlen == 64 || hexlen == 50) /* v5 fingerprint */ + { + /* The v5 fingerprint is commonly printed truncated to 25 + * octets. We accept the truncated as well as the full hex + * version here and format it like this: + * B2CCB6 838332 5D61BA C50F9F 5E CD21A8 0AC8C5 2565C8 C52565 + */ + hexlen = 50; + space = 8 * 6 + 2 + 8 + 1; + } else /* Other fingerprint versions - print as is. */ { + /* We truncated here so that we do not need to provide a buffer + * of a length which is in reality never used. */ + if (hexlen > MAX_FORMATTED_FINGERPRINT_LEN - 1) + hexlen = MAX_FORMATTED_FINGERPRINT_LEN - 1; space = hexlen + 1; } @@ -853,7 +867,7 @@ format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) { for (i = 0, j = 0; i < 40; i ++) { - if (i && i % 4 == 0) + if (i && !(i % 4)) buffer[j ++] = ' '; if (i == 40 / 2) buffer[j ++] = ' '; @@ -863,9 +877,29 @@ format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) buffer[j ++] = 0; log_assert (j == space); } + else if (hexlen == 50) /* v5 fingerprint */ + { + for (i=j=0; i < 24; i++) + { + if (i && !(i % 6)) + buffer[j++] = ' '; + buffer[j++] = fingerprint[i]; + } + buffer[j++] = ' '; + buffer[j++] = fingerprint[i++]; + buffer[j++] = fingerprint[i++]; + for (; i < 50; i++) + { + if (!((i-26) % 6)) + buffer[j++] = ' '; + buffer[j++] = fingerprint[i]; + } + buffer[j++] = 0; + log_assert (j == space); + } else { - strcpy (buffer, fingerprint); + mem2str (buffer, fingerprint, space); } return buffer; @@ -948,7 +982,7 @@ keygrip_from_pk (PKT_public_key *pk, unsigned char *array) else { if (DBG_PACKET) - log_printhex ("keygrip=", array, 20); + log_printhex (array, 20, "keygrip="); /* FIXME: Save the keygrip in PK. */ } gcry_sexp_release (s_pkey); @@ -963,18 +997,18 @@ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; - unsigned char grip[20]; + unsigned char grip[KEYGRIP_LEN]; *r_grip = NULL; err = keygrip_from_pk (pk, grip); if (!err) { - char * buf = xtrymalloc (20*2+1); + char * buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); if (!buf) err = gpg_error_from_syserror (); else { - bin2hex (grip, 20, buf); + bin2hex (grip, KEYGRIP_LEN, buf); *r_grip = buf; } } diff --git a/g10/keylist.c b/g10/keylist.c index 86d1c564f..bcbad450a 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -45,6 +45,7 @@ #include "../common/zb32.h" #include "tofu.h" #include "../common/compliance.h" +#include "../common/pkscreening.h" static void list_all (ctrl_t, int, int); @@ -696,6 +697,37 @@ print_key_data (PKT_public_key * pk) } } + +/* Various public key screenings. (Right now just ROCA). With + * COLON_MODE set the output is formatted for use in the compliance + * field of a colon listing. + */ +static void +print_pk_screening (PKT_public_key *pk, int colon_mode) +{ + gpg_error_t err; + + if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo)) + { + err = screen_key_for_roca (pk->pkey[0]); + if (!err) + ; + else if (gpg_err_code (err) == GPG_ERR_TRUE) + { + if (colon_mode) + es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001); + else + es_fprintf (es_stdout, + " Screening: ROCA vulnerability detected\n"); + } + else if (!colon_mode) + es_fprintf (es_stdout, " Screening: [ROCA check failed: %s]\n", + gpg_strerror (err)); + } + +} + + static void print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock) { @@ -922,6 +954,9 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, if (opt.with_key_data) print_key_data (pk); + if (opt.with_key_screening) + print_pk_screening (pk, 0); + if (opt.with_key_origin && (pk->keyorg || pk->keyupdate || pk->updateurl)) { @@ -1063,6 +1098,8 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, es_fprintf (es_stdout, " Keygrip = %s\n", 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 && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) @@ -1227,6 +1264,9 @@ print_compliance_flags (PKT_public_key *pk, gnupg_status_compliance_flag (CO_DE_VS)); any++; } + + if (opt.with_key_screening) + print_pk_screening (pk, 1+any); } @@ -1906,6 +1946,9 @@ print_card_serialno (const char *serialno) * pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B367 * + * pub rsa2048 2017-12-31 [SC] [expires: 2028-12-31] + * 80615870F5BAD690333686D0F2AD85AC1E42B3671122334455 + * * Some global options may result in a different output format. If * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and * depending on the value a flag character is shown: diff --git a/g10/main.h b/g10/main.h index 6c15a2a8d..de8639e98 100644 --- a/g10/main.h +++ b/g10/main.h @@ -31,7 +31,9 @@ (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ -#if GPG_USE_AES128 +#if GPG_USE_AES256 +# define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES256 +#elif GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 @@ -39,6 +41,12 @@ # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif +#if GCRYPT_VERSION_NUMBER < 0x019000 +# define DEFAULT_AEAD_ALGO AEAD_ALGO_OCB +#else +# define DEFAULT_AEAD_ALGO AEAD_ALGO_EAX +#endif + #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP @@ -121,6 +129,12 @@ int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); +gpg_error_t openpgp_aead_test_algo (aead_algo_t algo); +const char *openpgp_aead_algo_name (aead_algo_t algo); +gpg_error_t openpgp_aead_algo_info (aead_algo_t algo, + enum gcry_cipher_modes *r_mode, + unsigned int *r_noncelen); + pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); @@ -149,12 +163,14 @@ void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); +aead_algo_t string_to_aead_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); +aead_algo_t default_aead_algo(void); int default_compress_algo(void); void compliance_failure(void); @@ -223,7 +239,9 @@ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); -void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); +gpg_error_t encrypt_seskey (DEK *dek, aead_algo_t aead_algo, DEK **r_seskey, + void **r_enckey, size_t *r_enckeylen); +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 ); @@ -323,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 inp_fd, const char *iname, int mode, +int open_outfile (int 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); diff --git a/g10/mainproc.c b/g10/mainproc.c index 512d33c59..1af89c501 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -245,46 +245,116 @@ add_signature (CTX c, PACKET *pkt) return 1; } -static int +static gpg_error_t symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen) { + gpg_error_t err; gcry_cipher_hd_t hd; + unsigned int noncelen, keylen; + enum gcry_cipher_modes ciphermode; + + if (dek->use_aead) + { + err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen); + if (err) + return err; + } + else + { + ciphermode = GCRY_CIPHER_MODE_CFB; + noncelen = 0; + } - if(slen < 17 || slen > 33) + /* Check that the session key has a size of 16 to 32 bytes. */ + if ((dek->use_aead && (slen < (noncelen + 16 + 16) + || slen > (noncelen + 32 + 16))) + || (!dek->use_aead && (slen < 17 || slen > 33))) { log_error ( _("weird size for an encrypted session key (%d)\n"), (int)slen); - return GPG_ERR_BAD_KEY; + return gpg_error (GPG_ERR_BAD_KEY); } - if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG (); - if (gcry_cipher_setkey ( hd, dek->key, dek->keylen )) - BUG (); - gcry_cipher_setiv ( hd, NULL, 0 ); - gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 ); - gcry_cipher_close ( hd ); - - /* Now we replace the dek components with the real session key to - decrypt the contents of the sequencing packet. */ - - dek->keylen=slen-1; - dek->algo=seskey[0]; + err = openpgp_cipher_open (&hd, dek->algo, ciphermode, GCRY_CIPHER_SECURE); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen); + if (err) + goto leave; - if(dek->keylen > DIM(dek->key)) - BUG (); + if (dek->use_aead) + { + byte ad[4]; + + ad[0] = (0xc0 | PKT_SYMKEY_ENC); + ad[1] = 5; + ad[2] = dek->algo; + ad[3] = dek->use_aead; + err = gcry_cipher_authenticate (hd, ad, 4); + if (err) + goto leave; + gcry_cipher_final (hd); + keylen = slen - noncelen - 16; + err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0); + if (err) + goto leave; + err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16); + if (err) + goto leave; + /* Now we replace the dek components with the real session key to + * decrypt the contents of the sequencing packet. */ + if (keylen > DIM(dek->key)) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + dek->keylen = keylen; + memcpy (dek->key, seskey + noncelen, dek->keylen); + } + else + { + gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 ); + /* Here we can only test whether the algo given in decrypted + * session key is a valid OpenPGP algo. With 11 defined + * symmetric algorithms we will miss 4.3% of wrong passphrases + * here. The actual checking is done later during bulk + * decryption; we can't bring this check forward easily. We + * need to use the GPG_ERR_CHECKSUM so that we won't run into + * the gnupg < 2.2 bug compatible case which would terminate the + * process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above) + * we will have a reliable test here. */ + if (openpgp_cipher_test_algo (seskey[0])) + { + err = gpg_error (GPG_ERR_CHECKSUM); + goto leave; + } - memcpy(dek->key, seskey + 1, dek->keylen); + /* Now we replace the dek components with the real session key to + * decrypt the contents of the sequencing packet. */ + keylen = slen-1; + if (keylen > DIM(dek->key)) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + dek->algo = seskey[0]; + dek->keylen = keylen; + memcpy (dek->key, seskey + 1, dek->keylen); + } /*log_hexdump( "thekey", dek->key, dek->keylen );*/ - return 0; + leave: + gcry_cipher_close (hd); + return err; } static void proc_symkey_enc (CTX c, PACKET *pkt) { + gpg_error_t err; PKT_symkey_enc *enc; enc = pkt->pkt.symkey_enc; @@ -294,19 +364,21 @@ proc_symkey_enc (CTX c, PACKET *pkt) { int algo = enc->cipher_algo; const char *s = openpgp_cipher_algo_name (algo); + const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo) + /**/ : "CFB"); if (!openpgp_cipher_test_algo (algo)) { if (!opt.quiet) { if (enc->seskeylen) - log_info (_("%s encrypted session key\n"), s ); + log_info (_("%s.%s encrypted session key\n"), s, a ); else - log_info (_("%s encrypted data\n"), s ); + log_info (_("%s.%s encrypted data\n"), s, a ); } } else - log_error (_("encrypted with unknown algorithm %d\n"), algo); + log_error (_("encrypted with unknown algorithm %d.%s\n"), algo, a); if (openpgp_md_test_algo (enc->s2k.hash_algo)) { @@ -334,6 +406,7 @@ proc_symkey_enc (CTX c, PACKET *pkt) if (c->dek) { c->dek->symmetric = 1; + c->dek->use_aead = enc->aead_algo; /* FIXME: This doesn't work perfectly if a symmetric key comes before a public key in the message - if the @@ -344,9 +417,24 @@ proc_symkey_enc (CTX c, PACKET *pkt) come later. */ if (enc->seskeylen) { - if (symkey_decrypt_seskey (c->dek, - enc->seskey, enc->seskeylen)) + err = symkey_decrypt_seskey (c->dek, + enc->seskey, enc->seskeylen); + if (err) { + log_info ("decryption of the symmetrically encrypted" + " session key failed: %s\n", + gpg_strerror (err)); + if (gpg_err_code (err) != GPG_ERR_BAD_KEY + && gpg_err_code (err) != GPG_ERR_CHECKSUM) + log_fatal ("process terminated to be bug compatible" + " with GnuPG <= 2.2\n"); + if (c->dek->s2k_cacheid[0]) + { + if (opt.debug) + log_debug ("cleared passphrase cached with ID:" + " %s\n", c->dek->s2k_cacheid); + passphrase_clear_cache (c->dek->s2k_cacheid); + } xfree (c->dek); c->dek = NULL; } @@ -650,6 +738,7 @@ proc_encrypted (CTX c, PACKET *pkt) else if (!result && !opt.ignore_mdc_error && !pkt->pkt.encrypted->mdc_method + && !pkt->pkt.encrypted->aead_algo && openpgp_cipher_get_algo_blklen (c->dek->algo) != 8 && c->dek->algo != CIPHER_ALGO_TWOFISH) { @@ -664,17 +753,25 @@ proc_encrypted (CTX c, PACKET *pkt) write_status (STATUS_DECRYPTION_FAILED); } else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE + && !pkt->pkt.encrypted->aead_algo && opt.ignore_mdc_error)) { + /* All is fine or for an MDC message the MDC failed but the + * --ignore-mdc-error option is active. For compatibility + * reasons we issue GOODMDC also for AEAD messages. */ write_status (STATUS_DECRYPTION_OKAY); if (opt.verbose > 1) log_info(_("decryption okay\n")); - if (pkt->pkt.encrypted->mdc_method && !result) + + if (pkt->pkt.encrypted->aead_algo) + write_status (STATUS_GOODMDC); + else if (pkt->pkt.encrypted->mdc_method && !result) write_status (STATUS_GOODMDC); else if (!opt.no_mdc_warn) log_info (_("WARNING: message was not integrity protected\n")); } - else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE) + else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE + || gpg_err_code (result) == GPG_ERR_TRUNCATED) { glo_ctrl.lasterr = result; log_error (_("WARNING: encrypted message has been manipulated!\n")); @@ -684,6 +781,7 @@ proc_encrypted (CTX c, PACKET *pkt) else { if ((gpg_err_code (result) == GPG_ERR_BAD_KEY + || gpg_err_code (result) == GPG_ERR_CHECKSUM || gpg_err_code (result) == GPG_ERR_CIPHER_ALGO) && *c->dek->s2k_cacheid != '\0') { @@ -1393,7 +1491,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: - case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; default: newpkt = 0; break; } @@ -1409,6 +1508,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) case PKT_PUBKEY_ENC: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: write_status_text( STATUS_UNEXPECTED, "0" ); rc = GPG_ERR_UNEXPECTED; goto leave; @@ -1436,7 +1536,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_ENCRYPTED: - case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; @@ -1463,7 +1564,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break; case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break; case PKT_ENCRYPTED: - case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break; + case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; case PKT_PLAINTEXT: proc_plaintext (c, pkt); break; case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break; case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break; diff --git a/g10/misc.c b/g10/misc.c index 77c8f269c..8c54793ed 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -70,6 +70,11 @@ #include "../common/zb32.h" +/* FIXME: Libgcrypt 1.9 will support EAX. Until we kame this a + * requirement we hardwire the enum used for EAX. */ +#define MY_GCRY_CIPHER_MODE_EAX 14 + + #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ @@ -396,7 +401,7 @@ print_further_info (const char *format, ...) log_info (_("(further info: ")); va_start (arg_ptr, format); - log_logv (GPGRT_LOG_CONT, format, arg_ptr); + log_logv (GPGRT_LOGLVL_CONT, format, arg_ptr); va_end (arg_ptr); log_printf (")\n"); } @@ -582,6 +587,80 @@ openpgp_cipher_algo_name (cipher_algo_t algo) } +/* Return 0 if ALGO is supported. Return an error if not. */ +gpg_error_t +openpgp_aead_test_algo (aead_algo_t algo) +{ + /* FIXME: We currently have no easy way to test whether libgcrypt + * implements a mode. The only way we can do this is to open a + * cipher context with that mode and close it immediately. That is + * a bit costly. So we look at the libgcrypt version and assume + * nothing has been patched out. */ + switch (algo) + { + case AEAD_ALGO_NONE: + break; + + case AEAD_ALGO_EAX: +#if GCRYPT_VERSION_NUMBER < 0x010900 + break; +#else + return 0; +#endif + + case AEAD_ALGO_OCB: + return 0; + } + + return gpg_error (GPG_ERR_INV_CIPHER_MODE); +} + + +/* Map the OpenPGP AEAD algorithm with ID ALGO to a string + * representation of the algorithm name. For unknown algorithm IDs + * this function returns "?". */ +const char * +openpgp_aead_algo_name (aead_algo_t algo) +{ + switch (algo) + { + case AEAD_ALGO_NONE: break; + case AEAD_ALGO_EAX: return "EAX"; + case AEAD_ALGO_OCB: return "OCB"; + } + + return "?"; +} + + +/* Return information for the AEAD algorithm ALGO. The corresponding + * Libgcrypt ciphermode is stored at R_MODE and the required number of + * octets for the nonce at R_NONCELEN. On error and error code is + * returned. Note that the taglen is always 128 bits. */ +gpg_error_t +openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, + unsigned int *r_noncelen) +{ + switch (algo) + { + case AEAD_ALGO_OCB: + *r_mode = GCRY_CIPHER_MODE_OCB; + *r_noncelen = 15; + break; + + case AEAD_ALGO_EAX: + *r_mode = MY_GCRY_CIPHER_MODE_EAX; + *r_noncelen = 16; + break; + + default: + log_error ("unsupported AEAD algo %d\n", algo); + return gpg_error (GPG_ERR_INV_CIPHER_MODE); + } + return 0; +} + + /* Return 0 if ALGO is a supported OpenPGP public key algorithm. */ int openpgp_pk_test_algo (pubkey_algo_t algo) @@ -1112,6 +1191,39 @@ string_to_cipher_algo (const char *string) return val; } + +/* + * Map an AEAD mode string to a an AEAD algorithm number as defined by + * rrc4880bis. Also support the "An" syntax as used by the preference + * strings. + */ +aead_algo_t +string_to_aead_algo (const char *string) +{ + int result; + + if (!string) + result = 0; + if (!ascii_strcasecmp (string, "EAX")) + result = 1; + else if (!ascii_strcasecmp (string, "OCB")) + result = 2; + else if ((string[0]=='A' || string[0]=='a')) + { + char *endptr; + + string++; + result = strtol (string, &endptr, 10); + if (!*string || *endptr || result < 1 || result > 2) + result = 0; + } + else + result = 0; + + return result; +} + + /* * Wrapper around gcry_md_map_name to provide a fallback using the * "Hn" syntax as used by the preference strings. @@ -1228,6 +1340,18 @@ default_cipher_algo(void) return opt.s2k_cipher_algo; } + +aead_algo_t +default_aead_algo(void) +{ + if(opt.def_aead_algo) + return opt.def_aead_algo; + else if(opt.personal_aead_prefs) + return opt.personal_aead_prefs[0].value; + else + return DEFAULT_AEAD_ALGO; +} + /* There is no default_digest_algo function, but see sign.c:hash_for() */ diff --git a/g10/openfile.c b/g10/openfile.c index 78f4dbbcb..f4730da22 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -171,32 +171,34 @@ ask_outfile_name( const char *name, size_t namelen ) * 2 = use ".sig" * 3 = use ".rev" * - * If INP_FD is not -1 the function simply creates an IOBUF for that - * file descriptor and ignore INAME and MODE. Note that INP_FD won't - * be closed if the returned IOBUF is closed. With RESTRICTEDPERM a - * file will be created with mode 700 if possible. - */ + * With RESTRICTEDPERM a file will be created with mode 700 if + * possible. + * + * If OUT_FD is not -1 the function simply creates an IOBUF for that + * file descriptor and ignores INAME and MODE. Note that OUT_FD won't + * be closed if the returned IOBUF is closed. This is used for gpg's + * --server mode. */ int -open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, +open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a) { int rc = 0; *a = NULL; - if (inp_fd != -1) + if (out_fd != -1) { char xname[64]; - *a = iobuf_fdopen_nc (inp_fd, "wb"); + *a = iobuf_fdopen_nc (out_fd, "wb"); if (!*a) { rc = gpg_error_from_syserror (); - snprintf (xname, sizeof xname, "[fd %d]", inp_fd); + snprintf (xname, sizeof xname, "[fd %d]", out_fd); log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (rc)); } else if (opt.verbose) { - snprintf (xname, sizeof xname, "[fd %d]", inp_fd); + snprintf (xname, sizeof xname, "[fd %d]", out_fd); log_info (_("writing to '%s'\n"), xname); } } diff --git a/g10/options.h b/g10/options.h index 130bec84c..471aee7f4 100644 --- a/g10/options.h +++ b/g10/options.h @@ -62,6 +62,9 @@ struct * progress info and to decide on how to allocate buffers. */ uint64_t input_size_hint; + /* The AEAD chunk size expressed as a power of 2. */ + int chunk_size; + int dry_run; int autostart; int list_only; @@ -82,6 +85,7 @@ struct int with_fingerprint; /* Option --with-fingerprint active. */ int with_subkey_fingerprint; /* Option --with-subkey-fingerprint active. */ int with_keygrip; /* Option --with-keygrip active. */ + int with_key_screening;/* Option --with-key-screening active. */ int with_tofu_info; /* Option --with-tofu_info active. */ int with_secret; /* Option --with-secret active. */ int with_wkd_hash; /* Option --with-wkd-hash. */ @@ -91,8 +95,10 @@ struct int no_armor; int list_packets; /* Option --list-packets active. */ int def_cipher_algo; + int def_aead_algo; int force_mdc; int disable_mdc; + int force_aead; int def_digest_algo; int cert_digest_algo; int compress_algo; @@ -176,6 +182,7 @@ struct const char *def_preference_list; const char *def_keyserver_url; prefitem_t *personal_cipher_prefs; + prefitem_t *personal_aead_prefs; prefitem_t *personal_digest_prefs; prefitem_t *personal_compress_prefs; struct weakhash *weak_digests; @@ -315,7 +322,6 @@ struct { #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) -#define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) diff --git a/g10/packet.h b/g10/packet.h index 8dca88b75..4f4569f66 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -72,7 +72,8 @@ typedef enum { PREFTYPE_NONE = 0, PREFTYPE_SYM = 1, PREFTYPE_HASH = 2, - PREFTYPE_ZIP = 3 + PREFTYPE_ZIP = 3, + PREFTYPE_AEAD = 4 } preftype_t; typedef struct { @@ -93,12 +94,14 @@ typedef struct /* A symmetric-key encrypted session key packet as defined in RFC 4880, Section 5.3. All fields are serialized. */ typedef struct { - /* RFC 4880: this must be 4. */ + /* We support version 4 (rfc4880) and 5 (rfc4880bis). */ byte version; - /* The cipher algorithm used to encrypt the session key. (This may - be different from the algorithm that is used to encrypt the SED - packet.) */ + /* The cipher algorithm used to encrypt the session key. Note that + * this may be different from the algorithm that is used to encrypt + * bulk data. */ byte cipher_algo; + /* The AEAD algorithm or 0 for CFB encryption. */ + byte aead_algo; /* The string-to-key specifier. */ STRING2KEY s2k; /* The length of SESKEY in bytes or 0 if this packet does not @@ -106,7 +109,8 @@ typedef struct { S2K function on the password is the session key. See RFC 4880, Section 5.3.) */ byte seskeylen; - /* The session key as encrypted by the S2K specifier. */ + /* The session key as encrypted by the S2K specifier. For AEAD this + * includes the nonce and the authentication tag. */ byte seskey[1]; } PKT_symkey_enc; @@ -290,6 +294,7 @@ typedef struct struct { unsigned int mdc:1; + unsigned int aead:1; unsigned int ks_modify:1; unsigned int compacted:1; unsigned int primary:2; /* 2 if set via the primary flag, 1 if calculated */ @@ -386,6 +391,7 @@ typedef struct struct { unsigned int mdc:1; /* MDC feature set. */ + unsigned int aead:1; /* AEAD feature set. */ unsigned int disabled_valid:1;/* The next flag is valid. */ unsigned int disabled:1; /* The key has been disabled. */ unsigned int primary:1; /* This is a primary key. */ @@ -456,12 +462,13 @@ typedef struct { typedef struct { /* Remaining length of encrypted data. */ u32 len; - /* When encrypting, the first block size bytes of data are random - data and the following 2 bytes are copies of the last two bytes - of the random data (RFC 4880, Section 5.7). This provides a - simple check that the key is correct. extralen is the size of - this extra data. This is used by build_packet when writing out - the packet's header. */ + /* When encrypting in CFB mode, the first block size bytes of data + * are random data and the following 2 bytes are copies of the last + * two bytes of the random data (RFC 4880, Section 5.7). This + * provides a simple check that the key is correct. EXTRALEN is the + * size of this extra data or, in AEAD mode, the length of the + * headers and the tags. This is used by build_packet when writing + * out the packet's header. */ int extralen; /* Whether the serialized version of the packet used / should use the new format. */ @@ -471,8 +478,17 @@ typedef struct { Note: this is ignored when encrypting. */ byte is_partial; /* If 0, MDC is disabled. Otherwise, the MDC method that was used - (currently, only DIGEST_ALGO_SHA1 is supported). */ + (only DIGEST_ALGO_SHA1 has ever been defined). */ byte mdc_method; + /* If 0, AEAD is not used. Otherwise, the used AEAD algorithm. + * MDC_METHOD (above) shall be zero if AEAD is used. */ + byte aead_algo; + /* The cipher algo for/from the AEAD packet. 0 for other encryption + * packets. */ + byte cipher_algo; + /* The chunk byte from the AEAD packet. */ + byte chunkbyte; + /* An iobuf holding the data to be decrypted. (This is not used for encryption!) */ iobuf_t buf; diff --git a/g10/parse-packet.c b/g10/parse-packet.c index eee14f64e..e3ff4321e 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,7 +1,6 @@ /* parse-packet.c - read packets - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2009, 2010 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc. + * Copyright (C) 2014, 2018 Werner Koch * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. @@ -18,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+ */ #include <config.h> @@ -82,6 +82,9 @@ static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb, int partial); +static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype, + unsigned long pktlen, PACKET *packet, + int partial); static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet, int new_ctb); static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen, @@ -636,6 +639,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, case PKT_PLAINTEXT: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: case PKT_COMPRESSED: iobuf_set_partial_body_length_mode (inp, c & 0xff); pktlen = 0; /* To indicate partial length. */ @@ -823,6 +827,9 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, case PKT_MDC: rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb); break; + case PKT_ENCRYPTED_AEAD: + rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial); + break; case PKT_GPG_CONTROL: rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial); break; @@ -1098,19 +1105,17 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, { PKT_symkey_enc *k; int rc = 0; - int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen; + int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen; if (pktlen < 4) - { - log_error ("packet(%d) too short\n", pkttype); - if (list_mode) - es_fprintf (listfp, ":symkey enc packet: [too short]\n"); - rc = gpg_error (GPG_ERR_INV_PACKET); - goto leave; - } + goto too_short; version = iobuf_get_noeof (inp); pktlen--; - if (version != 4) + if (version == 4) + ; + else if (version == 5) + ; + else { log_error ("packet(%d) with unknown version %d\n", pkttype, version); if (list_mode) @@ -1128,6 +1133,15 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, } cipher_algo = iobuf_get_noeof (inp); pktlen--; + if (version == 5) + { + aead_algo = iobuf_get_noeof (inp); + pktlen--; + } + else + aead_algo = 0; + if (pktlen < 2) + goto too_short; s2kmode = iobuf_get_noeof (inp); pktlen--; hash_algo = iobuf_get_noeof (inp); @@ -1162,6 +1176,7 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, + seskeylen - 1); k->version = version; k->cipher_algo = cipher_algo; + k->aead_algo = aead_algo; k->s2k.mode = s2kmode; k->s2k.hash_algo = hash_algo; if (s2kmode == 1 || s2kmode == 3) @@ -1192,10 +1207,20 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, if (list_mode) { es_fprintf (listfp, - ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d", - version, cipher_algo, s2kmode, hash_algo); + ":symkey enc packet: version %d, cipher %d, aead %d," + " s2k %d, hash %d", + version, cipher_algo, aead_algo, s2kmode, hash_algo); if (seskeylen) - es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8); + { + /* To compute the size of the session key we need to know + * the size of the AEAD nonce which we may not know. Thus + * we show only the seize of the entire encrypted session + * key. */ + if (aead_algo) + es_fprintf (listfp, ", encrypted seskey %d bytes", seskeylen); + else + es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8); + } es_fprintf (listfp, "\n"); if (s2kmode == 1 || s2kmode == 3) { @@ -1212,6 +1237,13 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen, leave: iobuf_skip_rest (inp, pktlen, 0); return rc; + + too_short: + log_error ("packet(%d) too short\n", pkttype); + if (list_mode) + es_fprintf (listfp, ":symkey enc packet: [too short]\n"); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; } @@ -1392,6 +1424,11 @@ dump_sig_subpkt (int hashed, int type, int critical, for (i = 0; i < length; i++) es_fprintf (listfp, " %d", buffer[i]); break; + case SIGSUBPKT_PREF_AEAD: + es_fputs ("pref-aead-algos:", listfp); + for (i = 0; i < length; i++) + es_fprintf (listfp, " %d", buffer[i]); + break; case SIGSUBPKT_REV_KEY: es_fputs ("revocation key: ", listfp); if (length < 22) @@ -1554,6 +1591,7 @@ parse_one_sig_subpkt (const byte * buffer, size_t n, int type) case SIGSUBPKT_KEY_FLAGS: case SIGSUBPKT_KS_FLAGS: case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_AEAD: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_POLICY: @@ -1636,6 +1674,7 @@ can_handle_critical (const byte * buffer, size_t n, int type) case SIGSUBPKT_ISSUER: /* issuer key ID */ case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */ case SIGSUBPKT_PREF_SYM: + case SIGSUBPKT_PREF_AEAD: case SIGSUBPKT_PREF_HASH: case SIGSUBPKT_PREF_COMPR: case SIGSUBPKT_KEY_FLAGS: @@ -3161,6 +3200,9 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, ed->buf = NULL; ed->new_ctb = new_ctb; ed->is_partial = partial; + ed->aead_algo = 0; + ed->cipher_algo = 0; /* Only used with AEAD. */ + ed->chunkbyte = 0; /* Only used with AEAD. */ if (pkttype == PKT_ENCRYPTED_MDC) { /* Fixme: add some pktlen sanity checks. */ @@ -3252,6 +3294,81 @@ parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen, } +static gpg_error_t +parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen, + PACKET *pkt, int partial) +{ + int rc = 0; + PKT_encrypted *ed; + unsigned long orig_pktlen = pktlen; + int version; + + ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted); + if (!ed) + return gpg_error_from_syserror (); + ed->len = 0; + ed->extralen = 0; /* (only used in build_packet.) */ + ed->buf = NULL; + ed->new_ctb = 1; /* (packet number requires a new CTB anyway.) */ + ed->is_partial = partial; + ed->mdc_method = 0; + /* A basic sanity check. We need one version byte, one algo byte, + * one aead algo byte, one chunkbyte, at least 15 byte IV. */ + if (orig_pktlen && pktlen < 19) + { + log_error ("packet(%d) too short\n", pkttype); + if (list_mode) + es_fputs (":aead encrypted packet: [too short]\n", listfp); + rc = gpg_error (GPG_ERR_INV_PACKET); + iobuf_skip_rest (inp, pktlen, partial); + goto leave; + } + + version = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + if (version != 1) + { + log_error ("aead encrypted packet with unknown version %d\n", + version); + if (list_mode) + es_fputs (":aead encrypted packet: [unknown version]\n", listfp); + /*skip_rest(inp, pktlen); should we really do this? */ + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + ed->cipher_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->aead_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->chunkbyte = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + + /* Store the remaining length of the encrypted data. We read the + * rest during decryption. */ + ed->len = pktlen; + + if (list_mode) + { + es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n", + ed->cipher_algo, ed->aead_algo, ed->chunkbyte); + if (orig_pktlen) + es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen); + else + es_fprintf (listfp, "\tlength: unknown\n"); + } + + ed->buf = inp; + + leave: + return rc; +} + + /* * This packet is internally generated by us (in armor.c) to transfer * some information to the lower layer. To make sure that this packet diff --git a/g10/pkclist.c b/g10/pkclist.c index 581cae407..6ec5537f5 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -1468,9 +1468,12 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, support. All this doesn't mean IDEA is actually available, of course. */ implicit=CIPHER_ALGO_3DES; - break; + case PREFTYPE_AEAD: + /* No implicit algo. */ + break; + case PREFTYPE_HASH: /* While I am including this code for completeness, note that currently --pgp2 mode locks the hash at MD5, so this @@ -1553,6 +1556,8 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, prefs=NULL; if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs) prefs=opt.personal_cipher_prefs; + else if(preftype==PREFTYPE_AEAD && opt.personal_aead_prefs) + prefs=opt.personal_aead_prefs; else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs) prefs=opt.personal_digest_prefs; else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs) @@ -1646,6 +1651,32 @@ select_mdc_from_pklist (PK_LIST pk_list) } +/* Select the AEAD flag from the pk_list. We can only use AEAD if all + * recipients support this feature. Returns the AEAD to be used or 0 + * if AEAD shall not be used. */ +aead_algo_t +select_aead_from_pklist (PK_LIST pk_list) +{ + pk_list_t pkr; + int aead; + + if (!pk_list) + return 0; + + for (pkr = pk_list; pkr; pkr = pkr->next) + { + if (pkr->pk->user_id) /* selected by user ID */ + aead = pkr->pk->user_id->flags.aead; + else + aead = pkr->pk->flags.aead; + if (!aead) + return 0; /* At least one recipient does not support it. */ + } + + return default_aead_algo (); /* Yes, AEAD can be used. */ +} + + /* Print a warning for all keys in PK_LIST missing the MDC feature. */ void warn_missing_mdc_from_pklist (PK_LIST pk_list) diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c index d7ba95391..0185097a4 100644 --- a/g10/pubkey-enc.c +++ b/g10/pubkey-enc.c @@ -255,7 +255,7 @@ get_it (ctrl_t ctrl, * CSUM */ if (DBG_CRYPTO) - log_printhex ("DEK frame:", frame, nframe); + log_printhex (frame, nframe, "DEK frame:"); n = 0; if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) @@ -361,7 +361,7 @@ get_it (ctrl_t ctrl, if (DBG_CLOCK) log_clock ("decryption ready"); if (DBG_CRYPTO) - log_printhex ("DEK is:", dek->key, dek->keylen); + log_printhex (dek->key, dek->keylen, "DEK is:"); /* Check that the algo is in the preferences and whether it has * expired. Also print a status line with the key's fingerprint. */ diff --git a/g10/sig-check.c b/g10/sig-check.c index 23af12b2e..f8e366b7e 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -233,7 +233,7 @@ check_signature2 (ctrl_t ctrl, unsigned char *p, *buffer; size_t n, nbytes; int i; - char hashbuf[20]; + char hashbuf[20]; /* We use SHA-1 here. */ nbytes = 6; for (i=0; i < nsig; i++ ) @@ -510,7 +510,11 @@ check_signature_end_simple (PKT_public_key *pk, PKT_signature *sig, return GPG_ERR_GENERAL; /* Verify the signature. */ + if (DBG_CLOCK && sig->sig_class <= 0x01) + log_clock ("enter pk_verify"); rc = pk_verify( pk->pubkey_algo, result, sig->data, pk->pkey ); + if (DBG_CLOCK && sig->sig_class <= 0x01) + log_clock ("leave pk_verify"); gcry_mpi_release (result); if( !rc && sig->flags.unknown_critical ) diff --git a/g10/sign.c b/g10/sign.c index 4cf0cd39a..df71ccce1 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1326,9 +1326,6 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) s2k->hash_algo = S2K_DIGEST_ALGO; algo = default_cipher_algo(); - if (!opt.quiet || !opt.batch) - log_info (_("%s encryption will be used\n"), - openpgp_cipher_algo_name (algo) ); cfx.dek = passphrase_to_dek (algo, s2k, 1, 1, NULL, &canceled); if (!cfx.dek || !cfx.dek->keylen) { @@ -1337,7 +1334,15 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) goto leave; } - cfx.dek->use_mdc = use_mdc (NULL, cfx.dek->algo); + cfx.dek->use_aead = use_aead (NULL, cfx.dek->algo); + if (!cfx.dek->use_aead) + cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); + + if (!opt.quiet || !opt.batch) + log_info (_("%s.%s encryption will be used\n"), + openpgp_cipher_algo_name (algo), + cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) + /**/ : "CFB"); /* now create the outfile */ rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out); @@ -1376,12 +1381,15 @@ sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) } /* Push the encryption filter */ - iobuf_push_filter( out, cipher_filter, &cfx ); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &cfx); /* Push the compress filter */ if (default_compress_algo()) { - if (cfx.dek && cfx.dek->use_mdc) + if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx,default_compress_algo() ); } diff --git a/g10/tdbdump.c b/g10/tdbdump.c index 5ea903f45..37bf78b80 100644 --- a/g10/tdbdump.c +++ b/g10/tdbdump.c @@ -129,7 +129,7 @@ import_ownertrust (ctrl_t ctrl, const char *fname ) char *p; size_t n, fprlen; unsigned int otrust; - byte fpr[20]; + byte fpr[MAX_FINGERPRINT_LEN]; int any = 0; int rc; @@ -171,7 +171,7 @@ import_ownertrust (ctrl_t ctrl, const char *fname ) continue; } fprlen = p - line; - if( fprlen != 32 && fprlen != 40 ) { + if( fprlen != 32 && fprlen != 40 && fprlen != 64) { log_error (_("error in '%s': %s\n"), fname, _("invalid fingerprint") ); continue; @@ -183,10 +183,12 @@ import_ownertrust (ctrl_t ctrl, const char *fname ) } if( !otrust ) continue; /* no otrust defined - no need to update or insert */ - /* convert the ascii fingerprint to binary */ - for(p=line, fprlen=0; fprlen < 20 && *p != ':'; p += 2 ) - fpr[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]); - while (fprlen < 20) + /* Convert the ascii fingerprint to binary */ + for(p=line, fprlen=0; + fprlen < MAX_FINGERPRINT_LEN && *p != ':'; + p += 2 ) + fpr[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]); + while (fprlen < MAX_FINGERPRINT_LEN) fpr[fprlen++] = 0; rc = tdbio_search_trust_byfpr (fpr, &rec); diff --git a/g10/tofu.c b/g10/tofu.c index 091d5b0d0..762b19b7a 100644 --- a/g10/tofu.c +++ b/g10/tofu.c @@ -2083,13 +2083,16 @@ build_conflict_set (ctrl_t ctrl, tofu_dbs_t dbs, * policy to ask due to a conflict. */ for (iter = conflict_set; iter; iter = iter->next) { + /* Fixme: Why the check against N+1? */ int l = strlen (iter->d); - if (!(l == 2 * MAX_FINGERPRINT_LEN - || l == 2 * MAX_FINGERPRINT_LEN + 1)) + if (!(l == 2 * 20 + || l == 2 * 20 + 1 + || l == 2 * 32 + || l == 2 * 32 + 1)) { log_error (_("TOFU db corruption detected.\n")); - print_further_info ("fingerprint '%s' is not %d characters long", - iter->d, 2 * MAX_FINGERPRINT_LEN); + print_further_info ("fingerprint '%s' is %d characters long", + iter->d, l); } if (l >= 1 && iter->d[l - 1] == '!') @@ -2469,10 +2472,11 @@ get_policy (ctrl_t ctrl, tofu_dbs_t dbs, PKT_public_key *pk, /* See if the key is signed by an ultimately trusted key. */ { int fingerprint_raw_len = strlen (fingerprint) / 2; - char fingerprint_raw[20]; + char fingerprint_raw[MAX_FINGERPRINT_LEN]; int len = 0; - if (fingerprint_raw_len != sizeof fingerprint_raw + /* FIXME(fingerprint) */ + if (fingerprint_raw_len != 20 /*sizeof fingerprint_raw */ || ((len = hex2bin (fingerprint, fingerprint_raw, fingerprint_raw_len)) != strlen (fingerprint))) @@ -3210,7 +3214,7 @@ show_statistics (tofu_dbs_t dbs, *p = ' '; } - log_string (GPGRT_LOG_INFO, msg); + log_string (GPGRT_LOGLVL_INFO, msg); xfree (msg); if (policy == TOFU_POLICY_AUTO) @@ -3275,7 +3279,7 @@ show_warning (const char *fingerprint, strlist_t user_id_list) log_fatal ("format failed: %s\n", gpg_strerror (gpg_error_from_syserror())); xfree (tmpmsg); - log_string (GPGRT_LOG_INFO, text); + log_string (GPGRT_LOGLVL_INFO, text); xfree (text); es_free (set_policy_command); |