mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-03 22:56:33 +02:00
gpg: Support decryption of the new AEAD packet
* common/openpgpdefs.h (aead_algo_t): New.
(pkttype_t): Add PKT_ENCRYPTED_AEAD.
* g10/decrypt-data.c (struct decode_filter_context_s): Add fields for
AEAD.
(aead_set_nonce_and_ad): New.
(aead_checktag): New.
(decrypt_data): Support AEAD.
(aead_underflow): New.
(aead_decode_filter): New.
* g10/dek.h (DEK): Add field use_aead. Turn use_mdc,
algo_info_printed, and symmetric into bit flags.
* g10/mainproc.c (struct mainproc_context): Add field
seen_pkt_encrypted_aead.
(release_list): Clear it.
(have_seen_pkt_encrypted_aead): New.
(symkey_decrypt_seskey): Support AEAD.
(proc_symkey_enc): Ditto.
(proc_encrypted): Ditto.
(proc_plaintext): Ditto.
* g10/misc.c (MY_GCRY_CIPHER_MODE_EAX): New.
(openpgp_aead_test_algo): New.
(openpgp_aead_algo_name): New.
(openpgp_aead_algo_info): New.
* g10/packet.h (PKT_symkey_enc): Add field use_aead.
(PKT_user_id): Add field flags.aead
(PKT_public_key): Ditto.
(PKT_encrypted): Add fields for AEAD.
* g10/parse-packet.c (parse): Handle PKT_ENCRYPTED_AEAD.
(parse_symkeyenc): Support AEAD.
(parse_encrypted): Ditto.
(dump_sig_subpkt): Dump AEAD preference packet.
(parse_encrypted_aead): New.
--
This patch allows to decrypt data encrypted using the new AEAD
mechanism as specified in rfc4880bis. Although preferences are used
to enable this new mode, it is useful to have at least a decryption
option in case a user switches between GnuPG 2.2 and newer versions.
The new AEAD mechanism is much faster than the current CFB+MDC and
thus 2.2 will allow faster decryption of symmetric only decryption.
This patch is based on the current master (2.3) code base and includes
a few other patches. In particular
commit 44be675b75
(gpg: More check for symmetric key encryption.)
is included.
Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
144b95cc9d
commit
1dfe71c62b
10 changed files with 907 additions and 68 deletions
184
g10/mainproc.c
184
g10/mainproc.c
|
@ -97,6 +97,7 @@ struct mainproc_context
|
|||
int trustletter; /* Temporary usage in list_node. */
|
||||
ulong symkeys; /* Number of symmetrically encrypted session keys. */
|
||||
struct kidlist_item *pkenc_list; /* List of encryption packets. */
|
||||
int seen_pkt_encrypted_aead; /* PKT_ENCRYPTED_AEAD packet seen. */
|
||||
struct {
|
||||
unsigned int sig_seen:1; /* Set to true if a signature packet
|
||||
has been seen. */
|
||||
|
@ -145,6 +146,7 @@ release_list( CTX c )
|
|||
c->any.data = 0;
|
||||
c->any.uncompress_failed = 0;
|
||||
c->last_was_session_key = 0;
|
||||
c->seen_pkt_encrypted_aead = 0;
|
||||
xfree (c->dek);
|
||||
c->dek = NULL;
|
||||
}
|
||||
|
@ -252,47 +254,111 @@ add_signature (CTX c, PACKET *pkt)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
static gpg_error_t
|
||||
symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
|
||||
{
|
||||
gpg_error_t err;
|
||||
gcry_cipher_hd_t hd;
|
||||
enum gcry_cipher_modes ciphermode;
|
||||
unsigned int noncelen, keylen;
|
||||
|
||||
if(slen < 17 || slen > 33)
|
||||
if (dek->use_aead)
|
||||
{
|
||||
err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
else
|
||||
{
|
||||
ciphermode = GCRY_CIPHER_MODE_CFB;
|
||||
noncelen = 0;
|
||||
}
|
||||
|
||||
/* Check that the session key has a size of 16 to 32 bytes. */
|
||||
if ((dek->use_aead && (slen < (noncelen + 16 + 16)
|
||||
|| slen > (noncelen + 32 + 16)))
|
||||
|| (!dek->use_aead && (slen < 17 || slen > 33)))
|
||||
{
|
||||
log_error ( _("weird size for an encrypted session key (%d)\n"),
|
||||
(int)slen);
|
||||
return GPG_ERR_BAD_KEY;
|
||||
return gpg_error (GPG_ERR_BAD_KEY);
|
||||
}
|
||||
|
||||
if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
|
||||
BUG ();
|
||||
if (gcry_cipher_setkey ( hd, dek->key, dek->keylen ))
|
||||
BUG ();
|
||||
gcry_cipher_setiv ( hd, NULL, 0 );
|
||||
gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 );
|
||||
gcry_cipher_close ( hd );
|
||||
err = openpgp_cipher_open (&hd, dek->algo, ciphermode, 1);
|
||||
if (!err)
|
||||
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
|
||||
if (!err)
|
||||
err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Here we can only test whether the algo given in decrypted
|
||||
* session key is a valid OpenPGP algo. With 11 defined
|
||||
* symmetric algorithms we will miss 4.3% of wrong passphrases
|
||||
* here. The actual checking is done later during bulk
|
||||
* decryption; we can't bring this check forward easily. */
|
||||
if (openpgp_cipher_test_algo (seskey[0]))
|
||||
return gpg_error (GPG_ERR_BAD_KEY);
|
||||
if (dek->use_aead)
|
||||
{
|
||||
byte ad[4];
|
||||
|
||||
/* Now we replace the dek components with the real session key to
|
||||
decrypt the contents of the sequencing packet. */
|
||||
ad[0] = (0xc0 | PKT_SYMKEY_ENC);
|
||||
ad[1] = 5;
|
||||
ad[2] = dek->algo;
|
||||
ad[3] = dek->use_aead;
|
||||
err = gcry_cipher_authenticate (hd, ad, 4);
|
||||
if (err)
|
||||
goto leave;
|
||||
gcry_cipher_final (hd);
|
||||
keylen = slen - noncelen - 16;
|
||||
err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
|
||||
if (err)
|
||||
goto leave;
|
||||
/* Now we replace the dek components with the real session key to
|
||||
* decrypt the contents of the sequencing packet. */
|
||||
if (keylen > DIM(dek->key))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_TOO_LARGE);
|
||||
goto leave;
|
||||
}
|
||||
dek->keylen = keylen;
|
||||
memcpy (dek->key, seskey + noncelen, dek->keylen);
|
||||
}
|
||||
else
|
||||
{
|
||||
gcry_cipher_decrypt (hd, seskey, slen, NULL, 0);
|
||||
|
||||
dek->keylen=slen-1;
|
||||
dek->algo=seskey[0];
|
||||
/* Here we can only test whether the algo given in decrypted
|
||||
* session key is a valid OpenPGP algo. With 11 defined
|
||||
* symmetric algorithms we will miss 4.3% of wrong passphrases
|
||||
* here. The actual checking is done later during bulk
|
||||
* decryption; we can't bring this check forward easily. We
|
||||
* need to use the GPG_ERR_CHECKSUM so that we won't run into
|
||||
* the gnupg < 2.2 bug compatible case which would terminate the
|
||||
* process on GPG_ERR_CIPHER_ALGO. Note that with AEAD (above)
|
||||
* we will have a reliable test here. */
|
||||
if (openpgp_cipher_test_algo (seskey[0])
|
||||
|| openpgp_cipher_get_algo_keylen (seskey[0]) != slen - 1)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_CHECKSUM);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if(dek->keylen > DIM(dek->key))
|
||||
BUG ();
|
||||
|
||||
memcpy(dek->key, seskey + 1, dek->keylen);
|
||||
/* Now we replace the dek components with the real session key to
|
||||
* decrypt the contents of the sequencing packet. */
|
||||
keylen = slen-1;
|
||||
if (keylen > DIM(dek->key))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_TOO_LARGE);
|
||||
goto leave;
|
||||
}
|
||||
dek->algo = seskey[0];
|
||||
dek->keylen = slen-1;
|
||||
memcpy (dek->key, seskey + 1, dek->keylen);
|
||||
}
|
||||
|
||||
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
|
||||
|
||||
leave:
|
||||
gcry_cipher_close (hd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -300,6 +366,7 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
|
|||
static void
|
||||
proc_symkey_enc (CTX c, PACKET *pkt)
|
||||
{
|
||||
gpg_error_t err;
|
||||
PKT_symkey_enc *enc;
|
||||
|
||||
enc = pkt->pkt.symkey_enc;
|
||||
|
@ -309,15 +376,20 @@ proc_symkey_enc (CTX c, PACKET *pkt)
|
|||
{
|
||||
int algo = enc->cipher_algo;
|
||||
const char *s = openpgp_cipher_algo_name (algo);
|
||||
const char *a = (enc->aead_algo ? openpgp_aead_algo_name (enc->aead_algo)
|
||||
/**/ : "CFB");
|
||||
|
||||
if (!openpgp_cipher_test_algo (algo))
|
||||
{
|
||||
if (!opt.quiet)
|
||||
{
|
||||
/* Note: TMPSTR is only used to avoid i18n changes. */
|
||||
char *tmpstr = xstrconcat (s, ".", a, NULL);
|
||||
if (enc->seskeylen)
|
||||
log_info (_("%s encrypted session key\n"), s );
|
||||
log_info (_("%s encrypted session key\n"), tmpstr);
|
||||
else
|
||||
log_info (_("%s encrypted data\n"), s );
|
||||
log_info (_("%s encrypted data\n"), tmpstr);
|
||||
xfree (tmpstr);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -349,6 +421,7 @@ proc_symkey_enc (CTX c, PACKET *pkt)
|
|||
if (c->dek)
|
||||
{
|
||||
c->dek->symmetric = 1;
|
||||
c->dek->use_aead = enc->aead_algo;
|
||||
|
||||
/* FIXME: This doesn't work perfectly if a symmetric key
|
||||
comes before a public key in the message - if the
|
||||
|
@ -359,9 +432,16 @@ proc_symkey_enc (CTX c, PACKET *pkt)
|
|||
come later. */
|
||||
if (enc->seskeylen)
|
||||
{
|
||||
if (symkey_decrypt_seskey (c->dek,
|
||||
enc->seskey, enc->seskeylen))
|
||||
err = symkey_decrypt_seskey (c->dek,
|
||||
enc->seskey, enc->seskeylen);
|
||||
if (err)
|
||||
{
|
||||
log_info ("decryption of the symmetrically encrypted"
|
||||
" session key failed: %s\n",
|
||||
gpg_strerror (err));
|
||||
if (gpg_err_code (err) != GPG_ERR_BAD_KEY
|
||||
&& gpg_err_code (err) != GPG_ERR_CHECKSUM)
|
||||
log_fatal ("process terminated to be bug compatible\n");
|
||||
if (c->dek->s2k_cacheid[0])
|
||||
{
|
||||
if (opt.debug)
|
||||
|
@ -550,6 +630,9 @@ proc_encrypted (CTX c, PACKET *pkt)
|
|||
int result = 0;
|
||||
int early_plaintext = literals_seen;
|
||||
|
||||
if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
|
||||
c->seen_pkt_encrypted_aead = 1;
|
||||
|
||||
if (early_plaintext)
|
||||
{
|
||||
log_info (_("WARNING: multiple plaintexts seen\n"));
|
||||
|
@ -683,7 +766,8 @@ proc_encrypted (CTX c, PACKET *pkt)
|
|||
;
|
||||
else if (!result
|
||||
&& !opt.ignore_mdc_error
|
||||
&& !pkt->pkt.encrypted->mdc_method)
|
||||
&& !pkt->pkt.encrypted->mdc_method
|
||||
&& !pkt->pkt.encrypted->aead_algo)
|
||||
{
|
||||
/* The message has been decrypted but does not carry an MDC.
|
||||
* The option --ignore-mdc-error has also not been used. To
|
||||
|
@ -712,17 +796,25 @@ proc_encrypted (CTX c, PACKET *pkt)
|
|||
write_status (STATUS_DECRYPTION_FAILED);
|
||||
}
|
||||
else if (!result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
|
||||
&& !pkt->pkt.encrypted->aead_algo
|
||||
&& opt.ignore_mdc_error))
|
||||
{
|
||||
/* All is fine or for an MDC message the MDC failed but the
|
||||
* --ignore-mdc-error option is active. For compatibility
|
||||
* reasons we issue GOODMDC also for AEAD messages. */
|
||||
write_status (STATUS_DECRYPTION_OKAY);
|
||||
if (opt.verbose > 1)
|
||||
log_info(_("decryption okay\n"));
|
||||
if (pkt->pkt.encrypted->mdc_method && !result)
|
||||
|
||||
if (pkt->pkt.encrypted->aead_algo)
|
||||
write_status (STATUS_GOODMDC);
|
||||
else if (pkt->pkt.encrypted->mdc_method && !result)
|
||||
write_status (STATUS_GOODMDC);
|
||||
else
|
||||
log_info (_("WARNING: message was not integrity protected\n"));
|
||||
}
|
||||
else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE)
|
||||
else if (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
|
||||
|| gpg_err_code (result) == GPG_ERR_TRUNCATED)
|
||||
{
|
||||
glo_ctrl.lasterr = result;
|
||||
log_error (_("WARNING: encrypted message has been manipulated!\n"));
|
||||
|
@ -732,6 +824,7 @@ proc_encrypted (CTX c, PACKET *pkt)
|
|||
else
|
||||
{
|
||||
if ((gpg_err_code (result) == GPG_ERR_BAD_KEY
|
||||
|| gpg_err_code (result) == GPG_ERR_CHECKSUM
|
||||
|| gpg_err_code (result) == GPG_ERR_CIPHER_ALGO)
|
||||
&& *c->dek->s2k_cacheid != '\0')
|
||||
{
|
||||
|
@ -761,6 +854,21 @@ proc_encrypted (CTX c, PACKET *pkt)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
have_seen_pkt_encrypted_aead( CTX c )
|
||||
{
|
||||
CTX cc;
|
||||
|
||||
for (cc = c; cc; cc = cc->anchor)
|
||||
{
|
||||
if (cc->seen_pkt_encrypted_aead)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
proc_plaintext( CTX c, PACKET *pkt )
|
||||
{
|
||||
|
@ -836,7 +944,7 @@ proc_plaintext( CTX c, PACKET *pkt )
|
|||
}
|
||||
}
|
||||
|
||||
if (!any && !opt.skip_verify)
|
||||
if (!any && !opt.skip_verify && !have_seen_pkt_encrypted_aead(c))
|
||||
{
|
||||
/* This is for the old GPG LITERAL+SIG case. It's not legal
|
||||
according to 2440, so hopefully it won't come up that often.
|
||||
|
@ -1467,7 +1575,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
|
|||
case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
|
||||
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
|
||||
case PKT_ENCRYPTED:
|
||||
case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
|
||||
case PKT_ENCRYPTED_MDC:
|
||||
case PKT_ENCRYPTED_AEAD:proc_encrypted (c, pkt); break;
|
||||
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
|
||||
default: newpkt = 0; break;
|
||||
}
|
||||
|
@ -1483,6 +1592,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
|
|||
case PKT_PUBKEY_ENC:
|
||||
case PKT_ENCRYPTED:
|
||||
case PKT_ENCRYPTED_MDC:
|
||||
case PKT_ENCRYPTED_AEAD:
|
||||
write_status_text( STATUS_UNEXPECTED, "0" );
|
||||
rc = GPG_ERR_UNEXPECTED;
|
||||
goto leave;
|
||||
|
@ -1510,7 +1620,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
|
|||
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
|
||||
case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
|
||||
case PKT_ENCRYPTED:
|
||||
case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
|
||||
case PKT_ENCRYPTED_MDC:
|
||||
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
|
||||
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
|
||||
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
|
||||
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
|
||||
|
@ -1537,7 +1648,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
|
|||
case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
|
||||
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
|
||||
case PKT_ENCRYPTED:
|
||||
case PKT_ENCRYPTED_MDC: proc_encrypted (c, pkt); break;
|
||||
case PKT_ENCRYPTED_MDC:
|
||||
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
|
||||
case PKT_PLAINTEXT: proc_plaintext (c, pkt); break;
|
||||
case PKT_COMPRESSED: rc = proc_compressed (c, pkt); break;
|
||||
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig (c, pkt); break;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue