aboutsummaryrefslogtreecommitdiffstats
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/Makefile.am1
-rw-r--r--g10/build-packet.c67
-rw-r--r--g10/call-agent.c10
-rw-r--r--g10/card-util.c41
-rw-r--r--g10/cipher-aead.c457
-rw-r--r--g10/cipher.c7
-rw-r--r--g10/decrypt-data.c758
-rw-r--r--g10/dek.h25
-rw-r--r--g10/ecdh.c18
-rw-r--r--g10/encrypt.c342
-rw-r--r--g10/filter.h63
-rw-r--r--g10/getkey.c40
-rw-r--r--g10/gpg.c114
-rw-r--r--g10/gpg.h13
-rw-r--r--g10/gpgcompose.c12
-rw-r--r--g10/import.c19
-rw-r--r--g10/kbnode.c5
-rw-r--r--g10/keydb.h1
-rw-r--r--g10/keyedit.c32
-rw-r--r--g10/keygen.c131
-rw-r--r--g10/keyid.c50
-rw-r--r--g10/keylist.c43
-rw-r--r--g10/main.h22
-rw-r--r--g10/mainproc.c149
-rw-r--r--g10/misc.c126
-rw-r--r--g10/options.h8
-rw-r--r--g10/packet.h42
-rw-r--r--g10/parse-packet.c130
-rw-r--r--g10/pkclist.c33
-rw-r--r--g10/pubkey-enc.c4
-rw-r--r--g10/sig-check.c6
-rw-r--r--g10/sign.c20
-rw-r--r--g10/tdbdump.c14
-rw-r--r--g10/tofu.c20
34 files changed, 2437 insertions, 386 deletions
diff --git a/g10/Makefile.am b/g10/Makefile.am
index cc4ef5cb6..cba65b22a 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -132,6 +132,7 @@ gpg_sources = server.c \
decrypt.c \
decrypt-data.c \
cipher.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 545b2448a..61d06c663 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)
@@ -1474,7 +1474,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)
@@ -1854,10 +1854,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 a396b7df4..854b94f47 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -212,6 +212,7 @@ get_manufacturer (unsigned int no)
case 0x000A: return "Dangerous Things";
case 0x002A: return "Magrathea";
+ case 0x0042: return "GnuPG e.V.";
case 0x1337: return "Warsaw Hackerspace";
case 0x2342: return "warpzone"; /* hackerspace Muenster. */
@@ -530,9 +531,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])
@@ -1087,7 +1088,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);
@@ -1108,7 +1109,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;
@@ -1121,7 +1122,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)
@@ -1129,7 +1131,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++;
@@ -1143,8 +1145,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;
@@ -1152,7 +1155,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);
@@ -1314,12 +1317,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")
+ );
}
@@ -1379,8 +1381,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;
}
@@ -1885,7 +1891,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..573bb43fb
--- /dev/null
+++ b/g10/cipher-aead.c
@@ -0,0 +1,457 @@
+/* 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 additional data for the current chunk. If FINAL is set the
+ * final AEAD chunk is processed. */
+static gpg_error_t
+set_additional_data (cipher_filter_context_t *cfx, int final)
+{
+ unsigned char ad[21];
+
+ 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);
+}
+
+
+/* Set the nonce. This also reset the encryption machinery so that
+ * the handle can be used for a new chunk. */
+static gpg_error_t
+set_nonce (cipher_filter_context_t *cfx)
+{
+ unsigned char nonce[16];
+ 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:");
+ return gcry_cipher_setiv (cfx->cipher_hd, nonce, i);
+}
+
+
+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;
+
+ err = set_nonce (cfx);
+ if (err)
+ return err;
+
+ err = set_additional_data (cfx, 0);
+ 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:
+ 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];
+
+ cfx->chunkindex++;
+
+ err = set_nonce (cfx);
+ if (err)
+ goto leave;
+ err = set_additional_data (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 newchunk = 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);
+ newchunk = 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 || newchunk)
+ {
+ if (DBG_FILTER)
+ log_debug ("encrypting: buflen=%zu %s n=%zu\n",
+ cfx->buflen, newchunk?"(newchunk)":"", n);
+ if (newchunk)
+ gcry_cipher_final (cfx->cipher_hd);
+ if (!DBG_FILTER)
+ ;
+ else if (newchunk)
+ log_printhex (cfx->buffer, cfx->buflen, "plain(1):");
+ else if (cfx->buflen > 32)
+ log_printhex (cfx->buffer + cfx->buflen - 32, 32,
+ "plain(last 32):");
+
+ /* 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. */
+ gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen,
+ NULL, 0);
+ if (newchunk && 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 (newchunk)
+ {
+ if (DBG_FILTER)
+ log_debug ("chunklen=%ju total=%ju\n",
+ (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+ err = write_auth_tag (cfx, a);
+ if (err)
+ {
+ log_error ("gcry_cipher_gettag failed: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ if (DBG_FILTER)
+ log_debug ("starting a new chunk (cur size=%zu)\n", size);
+ cfx->chunkindex++;
+ cfx->chunklen = 0;
+ err = set_nonce (cfx);
+ if (err)
+ goto leave;
+ err = set_additional_data (cfx, 0);
+ if (err)
+ goto leave;
+ newchunk = 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);
+
+ /* FIXME: Check what happens if we just wrote the last chunk and no
+ * more bytes were to encrypt. We should then not call finalize and
+ * write the auth tag again, right? May this at all happen? */
+
+ /* Call finalize which will also allow us to flush out and encrypt
+ * the last arbitrary length buffer. */
+ gcry_cipher_final (cfx->cipher_hd);
+
+ /* Encrypt any remaining bytes. */
+ if (cfx->buflen)
+ {
+ if (DBG_FILTER)
+ log_debug ("processing last %zu bytes of the last chunk\n",
+ cfx->buflen);
+ gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, NULL, 0);
+ 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;
+ }
+ else /* Dummy encryption. */
+ gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, 0, NULL, 0);
+
+ /* Get and write the authentication tag. */
+ if (DBG_FILTER)
+ log_debug ("chunklen=%ju total=%ju\n",
+ (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+ err = write_auth_tag (cfx, a);
+ if (err)
+ goto leave;
+
+ /* 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.c
index b950d0c3f..ad7399dec 100644
--- a/g10/cipher.c
+++ b/g10/cipher.c
@@ -1,4 +1,4 @@
-/* cipher.c - En-/De-ciphering filter
+/* cipher.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..afdedcbf6 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,80 @@ release_dfx_context (decode_filter_ctx_t dfx)
}
+/* Set the nonce for AEAD. This also reset the decryption machinery
+ * so that the handle can be used for a new chunk. */
+static gpg_error_t
+aead_set_nonce (decode_filter_ctx_t dfx)
+{
+ 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:");
+ return gcry_cipher_setiv (dfx->cipher_hd, nonce, i);
+}
+
+
+/* Set the additional data for the current chunk. If FINAL is set the
+ * final AEAD chunk is processed. */
+static gpg_error_t
+aead_set_ad (decode_filter_ctx_t dfx, int final)
+{
+ unsigned char ad[21];
+
+ 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);
+}
+
/****************
* Decrypt the data, specified by ED with the key DEK.
@@ -80,8 +202,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 +213,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 +233,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 +262,196 @@ 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;
+ }
+
+ rc = aead_set_nonce (dfx);
+ if (rc)
+ goto leave;
+
+ rc = aead_set_ad (dfx, 0);
+ if (rc)
+ 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");
+ }
+
+ 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();
+ }
- if ( dfx->mdc_hash )
- gcry_md_write (dfx->mdc_hash, temp, nprefix+2);
+
+ /* 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 +511,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 +531,321 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek)
}
+/* 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. */
+ int c;
+
+ 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\n",
+ size, len, dfx->eof_seen? " eof":"");
+
+ /* Read and fill up BUF. We need to watchout 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 read at least
+ * one further byte; however we try to ready 16 extra bytes to avoid
+ * singel 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). */
+ if (dfx->partial)
+ {
+ for (; len < size; len++ )
+ {
+ if ((c = iobuf_get (a)) == -1)
+ {
+ dfx->eof_seen = 1; /* Normal EOF. */
+ break;
+ }
+ buf[len] = c;
+ }
+ }
+ else
+ {
+ for (; len < size && dfx->length; len++, dfx->length--)
+ {
+ c = iobuf_get (a);
+ if (c == -1)
+ {
+ dfx->eof_seen = 3; /* Premature EOF. */
+ break;
+ }
+ buf[len] = c;
+ }
+ if (!dfx->length)
+ dfx->eof_seen = 1; /* Normal EOF. */
+ }
+
+ 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 requires a loop because a chunk may end
+ * within the buffer. */
+ if (DBG_FILTER)
+ log_debug ("decrypt loop: 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);
+ /* 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 ("bytes left: %zu at off=%zu\n", 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. The 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);
+
+ len = dfx->holdbacklen;
+ if (dfx->partial)
+ {
+ for (; len < 48; len++ )
+ {
+ if ((c = iobuf_get (a)) == -1)
+ {
+ dfx->eof_seen = 1; /* Normal EOF. */
+ break;
+ }
+ dfx->holdback[len] = c;
+ }
+ }
+ else
+ {
+ for (; len < 48 && dfx->length; len++, dfx->length--)
+ {
+ c = iobuf_get (a);
+ if (c == -1)
+ {
+ dfx->eof_seen = 3; /* Premature EOF. */
+ break;
+ }
+ dfx->holdback[len] = c;
+ }
+ if (!dfx->length)
+ dfx->eof_seen = 1; /* Normal EOF. */
+ }
+ if (len < 32)
+ {
+ /* Not enough data for the last two tags. */
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave;
+ }
+ dfx->holdbacklen = len;
+ /* log_printhex (dfx->holdback, dfx->holdbacklen, "holdback:"); */
+ len = 0;
+ }
+ 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;
+ }
+ if (DBG_CRYPTO)
+ log_printhex (tagbuf, 16, "tag:");
+ err = gcry_cipher_checktag (dfx->cipher_hd, tagbuf, 16);
+ if (err)
+ {
+ if (DBG_FILTER)
+ log_debug ("gcry_cipher_checktag failed (1): %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Prepare a new chunk. */
+ dfx->chunklen = 0;
+ dfx->chunkindex++;
+ err = aead_set_nonce (dfx);
+ if (err)
+ goto leave;
+ err = aead_set_ad (dfx, 0);
+ if (err)
+ goto leave;
+
+ continue;
+ }
+
+ 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 (dfx->eof_seen)
+ {
+ if (DBG_FILTER)
+ log_debug ("eof seen: holdback buffer has the tags.\n");
+
+ log_assert (dfx->holdbacklen >= 32);
+
+ if (DBG_FILTER)
+ log_printhex (dfx->holdback, 16, "tag:");
+ err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback, 16);
+ if (err)
+ {
+ log_error ("gcry_cipher_checktag failed (2): %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Check the final chunk. */
+ dfx->chunkindex++;
+ err = aead_set_nonce (dfx);
+ if (err)
+ goto leave;
+ err = aead_set_ad (dfx, 1);
+ if (err)
+ goto leave;
+ gcry_cipher_final (dfx->cipher_hd);
+ /* decrypt an empty string. */
+ 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;
+ }
+ /* log_printhex (dfx->holdback+16, 16, "tag:"); */
+ err = gcry_cipher_checktag (dfx->cipher_hd, dfx->holdback+16, 16);
+ if (err)
+ {
+ if (DBG_FILTER)
+ log_debug ("gcry_cipher_checktag failed (final): %s\n",
+ gpg_strerror (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 +896,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 +936,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 +951,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. */
}
diff --git a/g10/dek.h b/g10/dek.h
index 666810c3d..1e861f565 100644
--- a/g10/dek.h
+++ b/g10/dek.h
@@ -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 e31e0232e..a838c3c61 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -144,7 +144,7 @@ static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
kbnode_t *ret_keyblock, kbnode_t *ret_found_key);
static kbnode_t finish_lookup (kbnode_t keyblock,
unsigned int req_usage, int want_exact,
- unsigned int *r_flags);
+ int want_secret, unsigned int *r_flags);
static void print_status_key_considered (kbnode_t keyblock, unsigned int flags);
@@ -1743,7 +1743,7 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname)
/* Warning: node flag bits 0 and 1 should be preserved by
* merge_selfsigs. FIXME: Check whether this still holds. */
merge_selfsigs (ctrl, keyblock);
- found_key = finish_lookup (keyblock, pk->req_usage, 0, &infoflags);
+ found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, &infoflags);
print_status_key_considered (keyblock, infoflags);
if (found_key)
pk_from_block (pk, keyblock, found_key);
@@ -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;
}
}
}
@@ -3494,7 +3511,7 @@ merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock)
*/
static kbnode_t
finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
- unsigned int *r_flags)
+ int want_secret, unsigned int *r_flags)
{
kbnode_t k;
@@ -3636,6 +3653,13 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact,
continue;
}
+ if (want_secret && agent_probe_secret_key (NULL, pk))
+ {
+ if (DBG_LOOKUP)
+ log_debug ("\tno secret key\n");
+ continue;
+ }
+
if (DBG_LOOKUP)
log_debug ("\tsubkey might be fine\n");
/* In case a key has a timestamp of 0 set, we make sure
@@ -3823,7 +3847,7 @@ lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret,
* merge_selfsigs. */
merge_selfsigs (ctrl, keyblock);
found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact,
- &infoflags);
+ want_secret, &infoflags);
print_status_key_considered (keyblock, infoflags);
if (found_key)
{
diff --git a/g10/gpg.c b/g10/gpg.c
index 62d6131ba..cbac967d8 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -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
diff --git a/g10/gpg.h b/g10/gpg.h
index 9b8b77ca0..03fe384aa 100644
--- a/g10/gpg.h
+++ b/g10/gpg.h
@@ -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 4acb2de5f..3ae96a3b2 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..2f7c159a9 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 );
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 512d33c59..f2a28df74 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -245,46 +245,101 @@ 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 );
+ 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;
- /* 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];
-
- if(dek->keylen > DIM(dek->key))
- BUG ();
-
- memcpy(dek->key, seskey + 1, dek->keylen);
+ 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 );
+ /* 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 +349,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 +391,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 +402,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 +723,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 +738,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 +766,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 +1476,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 +1493,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 +1521,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 +1549,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/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..5c6d364ee 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,7 +1105,7 @@ 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)
{
@@ -1110,7 +1117,11 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
}
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 +1139,13 @@ 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;
s2kmode = iobuf_get_noeof (inp);
pktlen--;
hash_algo = iobuf_get_noeof (inp);
@@ -1162,6 +1180,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 +1211,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)
{
@@ -1392,6 +1421,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 +1588,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 +1671,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 +3197,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 +3291,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);