gpg: Support OCB encryption.

* g10/build-packet.c (do_encrypted_aead): New.
(do_symkey_enc): Handle version 5.
(build_packet): Support the ENCRYPTED_AEAD packet.
* g10/cipher.c (MIN_PARTIAL_SIZE): Remove unused macro.
(AEAD_ENC_BUFFER_SIZE): New macro.
(my_iobuf_write): New.
(write_header): Rename to write_cfb_header.  Adjust caller.
(set_ocb_nonce_and_ad): New.
(write_ocb_header): New.
(write_ocb_auth_tag): New.
(write_ocb_final_chunk): New.
(do_ocb_flush): New.
(do_ocb_free): New.
(cipher_filter_ocb): New.
* g10/filter.h (cipher_filter_context_t): Add fields for AEAD.
* g10/encrypt.c (encrypt_symmetric): For the use of a session key in
OCB mode.
(encrypt_seskey): Revamp to support OCB.
(use_aead): New.
(encrypt_simple): Support OCB.
(write_symkey_enc): Ditto.
(encrypt_crypt): Ditto.
(encrypt_filter): Handle OCB.
* g10/options.h (opt): Add field force_ocb.
* g10/gpg.c (oForceOCB): New.
(opts): New option "--force-ocb".
(main): Set force_ocb option.
* g10/gpgcompose.c (encrypt_seskey): New.
* g10/keygen.c (aead_available): New global var.
(keygen_set_std_prefs): Set AEAD feature by default in GNUPG mode. Add
parings of aead feature flag.
(keygen_get_std_prefs): Set aead flag.
(add_feature_aead): New.
(keygen_upd_std_prefs): Set OCB as preference if AEAD is enabled.
* g10/pkclist.c (select_aead_from_pklist): New.
(warn_missing_aead_from_pklist): New.
(select_mdc_from_pklist): Remove this unused function.
--

This extends the long available OCB and EAX decryption feature.  Due
to the meanwhile expired patent on OCB there is no more reason for
using EAX.  Thus we forcefully use OCB if the AEAD feature flag is set
on a key.

In GNUPG mode new keys are now created with the AEAD feature flag set.
Option --rfc4880 is one way to disable this.

GnuPG-bug-id: 6263
This commit is contained in:
Werner Koch 2022-10-31 14:33:10 +01:00
parent aa397fdcdb
commit a545e14e8a
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
15 changed files with 942 additions and 126 deletions

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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. */

View File

@ -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;
log_assert ( dek->keylen <= 32 );
if (!*seskey)
*r_enckey = NULL;
*r_enckeylen = 0;
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. */

View File

@ -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,

View File

@ -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;

View File

@ -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)
{

View File

@ -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 --*/

View File

@ -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);

View File

@ -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 );

View File

@ -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;

View File

@ -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;

View File

@ -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)
{
PK_LIST pkr;
if ( !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)
{
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");
}
}