diff options
author | NIIBE Yutaka <[email protected]> | 2022-03-30 13:18:20 +0000 |
---|---|---|
committer | NIIBE Yutaka <[email protected]> | 2022-03-30 13:18:20 +0000 |
commit | c9315dada420598a345286cca24dc4d0b3063d8d (patch) | |
tree | 113a4ee0c362b182c76b722e66b21d8c51e5c718 | |
parent | gpg: Accept SEIPDv2 packet. (diff) | |
download | gnupg-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.h | 3 | ||||
-rw-r--r-- | g10/decrypt-data.c | 83 | ||||
-rw-r--r-- | g10/mainproc.c | 130 | ||||
-rw-r--r-- | g10/misc.c | 9 | ||||
-rw-r--r-- | g10/packet.h | 7 | ||||
-rw-r--r-- | g10/parse-packet.c | 17 |
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; |