summaryrefslogtreecommitdiffstats
path: root/g10/encrypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/encrypt.c')
-rw-r--r--g10/encrypt.c390
1 files changed, 297 insertions, 93 deletions
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 5f9480f..a4863fa 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -1,7 +1,7 @@
/* encrypt.c - Main encryption driver
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2009 Free Software Foundation, Inc.
- * Copyright (C) 2016, 2022 g10 Code GmbH
+ * Copyright (C) 2016, 2022, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -17,6 +17,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
@@ -52,7 +53,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 +127,169 @@ 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;
+}
+
+
+/* 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.compat_flags & COMPAT_VSD_ALLOW_OCB)
+ && opt.compliance == CO_DE_VS)
+ return 0; /* Not yet allowed. */
+
+ 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;
+ }
- 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;
+
+ /* 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 +321,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;
@@ -206,6 +331,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
text_filter_context_t tfx;
progress_filter_context_t *pfx;
int do_compress = !!default_compress_algo();
+ char peekbuf[32];
+ int peekbuflen;
if (!gnupg_rng_is_compliant (opt.compliance))
{
@@ -242,6 +369,14 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
return rc;
}
+ peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
+ if (peekbuflen < 0)
+ {
+ peekbuflen = 0;
+ if (DBG_FILTER)
+ log_debug ("peeking at input failed\n");
+ }
+
handle_progress (pfx, inp, filename);
if (opt.textmode)
@@ -250,6 +385,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)
{
@@ -265,31 +402,50 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if (use_seskey && s2k->mode != 1 && s2k->mode != 3)
{
use_seskey = 0;
- log_info (_("can't use a symmetric ESK packet "
- "due to the S2K mode\n"));
+ log_info (_("can't use a SKESK packet 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
- && is_file_compressed(filename, &rc))
+ if (do_compress
+ && cfx.dek
+ && (cfx.dek->use_mdc || cfx.dek->use_aead)
+ && !opt.explicit_compress_option
+ && is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
- log_info(_("'%s' already compressed\n"), filename);
+ log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
@@ -310,20 +466,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)
@@ -341,12 +500,12 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
- off_t tmpsize;
- int overflow;
+ uint64_t tmpsize;
- if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
- && !overflow && opt.verbose)
+ tmpsize = iobuf_get_filelength(inp);
+ if (!tmpsize && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
+
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the
size of a file is larger than 2^32 minus some bytes for
@@ -380,12 +539,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 +562,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 +586,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 +639,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 +673,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;
}
@@ -611,6 +784,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
progress_filter_context_t *pfx;
PK_LIST pk_list;
int do_compress;
+ char peekbuf[32];
+ int peekbuflen;
if (filefd != -1 && filename)
return gpg_error (GPG_ERR_INV_ARG); /* Both given. */
@@ -683,6 +858,14 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (opt.verbose)
log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));
+ peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
+ if (peekbuflen < 0)
+ {
+ peekbuflen = 0;
+ if (DBG_FILTER)
+ log_debug ("peeking at input failed\n");
+ }
+
handle_progress (pfx, inp, filename);
if (opt.textmode)
@@ -706,17 +889,22 @@ 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)
+ && !opt.explicit_compress_option
+ && is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
- log_info(_("'%s' already compressed\n"), filename);
+ log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0;
}
if (rc2)
@@ -737,7 +925,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)
@@ -747,11 +936,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (filename && *filename
&& !iobuf_is_pipe_filename (filename) && !opt.textmode )
{
- off_t tmpsize;
- int overflow;
+ uint64_t tmpsize;
- if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
- && !overflow && opt.verbose)
+ tmpsize = iobuf_get_filelength (inp);
+ if (!tmpsize && opt.verbose)
log_info(_("WARNING: '%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the size
@@ -780,7 +968,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 +1080,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 +1095,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);
@@ -967,9 +1163,17 @@ 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"),
+ if ((pk->pubkey_usage & PUBKEY_USAGE_RENC))
+ {
+ char *tmpustr = xstrconcat (ustr, " [ADSK]", NULL);
+ xfree (ustr);
+ ustr = tmpustr;
+ }
+ 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);
}