aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/openpgpdefs.h4
-rw-r--r--doc/gpg.texi8
-rw-r--r--g10/build-packet.c37
-rw-r--r--g10/cipher.c457
-rw-r--r--g10/dek.h4
-rw-r--r--g10/encrypt.c333
-rw-r--r--g10/filter.h57
-rw-r--r--g10/gpg.c4
-rw-r--r--g10/gpgcompose.c36
-rw-r--r--g10/keydb.h4
-rw-r--r--g10/keygen.c81
-rw-r--r--g10/main.h1
-rw-r--r--g10/misc.c2
-rw-r--r--g10/options.h1
-rw-r--r--g10/pkclist.c39
15 files changed, 942 insertions, 126 deletions
diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index 05f362159..f7ea0b52c 100644
--- a/common/openpgpdefs.h
+++ b/common/openpgpdefs.h
@@ -130,8 +130,8 @@ sigsubpkttype_t;
typedef enum
{
AEAD_ALGO_NONE = 0,
- AEAD_ALGO_EAX = 1,
- AEAD_ALGO_OCB = 2
+ AEAD_ALGO_EAX = 1, /* Deprecated. */
+ AEAD_ALGO_OCB = 2 /* The one and only. */
}
aead_algo_t;
diff --git a/doc/gpg.texi b/doc/gpg.texi
index 39c996bd9..6446f31bd 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -2709,6 +2709,14 @@ is the default.
@itemx --no-force-v4-certs
These options are obsolete and have no effect since GnuPG 2.1.
+@item --force-ocb
+@opindex force-ocb
+Force the use of OCB mode encryption instead of CFB+MDC encryption.
+OCB is a modern and faster way to do authenticated encryption than the
+older CFB+MDC method. This option is only useful for symmetric-only
+encryption because the mode is automatically selected based on the
+preferences of the recipients's public keys.
+
@item --force-mdc
@itemx --disable-mdc
@opindex force-mdc
diff --git a/g10/build-packet.c b/g10/build-packet.c
index a40ed0d82..63bfadbe0 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -42,6 +42,7 @@ static u32 calc_plaintext( PKT_plaintext *pt );
static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed);
static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
@@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt)
break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
+ case PKT_ENCRYPTED_AEAD:
new_ctb = pkt->pkt.encrypted->new_ctb;
break;
case PKT_COMPRESSED:
@@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt)
case PKT_ENCRYPTED_MDC:
rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted);
break;
+ case PKT_ENCRYPTED_AEAD:
+ rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted);
+ break;
case PKT_COMPRESSED:
rc = do_compressed (out, ctb, pkt->pkt.compressed);
break;
@@ -618,9 +623,7 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
IOBUF a = iobuf_temp();
log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC);
-
- /* The only acceptable version. */
- log_assert( enc->version == 4 );
+ log_assert (enc->version == 4 || enc->version == 5);
/* RFC 4880, Section 3.7. */
switch (enc->s2k.mode)
@@ -635,6 +638,8 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
}
iobuf_put( a, enc->version );
iobuf_put( a, enc->cipher_algo );
+ if (enc->version == 5)
+ iobuf_put (a, enc->aead_algo);
iobuf_put( a, enc->s2k.mode );
iobuf_put( a, enc->s2k.hash_algo );
if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) {
@@ -821,6 +826,32 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
}
+/* Serialize the symmetrically AEAD encrypted data packet
+ * (rfc4880bis-03, Section 5.16) described by ED and write it to OUT.
+ *
+ * Note: this only writes only packet's header. The caller must then
+ * follow up and write the actual encrypted data. This should be done
+ * by pushing the the cipher_filter_aead. */
+static int
+do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed)
+{
+ u32 n;
+
+ log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD);
+
+ n = ed->len ? (ed->len + ed->extralen + 4) : 0;
+ write_header (out, ctb, n );
+ iobuf_writebyte (out, 1); /* Version. */
+ iobuf_writebyte (out, ed->cipher_algo);
+ iobuf_writebyte (out, ed->aead_algo);
+ iobuf_writebyte (out, ed->chunkbyte);
+
+ /* This is all. The caller has to write the encrypted data */
+
+ return 0;
+}
+
+
/* Serialize the compressed packet (RFC 4880, Section 5.6) described
by CD and write it to OUT.
diff --git a/g10/cipher.c b/g10/cipher.c
index f577c97db..155ce45cd 100644
--- a/g10/cipher.c
+++ b/g10/cipher.c
@@ -37,11 +37,29 @@
#include "../common/status.h"
-#define MIN_PARTIAL_SIZE 512
+/* The size of the buffer we allocate to encrypt the data. This must
+ * be a multiple of the OCB blocksize (16 byte). */
+#define AEAD_ENC_BUFFER_SIZE (64*1024)
+
+
+/* Wrapper around iobuf_write to make sure that a proper error code is
+ * always returned. */
+static gpg_error_t
+my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen)
+{
+ if (iobuf_write (a, buffer, buflen))
+ {
+ gpg_error_t err = iobuf_error (a);
+ if (!err || !gpg_err_code (err)) /* (The latter should never happen) */
+ err = gpg_error (GPG_ERR_EIO);
+ return err;
+ }
+ return 0;
+}
static void
-write_header (cipher_filter_context_t *cfx, iobuf_t a)
+write_cfb_header (cipher_filter_context_t *cfx, iobuf_t a)
{
gcry_error_t err;
PACKET pkt;
@@ -116,7 +134,7 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a)
/*
- * This filter is used to en/de-cipher data with a symmetric algorithm
+ * This filter is used to encrypt with a symmetric algorithm in CFB mode.
*/
int
cipher_filter_cfb (void *opaque, int control,
@@ -128,13 +146,13 @@ cipher_filter_cfb (void *opaque, int control,
if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
{
- rc = -1; /* not yet used */
+ rc = -1; /* not used */
}
else if (control == IOBUFCTRL_FLUSH) /* encrypt */
{
log_assert (a);
if (!cfx->wrote_header)
- write_header (cfx, a);
+ write_cfb_header (cfx, a);
if (cfx->mdc_hash)
gcry_md_write (cfx->mdc_hash, buf, size);
gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0);
@@ -185,3 +203,432 @@ cipher_filter_cfb (void *opaque, int control,
return rc;
}
+
+
+
+/* Set the nonce and the additional data for the current chunk. If
+ * FINAL is set the final AEAD chunk is processed. This also reset
+ * the encryption machinery so that the handle can be used for a new
+ * chunk. */
+static gpg_error_t
+set_ocb_nonce_and_ad (cipher_filter_context_t *cfx, int final)
+{
+ gpg_error_t err;
+ unsigned char nonce[16];
+ unsigned char ad[21];
+ int i;
+
+ log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB);
+ memcpy (nonce, cfx->startiv, 15);
+ i = 7;
+
+ nonce[i++] ^= cfx->chunkindex >> 56;
+ nonce[i++] ^= cfx->chunkindex >> 48;
+ nonce[i++] ^= cfx->chunkindex >> 40;
+ nonce[i++] ^= cfx->chunkindex >> 32;
+ nonce[i++] ^= cfx->chunkindex >> 24;
+ nonce[i++] ^= cfx->chunkindex >> 16;
+ nonce[i++] ^= cfx->chunkindex >> 8;
+ nonce[i++] ^= cfx->chunkindex;
+
+ if (DBG_CRYPTO)
+ log_printhex (nonce, 15, "nonce:");
+ err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i);
+ if (err)
+ return err;
+
+ ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD);
+ ad[1] = 1;
+ ad[2] = cfx->dek->algo;
+ ad[3] = AEAD_ALGO_OCB;
+ ad[4] = cfx->chunkbyte;
+ ad[5] = cfx->chunkindex >> 56;
+ ad[6] = cfx->chunkindex >> 48;
+ ad[7] = cfx->chunkindex >> 40;
+ ad[8] = cfx->chunkindex >> 32;
+ ad[9] = cfx->chunkindex >> 24;
+ ad[10]= cfx->chunkindex >> 16;
+ ad[11]= cfx->chunkindex >> 8;
+ ad[12]= cfx->chunkindex;
+ if (final)
+ {
+ ad[13] = cfx->total >> 56;
+ ad[14] = cfx->total >> 48;
+ ad[15] = cfx->total >> 40;
+ ad[16] = cfx->total >> 32;
+ ad[17] = cfx->total >> 24;
+ ad[18] = cfx->total >> 16;
+ ad[19] = cfx->total >> 8;
+ ad[20] = cfx->total;
+ }
+ if (DBG_CRYPTO)
+ log_printhex (ad, final? 21 : 13, "authdata:");
+ return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13);
+}
+
+
+static gpg_error_t
+write_ocb_header (cipher_filter_context_t *cfx, iobuf_t a)
+{
+ gpg_error_t err;
+ PACKET pkt;
+ PKT_encrypted ed;
+ unsigned int blocksize;
+ unsigned int startivlen;
+ enum gcry_cipher_modes ciphermode;
+
+ log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB);
+
+ blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo);
+ if (blocksize != 16 )
+ log_fatal ("unsupported blocksize %u for AEAD\n", blocksize);
+
+ err = openpgp_aead_algo_info (cfx->dek->use_aead, &ciphermode, &startivlen);
+ if (err)
+ goto leave;
+
+ cfx->chunkbyte = 22 - 6; /* Default to the suggested max of 4 MiB. */
+ cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6);
+ cfx->chunklen = 0;
+ cfx->bufsize = AEAD_ENC_BUFFER_SIZE;
+ cfx->buflen = 0;
+ cfx->buffer = xtrymalloc (cfx->bufsize);
+ if (!cfx->buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ memset (&ed, 0, sizeof ed);
+ ed.new_ctb = 1; /* (Is anyway required for the packet type). */
+ ed.len = 0; /* fixme: cfx->datalen */
+ ed.extralen = startivlen + 16; /* (16 is the taglen) */
+ ed.cipher_algo = cfx->dek->algo;
+ ed.aead_algo = cfx->dek->use_aead;
+ ed.chunkbyte = cfx->chunkbyte;
+
+ init_packet (&pkt);
+ pkt.pkttype = PKT_ENCRYPTED_AEAD;
+ pkt.pkt.encrypted = &ed;
+
+ if (DBG_FILTER)
+ log_debug ("aead packet: len=%lu extralen=%d\n",
+ (unsigned long)ed.len, ed.extralen);
+
+ write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d",
+ cfx->dek->algo, ed.aead_algo);
+ print_cipher_algo_note (cfx->dek->algo);
+
+ if (build_packet( a, &pkt))
+ log_bug ("build_packet(ENCRYPTED_AEAD) failed\n");
+
+ log_assert (sizeof cfx->startiv >= startivlen);
+ gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM);
+ err = my_iobuf_write (a, cfx->startiv, startivlen);
+ if (err)
+ goto leave;
+
+ err = openpgp_cipher_open (&cfx->cipher_hd,
+ cfx->dek->algo,
+ ciphermode,
+ GCRY_CIPHER_SECURE);
+ if (err)
+ goto leave;
+
+ if (DBG_CRYPTO)
+ log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:");
+ err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen);
+ if (err)
+ return err;
+
+ cfx->wrote_header = 1;
+
+ leave:
+ return err;
+}
+
+
+/* Get and write the auth tag to stream A. */
+static gpg_error_t
+write_ocb_auth_tag (cipher_filter_context_t *cfx, iobuf_t a)
+{
+ gpg_error_t err;
+ char tag[16];
+
+ err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16);
+ if (err)
+ goto leave;
+ err = my_iobuf_write (a, tag, 16);
+ if (err)
+ goto leave;
+
+ leave:
+ if (err)
+ log_error ("write_auth_tag failed: %s\n", gpg_strerror (err));
+ return err;
+}
+
+
+/* Write the final chunk to stream A. */
+static gpg_error_t
+write_ocb_final_chunk (cipher_filter_context_t *cfx, iobuf_t a)
+{
+ gpg_error_t err;
+ char dummy[1];
+
+ err = set_ocb_nonce_and_ad (cfx, 1);
+ if (err)
+ goto leave;
+
+ gcry_cipher_final (cfx->cipher_hd);
+
+ /* Encrypt an empty string. */
+ err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0);
+ if (err)
+ goto leave;
+
+ err = write_ocb_auth_tag (cfx, a);
+
+ leave:
+ return err;
+}
+
+
+/* The core of the flush sub-function of cipher_filter_ocb. */
+static gpg_error_t
+do_ocb_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size)
+{
+ gpg_error_t err = 0;
+ int finalize = 0;
+ size_t n;
+
+ /* Put the data into a buffer, flush and encrypt as needed. */
+ if (DBG_FILTER)
+ log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen);
+ do
+ {
+ const unsigned fast_threshold = 512;
+ const byte *src_buf = NULL;
+ int enc_now = 0;
+
+ if (cfx->buflen + size < cfx->bufsize)
+ n = size;
+ else
+ n = cfx->bufsize - cfx->buflen;
+
+ if (cfx->buflen % fast_threshold != 0)
+ {
+ /* Attempt to align cfx->buflen to fast threshold size first. */
+ size_t nalign = fast_threshold - (cfx->buflen % fast_threshold);
+ if (nalign < n)
+ {
+ n = nalign;
+ }
+ }
+ else if (cfx->buflen == 0 && n >= fast_threshold)
+ {
+ /* Handle large input buffers as multiple of cipher blocksize. */
+ n = (n / 16) * 16;
+ }
+
+ if (cfx->chunklen + cfx->buflen + n >= cfx->chunksize)
+ {
+ size_t n1 = cfx->chunksize - (cfx->chunklen + cfx->buflen);
+ finalize = 1;
+ if (DBG_FILTER)
+ log_debug ("chunksize %"PRIu64" reached;"
+ " cur buflen=%zu using %zu of %zu\n",
+ cfx->chunksize, cfx->buflen,
+ n1, n);
+ n = n1;
+ }
+
+ if (!finalize && cfx->buflen % 16 == 0 && cfx->buflen > 0
+ && size >= fast_threshold)
+ {
+ /* If cfx->buffer is aligned and remaining input buffer length
+ * is long, encrypt cfx->buffer inplace now to allow fast path
+ * handling on next loop iteration. */
+ src_buf = cfx->buffer;
+ enc_now = 1;
+ n = 0;
+ }
+ else if (cfx->buflen == 0 && n >= fast_threshold)
+ {
+ /* Fast path for large input buffer. This avoids memcpy and
+ * instead encrypts directly from input to cfx->buffer. */
+ log_assert (n % 16 == 0 || finalize);
+ src_buf = buf;
+ cfx->buflen = n;
+ buf += n;
+ size -= n;
+ enc_now = 1;
+ }
+ else if (n > 0)
+ {
+ memcpy (cfx->buffer + cfx->buflen, buf, n);
+ src_buf = cfx->buffer;
+ cfx->buflen += n;
+ buf += n;
+ size -= n;
+ }
+
+ if (cfx->buflen == cfx->bufsize || enc_now || finalize)
+ {
+ if (DBG_FILTER)
+ log_debug ("encrypting: size=%zu buflen=%zu %s%s n=%zu\n",
+ size, cfx->buflen, finalize?"(finalize)":"",
+ enc_now?"(now)":"", n);
+
+ if (!cfx->chunklen)
+ {
+ if (DBG_FILTER)
+ log_debug ("start encrypting a new chunk\n");
+ err = set_ocb_nonce_and_ad (cfx, 0);
+ if (err)
+ goto leave;
+ }
+
+ if (finalize)
+ gcry_cipher_final (cfx->cipher_hd);
+ if (DBG_FILTER)
+ {
+ if (finalize)
+ log_printhex (src_buf, cfx->buflen, "plain(1):");
+ else if (cfx->buflen > 32)
+ log_printhex (src_buf + cfx->buflen - 32, 32,
+ "plain(last32):");
+ }
+
+ /* Take care: even with a buflen of zero an encrypt needs to
+ * be called after gcry_cipher_final and before
+ * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB
+ * mode. */
+ err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer,
+ cfx->buflen, src_buf, cfx->buflen);
+ if (err)
+ goto leave;
+ if (finalize && DBG_FILTER)
+ log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):");
+ err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
+ if (err)
+ goto leave;
+ cfx->chunklen += cfx->buflen;
+ cfx->total += cfx->buflen;
+ cfx->buflen = 0;
+
+ if (finalize)
+ {
+ if (DBG_FILTER)
+ log_debug ("writing tag: chunklen=%ju total=%ju\n",
+ (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+ err = write_ocb_auth_tag (cfx, a);
+ if (err)
+ goto leave;
+
+ cfx->chunkindex++;
+ cfx->chunklen = 0;
+ finalize = 0;
+ }
+ }
+ }
+ while (size);
+
+ leave:
+ return err;
+}
+
+
+/* The core of the free sub-function of cipher_filter_aead. */
+static gpg_error_t
+do_ocb_free (cipher_filter_context_t *cfx, iobuf_t a)
+{
+ gpg_error_t err = 0;
+
+ if (DBG_FILTER)
+ log_debug ("do_free: buflen=%zu\n", cfx->buflen);
+
+ if (cfx->chunklen || cfx->buflen)
+ {
+ if (DBG_FILTER)
+ log_debug ("encrypting last %zu bytes of the last chunk\n",cfx->buflen);
+
+ if (!cfx->chunklen)
+ {
+ if (DBG_FILTER)
+ log_debug ("start encrypting a new chunk\n");
+ err = set_ocb_nonce_and_ad (cfx, 0);
+ if (err)
+ goto leave;
+ }
+
+ gcry_cipher_final (cfx->cipher_hd);
+ err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen,
+ NULL, 0);
+ if (err)
+ goto leave;
+ err = my_iobuf_write (a, cfx->buffer, cfx->buflen);
+ if (err)
+ goto leave;
+ /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */
+ cfx->chunklen += cfx->buflen;
+ cfx->total += cfx->buflen;
+
+ /* Get and write the authentication tag. */
+ if (DBG_FILTER)
+ log_debug ("writing tag: chunklen=%ju total=%ju\n",
+ (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total);
+ err = write_ocb_auth_tag (cfx, a);
+ if (err)
+ goto leave;
+ cfx->chunkindex++;
+ cfx->chunklen = 0;
+ }
+
+ /* Write the final chunk. */
+ if (DBG_FILTER)
+ log_debug ("creating final chunk\n");
+ err = write_ocb_final_chunk (cfx, a);
+
+ leave:
+ xfree (cfx->buffer);
+ cfx->buffer = NULL;
+ gcry_cipher_close (cfx->cipher_hd);
+ cfx->cipher_hd = NULL;
+ return err;
+}
+
+
+/*
+ * This filter is used to encrypt with a symmetric algorithm in OCB mode.
+ */
+int
+cipher_filter_ocb (void *opaque, int control,
+ iobuf_t a, byte *buf, size_t *ret_len)
+{
+ cipher_filter_context_t *cfx = opaque;
+ size_t size = *ret_len;
+ int rc = 0;
+
+ if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */
+ {
+ rc = -1; /* not used */
+ }
+ else if (control == IOBUFCTRL_FLUSH) /* encrypt */
+ {
+ if (!cfx->wrote_header && (rc=write_ocb_header (cfx, a)))
+ ;
+ else
+ rc = do_ocb_flush (cfx, a, buf, size);
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+ rc = do_ocb_free (cfx, a);
+ }
+ else if (control == IOBUFCTRL_DESC)
+ {
+ mem2str (buf, "cipher_filter_ocb", *ret_len);
+ }
+
+ return rc;
+}
diff --git a/g10/dek.h b/g10/dek.h
index 365449179..764b0140c 100644
--- a/g10/dek.h
+++ b/g10/dek.h
@@ -31,7 +31,9 @@ typedef struct
* verbose mode. */
unsigned int algo_info_printed : 1;
- /* AEAD shall be used. The value is the AEAD algo. */
+ /* AEAD shall be used. The value is the AEAD algo. Note that in
+ * practise only AEAD_ALGO_OCB, AEAD_ALGO_EAX is only used for
+ * decryption. */
int use_aead : 4;
/* MDC shall be used. */
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 5f9480fad..0c6abf312 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -52,7 +52,7 @@ static int write_pubkey_enc_from_list (ctrl_t ctrl,
int
encrypt_symmetric (const char *filename)
{
- return encrypt_simple( filename, 1, 0 );
+ return encrypt_simple( filename, 1, opt.force_ocb);
}
@@ -126,45 +126,165 @@ create_dek_with_warnings (int fallback_to_3des, pk_list_t pk_list)
}
-/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and
- the algorithm that will be used to encrypt the contents of the SED
- packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random
- session key that is appropriate for DEK->ALGO is generated and
- stored there.
-
- Encrypt that session key using DEK and store the result in ENCKEY,
- which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */
-void
-encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey)
+/* Encrypt a session key using DEK and store a pointer to the result
+ * at R_ENCKEY and its length at R_ENCKEYLEN.
+ *
+ * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and
+ * the algorithm that will be used to encrypt the contents of the
+ * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random
+ * session key that is appropriate for DEK->ALGO is generated and
+ * stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm
+ * is used for encryption.
+ */
+static gpg_error_t
+encrypt_seskey (DEK *dek, aead_algo_t aead_algo,
+ DEK **r_seskey, void **r_enckey, size_t *r_enckeylen)
{
- gcry_cipher_hd_t hd;
- byte buf[33];
+ gpg_error_t err;
+ gcry_cipher_hd_t hd = NULL;
+ byte *buf = NULL;
+ DEK *seskey;
+
+ *r_enckey = NULL;
+ *r_enckeylen = 0;
- log_assert ( dek->keylen <= 32 );
- if (!*seskey)
+ if (*r_seskey)
+ seskey = *r_seskey;
+ else
{
- *seskey=xmalloc_clear(sizeof(DEK));
- (*seskey)->algo=dek->algo;
- make_session_key(*seskey);
+ seskey = xtrycalloc (1, sizeof(DEK));
+ if (!seskey)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ seskey->algo = dek->algo;
+ make_session_key (seskey);
/*log_hexdump( "thekey", c->key, c->keylen );*/
}
- /* The encrypted session key is prefixed with a one-octet algorithm id. */
- buf[0] = (*seskey)->algo;
- memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
-
- /* We only pass already checked values to the following function,
- thus we consider any failure as fatal. */
- if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
- BUG ();
- if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
- BUG ();
- gcry_cipher_setiv (hd, NULL, 0);
- gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
+
+ if (aead_algo)
+ {
+ unsigned int noncelen;
+ enum gcry_cipher_modes ciphermode;
+ byte ad[4];
+
+ err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen);
+ if (err)
+ goto leave;
+
+ /* Allocate space for the nonce, the key, and the authentication
+ * tag (16). */
+ buf = xtrymalloc_secure (noncelen + seskey->keylen + 16);
+ if (!buf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM);
+
+ err = openpgp_cipher_open (&hd, dek->algo,
+ ciphermode, GCRY_CIPHER_SECURE);
+ if (!err)
+ err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
+ if (!err)
+ err = gcry_cipher_setiv (hd, buf, noncelen);
+ if (err)
+ goto leave;
+
+ ad[0] = (0xc0 | PKT_SYMKEY_ENC);
+ ad[1] = 5;
+ ad[2] = dek->algo;
+ ad[3] = aead_algo;
+ err = gcry_cipher_authenticate (hd, ad, 4);
+ if (err)
+ goto leave;
+
+ memcpy (buf + noncelen, seskey->key, seskey->keylen);
+ gcry_cipher_final (hd);
+ err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0);
+ if (err)
+ goto leave;
+ err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16);
+ if (err)
+ goto leave;
+ *r_enckeylen = noncelen + seskey->keylen + 16;
+ *r_enckey = buf;
+ buf = NULL;
+ }
+ else
+ {
+ /* In the old version 4 SKESK the encrypted session key is
+ * prefixed with a one-octet algorithm id. */
+ buf = xtrymalloc_secure (1 + seskey->keylen);
+ if (!buf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ buf[0] = seskey->algo;
+ memcpy (buf + 1, seskey->key, seskey->keylen );
+
+ err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1);
+ if (!err)
+ err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
+ if (!err)
+ err = gcry_cipher_setiv (hd, NULL, 0);
+ if (!err)
+ err = gcry_cipher_encrypt (hd, buf, 1 + seskey->keylen, NULL, 0);
+ if (err)
+ goto leave;
+ *r_enckeylen = 1 + seskey->keylen;
+ *r_enckey = buf;
+ buf = NULL;
+ }
+
+ /* Return the session key in case we allocated it. */
+ *r_seskey = seskey;
+ seskey = NULL;
+
+ leave:
gcry_cipher_close (hd);
+ if (seskey != *r_seskey)
+ xfree (seskey);
+ xfree (buf);
+ return err;
+}
- memcpy( enckey, buf, (*seskey)->keylen + 1 );
- wipememory( buf, sizeof buf ); /* burn key */
+
+/* Return the AEAD algo if we shall use AEAD mode. Returns 0 if AEAD
+ * shall not be used. */
+aead_algo_t
+use_aead (pk_list_t pk_list, int algo)
+{
+ int can_use;
+
+ can_use = openpgp_cipher_get_algo_blklen (algo) == 16;
+
+ /* With --force-aead we want AEAD. */
+ if (opt.force_ocb)
+ {
+ if (!can_use)
+ {
+ log_info ("Warning: request to use OCB ignored for cipher '%s'\n",
+ openpgp_cipher_algo_name (algo));
+ return 0;
+ }
+ return AEAD_ALGO_OCB;
+ }
+
+ /* AEAD does only work with 128 bit cipher blocklength. */
+ if (!can_use)
+ return 0;
+
+ /* Note the user which keys have no AEAD feature flag set. */
+ if (opt.verbose)
+ warn_missing_aead_from_pklist (pk_list);
+
+ /* If all keys support AEAD we can use it. */
+ return select_aead_from_pklist (pk_list);
}
@@ -196,9 +316,9 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
PACKET pkt;
PKT_plaintext *pt = NULL;
STRING2KEY *s2k = NULL;
- byte enckey[33];
+ void *enckey = NULL;
+ size_t enckeylen = 0;
int rc = 0;
- int seskeylen = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t *afx = NULL;
@@ -250,6 +370,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
cfx.dek = NULL;
if ( mode )
{
+ aead_algo_t aead_algo;
+
rc = setup_symkey (&s2k, &cfx.dek);
if (rc)
{
@@ -269,23 +391,42 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
"due to the S2K mode\n"));
}
+ /* See whether we want to use OCB. */
+ aead_algo = use_aead (NULL, cfx.dek->algo);
+
if ( use_seskey )
{
- DEK *dek = NULL; /* Dummy. */
+ DEK *dek = NULL;
- seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ());
- encrypt_seskey( cfx.dek, &dek, enckey );
- xfree( cfx.dek ); cfx.dek = dek;
+ rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen);
+ if (rc)
+ {
+ xfree (cfx.dek);
+ xfree (s2k);
+ iobuf_close (inp);
+ release_progress_context (pfx);
+ return rc;
+ }
+ /* Replace key in DEK. */
+ xfree (cfx.dek);
+ cfx.dek = dek;
}
- if (opt.verbose)
- log_info(_("using cipher %s\n"),
- openpgp_cipher_algo_name (cfx.dek->algo));
+ if (aead_algo)
+ cfx.dek->use_aead = aead_algo;
+ else
+ cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo);
- cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
+ if (opt.verbose)
+ log_info(_("using cipher %s.%s\n"),
+ openpgp_cipher_algo_name (cfx.dek->algo),
+ cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead)
+ /**/ : "CFB");
}
- if (do_compress && cfx.dek && cfx.dek->use_mdc
+ if (do_compress
+ && cfx.dek
+ && (cfx.dek->use_mdc || cfx.dek->use_aead)
&& is_file_compressed(filename, &rc))
{
if (opt.verbose)
@@ -310,20 +451,23 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if ( s2k )
{
- PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
- enc->version = 4;
+ PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen);
+ enc->version = cfx.dek->use_aead ? 5 : 4;
enc->cipher_algo = cfx.dek->algo;
+ enc->aead_algo = cfx.dek->use_aead;
enc->s2k = *s2k;
- if ( use_seskey && seskeylen )
+ if (enckeylen)
{
- enc->seskeylen = seskeylen + 1; /* algo id */
- memcpy (enc->seskey, enckey, seskeylen + 1 );
+ enc->seskeylen = enckeylen;
+ memcpy (enc->seskey, enckey, enckeylen);
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if ((rc = build_packet( out, &pkt )))
log_error("build symkey packet failed: %s\n", gpg_strerror (rc) );
xfree (enc);
+ xfree (enckey);
+ enckey = NULL;
}
if (!opt.no_literal)
@@ -380,12 +524,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
/* Register the cipher filter. */
if (mode)
- iobuf_push_filter ( out, cipher_filter_cfb, &cfx );
+ iobuf_push_filter (out,
+ cfx.dek->use_aead? cipher_filter_ocb
+ /**/ : cipher_filter_cfb,
+ &cfx );
/* Register the compress filter. */
if ( do_compress )
{
- if (cfx.dek && cfx.dek->use_mdc)
+ if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead))
zfx.new_ctb = 1;
push_compress_filter (out, &zfx, default_compress_algo());
}
@@ -400,15 +547,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
{
/* User requested not to create a literal packet, so we copy the
plain data. */
- byte copy_buffer[4096];
- int bytes_copied;
- while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
- if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
- log_error ("copying input to output failed: %s\n",
- gpg_strerror (rc) );
- break;
- }
- wipememory (copy_buffer, 4096); /* burn buffer */
+ byte copy_buffer[4096];
+ int bytes_copied;
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) {
+ log_error ("copying input to output failed: %s\n",
+ gpg_strerror (rc) );
+ break;
+ }
+ wipememory (copy_buffer, 4096); /* burn buffer */
}
/* Finish the stuff. */
@@ -424,6 +571,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if (pt)
pt->buf = NULL;
free_packet (&pkt, NULL);
+ xfree (enckey);
xfree (cfx.dek);
xfree (s2k);
release_armor_context (afx);
@@ -476,23 +624,33 @@ setup_symkey (STRING2KEY **symkey_s2k, DEK **symkey_dek)
static int
-write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
- iobuf_t out)
+write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo,
+ DEK *symkey_dek, DEK *dek, iobuf_t out)
{
- int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo);
-
+ int rc;
+ void *enckey;
+ size_t enckeylen;
PKT_symkey_enc *enc;
- byte enckey[33];
PACKET pkt;
- enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
- encrypt_seskey(symkey_dek,&dek,enckey);
+ rc = encrypt_seskey (symkey_dek, aead_algo, &dek, &enckey, &enckeylen);
+ if (rc)
+ return rc;
+ enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen);
+ if (!enc)
+ {
+ rc = gpg_error_from_syserror ();
+ xfree (enckey);
+ return rc;
+ }
- enc->version = 4;
+ enc->version = aead_algo? 5 : 4;
enc->cipher_algo = opt.s2k_cipher_algo;
+ enc->aead_algo = aead_algo;
enc->s2k = *symkey_s2k;
- enc->seskeylen = seskeylen + 1; /* algo id */
- memcpy( enc->seskey, enckey, seskeylen + 1 );
+ enc->seskeylen = enckeylen;
+ memcpy (enc->seskey, enckey, enckeylen);
+ xfree (enckey);
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
@@ -500,7 +658,7 @@ write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
if ((rc=build_packet(out,&pkt)))
log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc));
- xfree(enc);
+ xfree (enc);
return rc;
}
@@ -706,14 +864,18 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (rc)
goto leave;
- cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo);
+ cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo);
+ if (!cfx.dek->use_aead)
+ cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo);
/* Only do the is-file-already-compressed check if we are using a
MDC. This forces compressed files to be re-compressed if we do
not have a MDC to give some protection against chosen ciphertext
attacks. */
- if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2))
+ if (do_compress
+ && (cfx.dek->use_mdc || cfx.dek->use_aead)
+ && is_file_compressed(filename, &rc2))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename);
@@ -737,7 +899,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
seems to be the most useful on the recipient side - there is no
point in prompting a user for a passphrase if they have the
secret key needed to decrypt. */
- if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
+ if(use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead,
+ symkey_dek, cfx.dek, out)))
goto leave;
if (!opt.no_literal)
@@ -780,7 +943,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
cfx.datalen = filesize && !do_compress ? filesize : 0;
/* Register the cipher filter. */
- iobuf_push_filter (out, cipher_filter_cfb, &cfx);
+ iobuf_push_filter (out,
+ cfx.dek->use_aead? cipher_filter_ocb
+ /**/ : cipher_filter_cfb,
+ &cfx);
/* Register the compress filter. */
if (do_compress)
@@ -889,7 +1055,9 @@ encrypt_filter (void *opaque, int control,
if (rc)
return rc;
- efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo);
+ efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo);
+ if (!efx->cfx.dek->use_aead)
+ efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo);
make_session_key ( efx->cfx.dek );
if (DBG_CRYPTO)
@@ -902,13 +1070,16 @@ encrypt_filter (void *opaque, int control,
if(efx->symkey_s2k && efx->symkey_dek)
{
- rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
- efx->cfx.dek,a);
+ rc = write_symkey_enc (efx->symkey_s2k, efx->cfx.dek->use_aead,
+ efx->symkey_dek, efx->cfx.dek, a);
if(rc)
return rc;
}
- iobuf_push_filter (a, cipher_filter_cfb, &efx->cfx);
+ iobuf_push_filter (a,
+ efx->cfx.dek->use_aead? cipher_filter_ocb
+ /**/ : cipher_filter_cfb,
+ &efx->cfx);
}
rc = iobuf_write (a, buf, size);
@@ -971,6 +1142,12 @@ write_pubkey_enc (ctrl_t ctrl,
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
ustr );
+ log_info (_("%s/%s.%s encrypted for: \"%s\"\n"),
+ openpgp_pk_algo_name (enc->pubkey_algo),
+ openpgp_cipher_algo_name (dek->algo),
+ dek->use_aead? openpgp_aead_algo_name (dek->use_aead)
+ /**/ : "CFB",
+ ustr );
xfree (ustr);
}
/* And write it. */
diff --git a/g10/filter.h b/g10/filter.h
index d2f6c3f0f..fdb35232d 100644
--- a/g10/filter.h
+++ b/g10/filter.h
@@ -88,15 +88,52 @@ struct compress_filter_context_s {
typedef struct compress_filter_context_s compress_filter_context_t;
-typedef struct {
- DEK *dek;
- u32 datalen;
- gcry_cipher_hd_t cipher_hd;
- unsigned int wrote_header : 1;
- unsigned int short_blklen_warn : 1;
- unsigned long short_blklen_count;
- gcry_md_hd_t mdc_hash;
- byte enchash[20];
+typedef struct
+{
+ /* Object with the key and algo */
+ DEK *dek;
+
+ /* Length of the data to encrypt if known - 32 bit because OpenPGP
+ * requires partial encoding for a larger data size. */
+ u32 datalen;
+
+ /* The current cipher handle. */
+ gcry_cipher_hd_t cipher_hd;
+
+ /* Various processing flags. */
+ unsigned int wrote_header : 1;
+ unsigned int short_blklen_warn : 1;
+ unsigned long short_blklen_count;
+
+ /* The encoded chunk byte for AEAD. */
+ byte chunkbyte;
+
+ /* The decoded CHUNKBYTE. */
+ uint64_t chunksize;
+
+ /* The chunk index for AEAD. */
+ uint64_t chunkindex;
+
+ /* The number of bytes in the current chunk. */
+ uint64_t chunklen;
+
+ /* The total count of encrypted plaintext octets. Note that we
+ * don't care about encrypting more than 16 Exabyte. */
+ uint64_t total;
+
+ /* The hash context and a buffer used for MDC. */
+ gcry_md_hd_t mdc_hash;
+ byte enchash[20];
+
+ /* The start IV for AEAD encryption. */
+ byte startiv[16];
+
+ /* Using a large buffer for encryption makes processing easier and
+ * also makes sure the data is well aligned. */
+ char *buffer;
+ size_t bufsize; /* Allocated length. */
+ size_t buflen; /* Used length. */
+
} cipher_filter_context_t;
@@ -148,6 +185,8 @@ gpg_error_t push_compress_filter2 (iobuf_t out,compress_filter_context_t *zfx,
/*-- cipher.c --*/
int cipher_filter_cfb (void *opaque, int control,
iobuf_t chain, byte *buf, size_t *ret_len);
+int cipher_filter_ocb (void *opaque, int control,
+ iobuf_t chain, byte *buf, size_t *ret_len);
/*-- textfilter.c --*/
int text_filter( void *opaque, int control,
diff --git a/g10/gpg.c b/g10/gpg.c
index bd65612e3..cd40cf376 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -299,6 +299,7 @@ enum cmd_and_opt_values
oShowPhotos,
oNoShowPhotos,
oPhotoViewer,
+ oForceOCB,
oS2KMode,
oS2KDigest,
oS2KCipher,
@@ -834,6 +835,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"),
ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"),
ARGPARSE_s_i (oS2KCount, "s2k-count", "@"),
+ ARGPARSE_s_n (oForceOCB, "force-ocb", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"),
@@ -2981,6 +2983,8 @@ main (int argc, char **argv)
break;
case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
+ case oForceOCB: opt.force_ocb = 1; break;
+
case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break;
case oIncludeKeyBlock: opt.flags.include_key_block = 1; break;
case oNoIncludeKeyBlock: opt.flags.include_key_block = 0; break;
diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c
index d82995d1d..190949278 100644
--- a/g10/gpgcompose.c
+++ b/g10/gpgcompose.c
@@ -2169,6 +2169,42 @@ static struct option sk_esk_options[] = {
" --literal --value foo | " GPG_NAME " --list-packets" }
};
+
+/* Old version of encrypt_seskey copied from encrypt.c. */
+static void
+encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey)
+{
+ gcry_cipher_hd_t hd;
+ byte buf[33];
+
+ log_assert ( dek->keylen <= 32 );
+ if (!*seskey)
+ {
+ *seskey=xmalloc_clear(sizeof(DEK));
+ (*seskey)->algo=dek->algo;
+ make_session_key(*seskey);
+ /*log_hexdump( "thekey", c->key, c->keylen );*/
+ }
+
+ /* The encrypted session key is prefixed with a one-octet algorithm id. */
+ buf[0] = (*seskey)->algo;
+ memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
+
+ /* We only pass already checked values to the following function,
+ thus we consider any failure as fatal. */
+ if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
+ BUG ();
+ if (gcry_cipher_setkey (hd, dek->key, dek->keylen))
+ BUG ();
+ gcry_cipher_setiv (hd, NULL, 0);
+ gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0);
+ gcry_cipher_close (hd);
+
+ memcpy( enckey, buf, (*seskey)->keylen + 1 );
+ wipememory( buf, sizeof buf ); /* burn key */
+}
+
+
static int
sk_esk (const char *option, int argc, char *argv[], void *cookie)
{
diff --git a/g10/keydb.h b/g10/keydb.h
index 0f8d711a9..851d29317 100644
--- a/g10/keydb.h
+++ b/g10/keydb.h
@@ -266,8 +266,8 @@ int algo_available( preftype_t preftype, int algo,
const struct pref_hint *hint );
int select_algo_from_prefs( PK_LIST pk_list, int preftype,
int request, const struct pref_hint *hint);
-int select_mdc_from_pklist (PK_LIST pk_list);
-void warn_missing_mdc_from_pklist (PK_LIST pk_list);
+aead_algo_t select_aead_from_pklist (PK_LIST pk_list);
+void warn_missing_aead_from_pklist (PK_LIST pk_list);
void warn_missing_aes_from_pklist (PK_LIST pk_list);
/*-- skclist.c --*/
diff --git a/g10/keygen.c b/g10/keygen.c
index 80d65c444..d0731182c 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -135,6 +135,8 @@ static int nhash_prefs;
static byte zip_prefs[MAX_PREFS];
static int nzip_prefs;
static int mdc_available,ks_modify;
+static int aead_available;
+
static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey,
const char *algostr, const char *usagestr,
@@ -354,8 +356,12 @@ keygen_set_std_prefs (const char *string,int personal)
byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS];
int nsym=0, nhash=0, nzip=0, val, rc=0;
int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */
+ int ocb;
char dummy_string[20*4+1]; /* Enough for 20 items. */
+ /* Use OCB as default in GnuPG and de-vs mode. */
+ ocb = GNUPG;
+
if (!string || !ascii_strcasecmp (string, "default"))
{
if (opt.def_preference_list)
@@ -480,14 +486,24 @@ keygen_set_std_prefs (const char *string,int personal)
if(set_one_pref(val,3,tok,zip,&nzip))
rc=-1;
}
- else if (ascii_strcasecmp(tok,"mdc")==0)
+ else if (!ascii_strcasecmp(tok, "mdc")
+ || !ascii_strcasecmp(tok, "[mdc]"))
mdc=1;
- else if (ascii_strcasecmp(tok,"no-mdc")==0)
+ else if (!ascii_strcasecmp(tok, "no-mdc")
+ || !ascii_strcasecmp(tok, "[no-mdc]"))
mdc=0;
- else if (ascii_strcasecmp(tok,"ks-modify")==0)
+ else if (!ascii_strcasecmp(tok, "ks-modify")
+ || !ascii_strcasecmp(tok, "[ks-modify]"))
modify=1;
- else if (ascii_strcasecmp(tok,"no-ks-modify")==0)
+ else if (!ascii_strcasecmp(tok,"no-ks-modify")
+ || !ascii_strcasecmp(tok,"[no-ks-modify]"))
modify=0;
+ else if (!ascii_strcasecmp(tok,"aead")
+ || !ascii_strcasecmp(tok,"[aead]"))
+ ocb = 1;
+ else if (!ascii_strcasecmp(tok,"no-aead")
+ || !ascii_strcasecmp(tok,"[no-aead]"))
+ ocb = 0;
else
{
log_info (_("invalid item '%s' in preference string\n"),tok);
@@ -578,6 +594,7 @@ keygen_set_std_prefs (const char *string,int personal)
memcpy (hash_prefs, hash, (nhash_prefs=nhash));
memcpy (zip_prefs, zip, (nzip_prefs=nzip));
mdc_available = mdc;
+ aead_available = ocb;
ks_modify = modify;
prefs_initialized = 1;
}
@@ -586,6 +603,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 *
@@ -624,6 +642,7 @@ keygen_get_std_prefs(void)
uid->prefs[j].value=0;
uid->flags.mdc=mdc_available;
+ uid->flags.aead=aead_available;
uid->flags.ks_modify=ks_modify;
return uid;
@@ -670,6 +689,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)
{
@@ -731,6 +793,14 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque)
delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM);
}
+ if (aead_available) /* The only preference is AEAD_ALGO_OCB. */
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, "\x02", 1);
+ else
+ {
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD);
+ }
+
if (nhash_prefs)
build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
else
@@ -747,8 +817,9 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque)
delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR);
}
- /* Make sure that the MDC feature flag is set if needed. */
+ /* Make sure that the MDC and AEAD feature flags are set as needed. */
add_feature_mdc (sig,mdc_available);
+ add_feature_aead (sig, aead_available);
add_keyserver_modify (sig,ks_modify);
keygen_add_keyserver_url(sig,NULL);
diff --git a/g10/main.h b/g10/main.h
index 273ddaaaf..7e5c5c45c 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -233,7 +233,6 @@ void display_online_help( const char *keyword );
/*-- encode.c --*/
gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek);
-void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey);
int use_mdc (pk_list_t pk_list,int algo);
int encrypt_symmetric (const char *filename );
int encrypt_store (const char *filename );
diff --git a/g10/misc.c b/g10/misc.c
index 0b19e1a2b..23a627a66 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -687,7 +687,7 @@ openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode,
*r_noncelen = 15;
break;
- case AEAD_ALGO_EAX:
+ case AEAD_ALGO_EAX: /* Only for decryption of some old data. */
*r_mode = MY_GCRY_CIPHER_MODE_EAX;
*r_noncelen = 16;
break;
diff --git a/g10/options.h b/g10/options.h
index b11e91c66..e631b83c1 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -89,6 +89,7 @@ struct
int list_packets; /* Option --list-packets active. */
int def_cipher_algo;
int def_digest_algo;
+ int force_ocb;
int cert_digest_algo;
int compress_algo;
int compress_level;
diff --git a/g10/pkclist.c b/g10/pkclist.c
index fb8b17620..54326822d 100644
--- a/g10/pkclist.c
+++ b/g10/pkclist.c
@@ -1648,36 +1648,37 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype,
return result;
}
-/*
- * Select the MDC flag from the pk_list. We can only use MDC if all
- * recipients support this feature.
- */
-int
-select_mdc_from_pklist (PK_LIST pk_list)
+
+/* Select the AEAD flag from the pk_list. We can only use AEAD if all
+ * recipients support this feature. Returns the AEAD to be used or 0
+ * if AEAD shall not be used. */
+aead_algo_t
+select_aead_from_pklist (PK_LIST pk_list)
{
- PK_LIST pkr;
+ pk_list_t pkr;
+ int aead;
- if ( !pk_list )
+ if (!pk_list)
return 0;
for (pkr = pk_list; pkr; pkr = pkr->next)
{
- int mdc;
-
if (pkr->pk->user_id) /* selected by user ID */
- mdc = pkr->pk->user_id->flags.mdc;
+ aead = pkr->pk->user_id->flags.aead;
else
- mdc = pkr->pk->flags.mdc;
- if (!mdc)
+ aead = pkr->pk->flags.aead;
+ if (!aead)
return 0; /* At least one recipient does not support it. */
}
- return 1; /* Can be used. */
+
+ return AEAD_ALGO_OCB; /* Yes, AEAD can be used. */
}
-/* Print a warning for all keys in PK_LIST missing the MDC feature. */
+/* Print a warning for all keys in PK_LIST missing the AEAD feature
+ * flag or AEAD algorithms. */
void
-warn_missing_mdc_from_pklist (PK_LIST pk_list)
+warn_missing_aead_from_pklist (PK_LIST pk_list)
{
PK_LIST pkr;
@@ -1686,12 +1687,12 @@ warn_missing_mdc_from_pklist (PK_LIST pk_list)
int mdc;
if (pkr->pk->user_id) /* selected by user ID */
- mdc = pkr->pk->user_id->flags.mdc;
+ mdc = pkr->pk->user_id->flags.aead;
else
- mdc = pkr->pk->flags.mdc;
+ mdc = pkr->pk->flags.aead;
if (!mdc)
log_info (_("Note: key %s has no %s feature\n"),
- keystr_from_pk (pkr->pk), "MDC");
+ keystr_from_pk (pkr->pk), "AEAD");
}
}