From b722fd755c77cbba12478f6de8913c73213d78ee Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 2 Jun 2021 11:03:55 +0200 Subject: [PATCH] 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: 4980fb3c6dde8c1dda975e8a36d6086c8456a631 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 --- common/compliance.c | 3 +- sm/decrypt.c | 82 ++++++++++++++++++++++++++++++++++++++++++--- sm/gpgsm.c | 12 +++++-- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/common/compliance.c b/common/compliance.c index f62f2f77d..1cda1ec16 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -418,7 +418,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, || mode == GCRY_CIPHER_MODE_CFB); case GNUPG_MODULE_NAME_GPGSM: 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"); diff --git a/sm/decrypt.c b/sm/decrypt.c index beb236ad8..38aa83210 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -38,6 +38,15 @@ #include "../common/compliance.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 { int algo; @@ -555,6 +564,14 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, 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: xfree (seskey); 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. */ int @@ -972,9 +1019,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) else { /* setup the bulk decrypter */ any_key = 1; - ksba_writer_set_filter (writer, - decrypt_filter, - &dfparm); + ksba_writer_set_filter + (writer, + dfparm.mode == GCRY_CIPHER_MODE_GCM? + decrypt_gcm_filter : decrypt_filter, + &dfparm); if (dfparm.is_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) { 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 */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; 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); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index d91379da2..3e4f67f4d 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1825,16 +1825,22 @@ main ( int argc, char **argv) case aDecrypt: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); + gpg_error_t err; set_binary (stdin); if (!argc) - gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ + err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ else if (argc == 1) - gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ + err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ else 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;