diff options
author | NIIBE Yutaka <[email protected]> | 2022-02-25 12:50:32 +0000 |
---|---|---|
committer | NIIBE Yutaka <[email protected]> | 2022-02-25 12:50:32 +0000 |
commit | 51fe2667052ff9e838b57ffab08a05923c8c2988 (patch) | |
tree | cb3e5f5cd650c7ce6d3f8bcfe586eb8b35820d4d | |
parent | Fix the semantics of memory_cost. (diff) | |
download | gnupg-51fe2667052ff9e838b57ffab08a05923c8c2988.tar.gz gnupg-51fe2667052ff9e838b57ffab08a05923c8c2988.zip |
gpg: Accept SEIPDv2 packet.
Signed-off-by: NIIBE Yutaka <[email protected]>
-rw-r--r-- | g10/decrypt-data.c | 233 | ||||
-rw-r--r-- | g10/parse-packet.c | 18 |
2 files changed, 228 insertions, 23 deletions
diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 8140de6dd..341c8c70b 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -78,6 +78,9 @@ struct decode_filter_context_s /* The AEAD algo. */ byte aead_algo; + /* The Packet Type. */ + byte pkttype; + /* The encoded chunk byte for AEAD. */ byte chunkbyte; @@ -161,33 +164,40 @@ aead_set_nonce_and_ad (decode_filter_ctx_t dfx, int final) if (err) return err; - ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); - ad[1] = 1; + ad[0] = (0xc0 | dfx->pkttype); + ad[1] = dfx->pkttype == PKT_ENCRYPTED_AEAD? 1 : 2; ad[2] = dfx->cipher_algo; ad[3] = dfx->aead_algo; ad[4] = dfx->chunkbyte; - ad[5] = dfx->chunkindex >> 56; - ad[6] = dfx->chunkindex >> 48; - ad[7] = dfx->chunkindex >> 40; - ad[8] = dfx->chunkindex >> 32; - ad[9] = dfx->chunkindex >> 24; - ad[10]= dfx->chunkindex >> 16; - ad[11]= dfx->chunkindex >> 8; - ad[12]= dfx->chunkindex; + if (dfx->pkttype == PKT_ENCRYPTED_AEAD) + { + ad[5] = dfx->chunkindex >> 56; + ad[6] = dfx->chunkindex >> 48; + ad[7] = dfx->chunkindex >> 40; + ad[8] = dfx->chunkindex >> 32; + ad[9] = dfx->chunkindex >> 24; + ad[10]= dfx->chunkindex >> 16; + ad[11]= dfx->chunkindex >> 8; + ad[12]= dfx->chunkindex; + i = 13; + } + else + i = 5; if (final) { - ad[13] = dfx->total >> 56; - ad[14] = dfx->total >> 48; - ad[15] = dfx->total >> 40; - ad[16] = dfx->total >> 32; - ad[17] = dfx->total >> 24; - ad[18] = dfx->total >> 16; - ad[19] = dfx->total >> 8; - ad[20] = dfx->total; + ad[i++] = dfx->total >> 56; + ad[i++] = dfx->total >> 48; + ad[i++] = dfx->total >> 40; + ad[i++] = dfx->total >> 32; + ad[i++] = dfx->total >> 24; + ad[i++] = dfx->total >> 16; + ad[i++] = dfx->total >> 8; + ad[i++] = dfx->total; } + if (DBG_CRYPTO) log_printhex (ad, final? 21 : 13, "authdata:"); - return gcry_cipher_authenticate (dfx->cipher_hd, ad, final? 21 : 13); + return gcry_cipher_authenticate (dfx->cipher_hd, ad, i); } @@ -213,6 +223,84 @@ 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. */ @@ -295,8 +383,10 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) if ( !blocksize || blocksize > 16 ) log_fatal ("unsupported blocksize %u\n", blocksize ); - if (ed->aead_algo) + if (ed->aead_algo && ed->mdc_method == 0) { + dfx->pkttype = PKT_ENCRYPTED_AEAD; + if (blocksize != 16) { rc = gpg_error (GPG_ERR_CIPHER_ALGO); @@ -375,6 +465,107 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) } } + else if (ed->aead_algo && ed->mdc_method) + { /* SEIPDv2 */ + unsigned char info[5]; + int keylen; + int outlen; + unsigned char *out = NULL; + + dfx->pkttype = PKT_ENCRYPTED_MDC; + + info[0] = (0xc0 | dfx->pkttype); + info[1] = 0x02; + info[2] = ed->cipher_algo; + info[3] = ed->aead_algo; + info[4] = ed->chunkbyte; + + if (blocksize != 16) + { + rc = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + + if (ed->chunkbyte > 16) + { + log_error ("invalid AEAD chunkbyte %u\n", ed->chunkbyte); + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + /* Read the salt. */ + for (i=0; i < 32 && ed->len; i++, ed->len--) + if ((c=iobuf_get (ed->buf)) == -1) + break; + else + temp[i] = c; + if (i != 32) + { + log_error ("Salt in SEIPDv2 packet too short (%d/%u)\n", + i, 32); + rc = gpg_error (GPG_ERR_TOO_SHORT); + goto leave; + } + + keylen = openpgp_cipher_get_algo_keylen (ed->cipher_algo); + outlen = startivlen - 8 + keylen; + out = xtrymalloc (outlen); + if (!out) + { + rc = gpg_error_from_syserror (); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (dek->key, dek->keylen, "the session key:"); + + hkdf_derive (temp, 32, dek->key, dek->keylen, + info, sizeof (info), outlen, out); + + dfx->cipher_algo = ed->cipher_algo; + dfx->aead_algo = ed->aead_algo; + dfx->chunkbyte = ed->chunkbyte; + dfx->chunksize = (uint64_t)1 << (dfx->chunkbyte + 6); + memcpy (dfx->startiv, out+keylen, startivlen-8); + memset (dfx->startiv+startivlen-8, 0, 8); + + if (dek->algo != dfx->cipher_algo) + log_info ("Note: different cipher algorithms used (%s/%s)\n", + openpgp_cipher_algo_name (dek->algo), + openpgp_cipher_algo_name (dfx->cipher_algo)); + + rc = openpgp_cipher_open (&dfx->cipher_hd, + dfx->cipher_algo, + ciphermode, + GCRY_CIPHER_SECURE); + if (rc) + { + xfree (out); + goto leave; /* Should never happen. */ + } + + if (DBG_CRYPTO) + log_printhex (out, keylen, "thekey:"); + rc = gcry_cipher_setkey (dfx->cipher_hd, out, keylen); + xfree (out); + if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) + { + log_info (_("WARNING: message was encrypted with" + " a weak key in the symmetric cipher.\n")); + rc = 0; + } + else if (rc) + { + log_error("key setup failed: %s\n", gpg_strerror (rc)); + goto leave; + } + + if (!ed->buf) + { + log_error(_("problem handling encrypted packet\n")); + goto leave; + } + } else /* CFB encryption. */ { nprefix = blocksize; @@ -509,7 +700,7 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) ed->buf = NULL; if (dfx->eof_seen > 1 ) rc = gpg_error (GPG_ERR_INV_PACKET); - else if ( ed->mdc_method ) + else if ( ed->mdc_method && ed->aead_algo == 0 ) { /* We used to let parse-packet.c handle the MDC packet but this turned out to be a problem with compressed packets: With old diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 7e1c8f959..2f4f003ad 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -3525,7 +3525,22 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, version = iobuf_get_noeof (inp); if (orig_pktlen) pktlen--; - if (version != 1) + if (version == 1) + ed->mdc_method = DIGEST_ALGO_SHA1; + else if (version == 2) + { + ed->mdc_method = 0xff; /* SEIPDv2 */ + ed->cipher_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->aead_algo = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + ed->chunkbyte = iobuf_get_noeof (inp); + if (orig_pktlen) + pktlen--; + } + else { log_error ("encrypted_mdc packet with unknown version %d\n", version); @@ -3535,7 +3550,6 @@ parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen, rc = gpg_error (GPG_ERR_INV_PACKET); goto leave; } - ed->mdc_method = DIGEST_ALGO_SHA1; } else ed->mdc_method = 0; |