aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNIIBE Yutaka <[email protected]>2022-03-30 13:18:20 +0000
committerNIIBE Yutaka <[email protected]>2022-03-30 13:18:20 +0000
commitc9315dada420598a345286cca24dc4d0b3063d8d (patch)
tree113a4ee0c362b182c76b722e66b21d8c51e5c718
parentgpg: Accept SEIPDv2 packet. (diff)
downloadgnupg-c9315dada420598a345286cca24dc4d0b3063d8d.tar.gz
gnupg-c9315dada420598a345286cca24dc4d0b3063d8d.zip
gpg: Accept GCM and v5 AEAD with v2 SEIPD packet.
Signed-off-by: NIIBE Yutaka <[email protected]>
-rw-r--r--common/openpgpdefs.h3
-rw-r--r--g10/decrypt-data.c83
-rw-r--r--g10/mainproc.c130
-rw-r--r--g10/misc.c9
-rw-r--r--g10/packet.h7
-rw-r--r--g10/parse-packet.c17
6 files changed, 151 insertions, 98 deletions
diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h
index 625747983..0696172d7 100644
--- a/common/openpgpdefs.h
+++ b/common/openpgpdefs.h
@@ -152,7 +152,8 @@ typedef enum
{
AEAD_ALGO_NONE = 0,
AEAD_ALGO_EAX = 1,
- AEAD_ALGO_OCB = 2
+ AEAD_ALGO_OCB = 2,
+ AEAD_ALGO_GCM = 3
}
aead_algo_t;
diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c
index 341c8c70b..61be87cd3 100644
--- a/g10/decrypt-data.c
+++ b/g10/decrypt-data.c
@@ -141,6 +141,11 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final)
i = 7;
break;
+ case AEAD_ALGO_GCM:
+ memcpy (nonce, dfx->startiv, 12);
+ i = 4;
+ break;
+
case AEAD_ALGO_EAX:
memcpy (nonce, dfx->startiv, 16);
i = 8;
@@ -223,84 +228,6 @@ aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf)
}
-/* HKDF Extract and expand. */
-static gpg_error_t
-hkdf_derive (const byte *salt, int saltlen, const byte *key, int keylen,
- const byte *info, int infolen, int outlen, byte *out)
-{
- int algo = GCRY_MD_SHA256;
- gcry_md_hd_t hd;
- unsigned char *t;
- unsigned char *p;
- int mdlen;
- gpg_error_t err = 0;
- int off = 0;
- unsigned char n = 0;
-
- mdlen = gcry_md_get_algo_dlen (algo);
- if (salt && saltlen != mdlen)
- return gpg_error (GPG_ERR_INV_LENGTH);
-
- t = xtrymalloc (mdlen);
- if (!t)
- return gpg_error_from_syserror ();
-
- err = gcry_md_open (&hd, algo, GCRY_MD_FLAG_HMAC);
- if (err)
- return err;
-
- if (salt)
- {
- err = gcry_md_setkey (hd, salt, mdlen);
- if (err)
- {
- xfree (t);
- gcry_md_close (hd);
- return err;
- }
- }
-
- if (keylen)
- gcry_md_write (hd, key, keylen);
-
- p = gcry_md_read (hd, 0);
- memcpy (t, p, mdlen);
-
- gcry_md_reset (hd);
- err = gcry_md_setkey (hd, t, mdlen);
- if (err)
- {
- xfree (t);
- gcry_md_close (hd);
- return err;
- }
-
- while (1)
- {
- n++;
- gcry_md_write (hd, info, infolen);
- gcry_md_write (hd, &n, 1);
- p = gcry_md_read (hd, 0);
- memcpy (t, p, mdlen);
- if ((outlen - off) / mdlen)
- {
- memcpy (out + off, t, mdlen);
- off += mdlen;
- }
- else
- {
- memcpy (out + off, t, (outlen - off));
- break;
- }
- gcry_md_reset (hd);
- gcry_md_write (hd, t, mdlen);
- }
-
- xfree (t);
- gcry_md_close (hd);
- return 0;
-}
-
/****************
* Decrypt the data, specified by ED with the key DEK.
*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 082846812..feedfcb88 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -249,14 +249,109 @@ add_signature (CTX c, PACKET *pkt)
return 1;
}
+
+/* HKDF Extract and expand. */
+gpg_error_t
+hkdf_derive (const byte *salt, int saltlen, const byte *key, int keylen,
+ const byte *info, int infolen, int outlen, byte *out)
+{
+ int algo = GCRY_MD_SHA256;
+ gcry_md_hd_t hd;
+ unsigned char *t;
+ unsigned char *p;
+ int mdlen;
+ gpg_error_t err = 0;
+ int off = 0;
+ unsigned char n = 0;
+
+ mdlen = gcry_md_get_algo_dlen (algo);
+ if (salt && saltlen != mdlen)
+ return gpg_error (GPG_ERR_INV_LENGTH);
+
+ t = xtrymalloc (mdlen);
+ if (!t)
+ return gpg_error_from_syserror ();
+
+ err = gcry_md_open (&hd, algo, GCRY_MD_FLAG_HMAC);
+ if (err)
+ return err;
+
+ err = gcry_md_setkey (hd, salt, salt? mdlen: 0);
+ if (err)
+ {
+ xfree (t);
+ gcry_md_close (hd);
+ return err;
+ }
+
+ if (keylen)
+ gcry_md_write (hd, key, keylen);
+
+ p = gcry_md_read (hd, 0);
+ memcpy (t, p, mdlen);
+
+ gcry_md_reset (hd);
+ err = gcry_md_setkey (hd, t, mdlen);
+ if (err)
+ {
+ xfree (t);
+ gcry_md_close (hd);
+ return err;
+ }
+
+ while (1)
+ {
+ n++;
+ gcry_md_write (hd, info, infolen);
+ gcry_md_write (hd, &n, 1);
+ p = gcry_md_read (hd, 0);
+ memcpy (t, p, mdlen);
+ if ((outlen - off) / mdlen)
+ {
+ memcpy (out + off, t, mdlen);
+ off += mdlen;
+ }
+ else
+ {
+ memcpy (out + off, t, (outlen - off));
+ break;
+ }
+ gcry_md_reset (hd);
+ gcry_md_write (hd, t, mdlen);
+ }
+
+ xfree (t);
+ gcry_md_close (hd);
+ return 0;
+}
+
+
static gpg_error_t
-symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
+symkey_decrypt_seskey (DEK *dek, PKT_symkey_enc *enc)
{
gpg_error_t err;
gcry_cipher_hd_t hd;
unsigned int noncelen, keylen;
enum gcry_cipher_modes ciphermode;
+ if (enc->use_hkdf)
+ {
+ unsigned char info[4];
+
+ keylen = openpgp_cipher_get_algo_keylen (enc->cipher_algo);
+ if (keylen > DIM (dek->key))
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ info[0] = 0xc3;
+ info[1] = 0x05;
+ info[2] = enc->cipher_algo;
+ info[3] = enc->aead_algo;
+
+ hkdf_derive (NULL, 0, dek->key, dek->keylen, info, sizeof (info),
+ keylen, dek->key);
+ dek->keylen = keylen;
+ }
+
if (dek->use_aead)
{
err = openpgp_aead_algo_info (dek->use_aead, &ciphermode, &noncelen);
@@ -270,12 +365,12 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
}
/* 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)))
+ if ((dek->use_aead && (enc->seskeylen < (noncelen + 16 + 16)
+ || enc->seskeylen > (noncelen + 32 + 16)))
+ || (!dek->use_aead && (enc->seskeylen < 17 || enc->seskeylen > 33)))
{
log_error ( _("weird size for an encrypted session key (%d)\n"),
- (int)slen);
+ (int)enc->seskeylen);
return gpg_error (GPG_ERR_BAD_KEY);
}
@@ -283,7 +378,7 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
if (!err)
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
if (!err)
- err = gcry_cipher_setiv (hd, noncelen? seskey : NULL, noncelen);
+ err = gcry_cipher_setiv (hd, noncelen? enc->seskey : NULL, noncelen);
if (err)
goto leave;
@@ -299,11 +394,11 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
if (err)
goto leave;
gcry_cipher_final (hd);
- keylen = slen - noncelen - 16;
- err = gcry_cipher_decrypt (hd, seskey+noncelen, keylen, NULL, 0);
+ keylen = enc->seskeylen - noncelen - 16;
+ err = gcry_cipher_decrypt (hd, enc->seskey+noncelen, keylen, NULL, 0);
if (err)
goto leave;
- err = gcry_cipher_checktag (hd, seskey+noncelen+keylen, 16);
+ err = gcry_cipher_checktag (hd, enc->seskey+noncelen+keylen, 16);
if (err)
goto leave;
/* Now we replace the dek components with the real session key to
@@ -314,11 +409,11 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
goto leave;
}
dek->keylen = keylen;
- memcpy (dek->key, seskey + noncelen, dek->keylen);
+ memcpy (dek->key, enc->seskey + noncelen, dek->keylen);
}
else
{
- gcry_cipher_decrypt (hd, seskey, slen, NULL, 0 );
+ gcry_cipher_decrypt (hd, enc->seskey, enc->seskeylen, NULL, 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
@@ -328,8 +423,8 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
* 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)
+ if (openpgp_cipher_test_algo (enc->seskey[0])
+ || openpgp_cipher_get_algo_keylen (enc->seskey[0]) != enc->seskeylen - 1)
{
err = gpg_error (GPG_ERR_CHECKSUM);
goto leave;
@@ -337,15 +432,15 @@ symkey_decrypt_seskey (DEK *dek, byte *seskey, size_t slen)
/* Now we replace the dek components with the real session key to
* decrypt the contents of the sequencing packet. */
- keylen = slen-1;
+ keylen = enc->seskeylen-1;
if (keylen > DIM(dek->key))
{
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
- dek->algo = seskey[0];
+ dek->algo = enc->seskey[0];
dek->keylen = keylen;
- memcpy (dek->key, seskey + 1, dek->keylen);
+ memcpy (dek->key, enc->seskey + 1, dek->keylen);
}
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
@@ -426,8 +521,7 @@ proc_symkey_enc (CTX c, PACKET *pkt)
come later. */
if (enc->seskeylen)
{
- err = symkey_decrypt_seskey (c->dek,
- enc->seskey, enc->seskeylen);
+ err = symkey_decrypt_seskey (c->dek, enc);
if (err)
{
log_info ("decryption of the symmetrically encrypted"
diff --git a/g10/misc.c b/g10/misc.c
index 2a431b137..bd550dec6 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -630,6 +630,7 @@ openpgp_aead_test_algo (aead_algo_t algo)
case AEAD_ALGO_EAX:
case AEAD_ALGO_OCB:
+ case AEAD_ALGO_GCM:
return 0;
}
@@ -648,6 +649,7 @@ openpgp_aead_algo_name (aead_algo_t algo)
case AEAD_ALGO_NONE: break;
case AEAD_ALGO_EAX: return "EAX";
case AEAD_ALGO_OCB: return "OCB";
+ case AEAD_ALGO_GCM: return "GCM";
}
return "?";
@@ -674,6 +676,11 @@ openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode,
*r_noncelen = 16;
break;
+ case AEAD_ALGO_GCM:
+ *r_mode = GCRY_CIPHER_MODE_GCM;
+ *r_noncelen = 12;
+ break;
+
default:
log_error ("unsupported AEAD algo %d\n", algo);
return gpg_error (GPG_ERR_INV_CIPHER_MODE);
@@ -1241,6 +1248,8 @@ string_to_aead_algo (const char *string)
result = 1;
else if (!ascii_strcasecmp (string, "OCB"))
result = 2;
+ else if (!ascii_strcasecmp (string, "GCM"))
+ result = 3;
else if ((string[0]=='A' || string[0]=='a'))
{
char *endptr;
diff --git a/g10/packet.h b/g10/packet.h
index 8f69a247a..2301107b2 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -115,6 +115,8 @@ typedef struct
typedef struct {
/* We support version 4 (rfc4880) and 5 (rfc4880bis). */
byte version;
+ /* If we use hkdf or not. */
+ byte use_hkdf;
/* The cipher algorithm used to encrypt the session key. Note that
* this may be different from the algorithm that is used to encrypt
* bulk data. */
@@ -646,6 +648,9 @@ int list_packets( iobuf_t a );
const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len);
char *issuer_fpr_string (PKT_signature *sig);
+gpg_error_t hkdf_derive (const byte *salt, int saltlen, const byte *key,
+ int keylen, const byte *info, int infolen,
+ int outlen, byte *out);
/*-- parse-packet.c --*/
@@ -928,7 +933,7 @@ gpg_error_t get_override_session_key (DEK *dek, const char *string);
int handle_compressed (ctrl_t ctrl, void *ctx, PKT_compressed *cd,
int (*callback)(iobuf_t, void *), void *passthru );
-/*-- encr-data.c --*/
+/*-- decrypt-data.c --*/
int decrypt_data (ctrl_t ctrl, void *ctx, PKT_encrypted *ed, DEK *dek );
/*-- plaintext.c --*/
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index 2f4f003ad..230eb52b5 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1203,6 +1203,8 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
PKT_symkey_enc *k;
int rc = 0;
int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen;
+ int v5_cr05_five_field_len = 0;
+ int v5_cr05_s2k_len = 0;
if (pktlen < 4)
goto too_short;
@@ -1232,8 +1234,22 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
pktlen--;
if (version == 5)
{
+ if (cipher_algo > 13)
+ {
+ v5_cr05_five_field_len = cipher_algo;
+ cipher_algo = iobuf_get_noeof (inp);
+ pktlen--;
+ }
+
aead_algo = iobuf_get_noeof (inp);
pktlen--;
+ if (v5_cr05_five_field_len)
+ {
+ if (pktlen < 1)
+ goto too_short;
+ v5_cr05_s2k_len = iobuf_get_noeof (inp);
+ pktlen--;
+ }
}
else
aead_algo = 0;
@@ -1280,6 +1296,7 @@ parse_symkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
k = packet->pkt.symkey_enc = xmalloc_clear (sizeof *packet->pkt.symkey_enc
+ seskeylen - 1);
k->version = version;
+ k->use_hkdf = (v5_cr05_five_field_len != 0);
k->cipher_algo = cipher_algo;
k->aead_algo = aead_algo;
k->s2k.mode = s2kmode;