sm: Support AES-GCM decryption.

* sm/gpgsm.c (main): Use gpgrt_fcancel on decryption error if gpgrt
supports this.
* sm/decrypt.c (decrypt_gcm_filter): New.
(gpgsm_decrypt): Use this filter if requested.  Check authtag.
* common/compliance.c (gnupg_cipher_is_allowed): Allow GCM for gpgsm
in consumer (decrypt) de-vs mode.
--

Backported-from-master: 4980fb3c6d

We allow GCM in de-vs mode for decryption although this has not been
evaluation.  It is decryption and thus no serious harm may happen.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2021-06-02 11:03:55 +02:00
parent c8f0b02936
commit b722fd755c
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 89 additions and 8 deletions

View File

@ -418,7 +418,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer,
|| mode == GCRY_CIPHER_MODE_CFB); || mode == GCRY_CIPHER_MODE_CFB);
case GNUPG_MODULE_NAME_GPGSM: case GNUPG_MODULE_NAME_GPGSM:
return (mode == GCRY_CIPHER_MODE_NONE return (mode == GCRY_CIPHER_MODE_NONE
|| mode == GCRY_CIPHER_MODE_CBC); || mode == GCRY_CIPHER_MODE_CBC
|| (mode == GCRY_CIPHER_MODE_GCM && !producer));
} }
log_assert (!"reached"); log_assert (!"reached");

View File

@ -38,6 +38,15 @@
#include "../common/compliance.h" #include "../common/compliance.h"
#include "../common/tlv.h" #include "../common/tlv.h"
/* We can provide an enum value which is only availabale with KSBA
* 1.6.0 so that we can compile even against older versions. Some
* calls will of course return an error in this case. This value is
* currently not used because the cipher mode is sufficient here. */
/* #if KSBA_VERSION_NUMBER < 0x010600 /\* 1.6.0 *\/ */
/* # define KSBA_CT_AUTHENVELOPED_DATA 10 */
/* #endif */
struct decrypt_filter_parm_s struct decrypt_filter_parm_s
{ {
int algo; int algo;
@ -555,6 +564,14 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
goto leave; goto leave;
} }
if (parm->mode == GCRY_CIPHER_MODE_GCM)
{
/* GCM mode really sucks in CMS. We need to know the AAD before
* we start decrypting but CMS puts the AAD after the content.
* Thus temporary files are required. Let's hope that no real
* messages with actual AAD are ever used. OCB Rules! */
}
leave: leave:
xfree (seskey); xfree (seskey);
return rc; return rc;
@ -654,6 +671,36 @@ decrypt_filter (void *arg,
} }
/* This is the GCM version of decrypt_filter. */
static gpg_error_t
decrypt_gcm_filter (void *arg,
const void *inbuf, size_t inlen, size_t *inused,
void *outbuf, size_t maxoutlen, size_t *outlen)
{
struct decrypt_filter_parm_s *parm = arg;
if (!inlen)
return gpg_error (GPG_ERR_BUG);
if (maxoutlen < parm->blklen)
return gpg_error (GPG_ERR_BUG);
if (inlen > maxoutlen)
inlen = maxoutlen;
*inused = inlen;
if (inlen)
{
gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen);
*outlen = inlen;
parm->any_data = 1;
}
else
*outlen = 0;
return 0;
}
/* Perform a decrypt operation. */ /* Perform a decrypt operation. */
int int
@ -972,9 +1019,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
else else
{ /* setup the bulk decrypter */ { /* setup the bulk decrypter */
any_key = 1; any_key = 1;
ksba_writer_set_filter (writer, ksba_writer_set_filter
decrypt_filter, (writer,
&dfparm); dfparm.mode == GCRY_CIPHER_MODE_GCM?
decrypt_gcm_filter : decrypt_filter,
&dfparm);
if (dfparm.is_de_vs if (dfparm.is_de_vs
&& gnupg_gcrypt_is_compliant (CO_DE_VS)) && gnupg_gcrypt_is_compliant (CO_DE_VS))
@ -1028,7 +1077,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
else if (stopreason == KSBA_SR_END_DATA) else if (stopreason == KSBA_SR_END_DATA)
{ {
ksba_writer_set_filter (writer, NULL, NULL); ksba_writer_set_filter (writer, NULL, NULL);
if (dfparm.any_data) if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
/* Nothing yet to do. We wait for the ready event. */
}
else if (dfparm.any_data )
{ /* write the last block with padding removed */ { /* write the last block with padding removed */
int i, npadding = dfparm.lastblock[dfparm.blklen-1]; int i, npadding = dfparm.lastblock[dfparm.blklen-1];
if (!npadding || npadding > dfparm.blklen) if (!npadding || npadding > dfparm.blklen)
@ -1054,7 +1107,28 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
} }
} }
} }
else if (stopreason == KSBA_SR_READY)
{
if (dfparm.mode == GCRY_CIPHER_MODE_GCM)
{
char *authtag;
size_t authtaglen;
rc = ksba_cms_get_message_digest (cms, 0, &authtag, &authtaglen);
if (rc)
{
log_error ("error getting authtag: %s\n", gpg_strerror (rc));
goto leave;
}
if (DBG_CRYPTO)
log_printhex (authtag, authtaglen, "Authtag ...:");
rc = gcry_cipher_checktag (dfparm.hd, authtag, authtaglen);
xfree (authtag);
if (rc)
log_error ("data is not authentic: %s\n", gpg_strerror (rc));
goto leave;
}
}
} }
while (stopreason != KSBA_SR_READY); while (stopreason != KSBA_SR_READY);

View File

@ -1825,16 +1825,22 @@ main ( int argc, char **argv)
case aDecrypt: case aDecrypt:
{ {
estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
gpg_error_t err;
set_binary (stdin); set_binary (stdin);
if (!argc) if (!argc)
gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */
else if (argc == 1) else if (argc == 1)
gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */
else else
wrong_args ("--decrypt [filename]"); wrong_args ("--decrypt [filename]");
es_fclose (fp); #if GPGRT_VERSION_NUMBER >= 0x012700 /* 1.39 */
if (err)
gpgrt_fcancel (fp);
else
#endif
es_fclose (fp);
} }
break; break;