mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
pgcrypto: Add option to revert to prior decryption behavior
The previous commit raises an ERROR during PGP operations if OpenSSL does not support the cipher in use. However, any existing messages created with faulty encryption will no longer be accessible via pgp_[sym|pub]_decrypt(). To help users out of this situation, add a new ignore-cipher-failure option which reverts to the broken behavior during decryption only. A faulty encryption wrapper, created by an OpenSSL configuration that does not support the cipher, can then be stripped back off by that same OpenSSL in order to safely reencrypt it. (Note that when OpenSSL does support the cipher, corrupted messages will not be decrypted regardless of the ignore-cipher-failure setting; this is unchanged.) The new tests add a corrupted Blowfish message for both public- and symmetric-key decryption, resulting in the following test matrix: - Blowfish supported, default behavior: fails to decrypt - Blowfish supported, ignore-cipher-failure: fails to decrypt - Blowfish unsupported, default behavior: fails to load cipher - Blowfish unsupported, ignore-cipher-failure: strips faulty encryption The previous commit's change to the pubkey tests is expanded similarly: correctly encrypted messages cannot be decrypted by an OpenSSL that does not support the cipher, regardless of the option's setting, though the failure mode will change. Suggested-by: Noah Misch <noah@leadboat.com> Reviewed-by: Daniel Gustafsson <daniel@yesql.se> Reviewed-by: Noah Misch <noah@leadboat.com> Security: CVE-2026-14663 Backpatch-through: 14
This commit is contained in:
committed by
Noah Misch
parent
6ca6023ca0
commit
ba2a63faff
@@ -439,3 +439,29 @@ UCAAw2JRIISttRHMfDpDuZJpvYo=
|
||||
'), 'key', 'debug=1');
|
||||
NOTICE: dbg: parse_compressed_data: bzip2 unsupported
|
||||
ERROR: Unsupported compression algorithm
|
||||
-- Check ignore-cipher-failure. This message isn't actually encrypted; it was
|
||||
-- created with cipher-algo=bf using an OpenSSL that didn't actually support
|
||||
-- Blowfish. After the fix for CVE-2026-14663, we no longer create these broken
|
||||
-- ciphertexts, but we allow users to return to the previous behavior during
|
||||
-- decryption so that the bad wrapper can be stripped.
|
||||
--
|
||||
-- Note that if Blowfish is supported by the linked OpenSSL, both decryptions
|
||||
-- will fail.
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key');
|
||||
ERROR: Wrong key or corrupt data
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key', 'ignore-cipher-failure=1');
|
||||
ERROR: Wrong key or corrupt data
|
||||
|
||||
@@ -435,3 +435,33 @@ UCAAw2JRIISttRHMfDpDuZJpvYo=
|
||||
'), 'key', 'debug=1');
|
||||
NOTICE: dbg: parse_compressed_data: bzip2 unsupported
|
||||
ERROR: Unsupported compression algorithm
|
||||
-- Check ignore-cipher-failure. This message isn't actually encrypted; it was
|
||||
-- created with cipher-algo=bf using an OpenSSL that didn't actually support
|
||||
-- Blowfish. After the fix for CVE-2026-14663, we no longer create these broken
|
||||
-- ciphertexts, but we allow users to return to the previous behavior during
|
||||
-- decryption so that the bad wrapper can be stripped.
|
||||
--
|
||||
-- Note that if Blowfish is supported by the linked OpenSSL, both decryptions
|
||||
-- will fail.
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key');
|
||||
ERROR: encrypt error: Cipher cannot be initialized
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key', 'ignore-cipher-failure=1');
|
||||
pgp_sym_decrypt
|
||||
-----------------
|
||||
Secret.
|
||||
(1 row)
|
||||
|
||||
|
||||
@@ -75,5 +75,6 @@ from encdata order by id;
|
||||
B68504FD128E1FF9
|
||||
FD0206C409B74875
|
||||
FD0206C409B74875
|
||||
(5 rows)
|
||||
D936CF64BB73F466
|
||||
(6 rows)
|
||||
|
||||
|
||||
@@ -585,6 +585,20 @@ blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
|
||||
=PHJ1
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
-- CVE-2026-14663. This message was created with cipher-algo=bf using an OpenSSL
|
||||
-- that didn't actually support Blowfish.
|
||||
insert into encdata (id, data) values (6, '
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
wcBOA9k2z2S7c/RmEAP8DYbU6AeEo6riMMdnf2G62BM9gC0Z32ODydewy3Ki8AnSzpwBDAHuDMcr
|
||||
P6RJDWvBOVOwgxHEwR7ZHMoFRDJEXdo6rQ9dQpDtbasMLyi6Lm1q+PbEefVd9WkU7fvFAFQx8k3t
|
||||
lxrlWg/byoNplc7/hFxIFO8bN+FIlLgilAdApNcD/3Mg2/nd7pczovsYoryf9ib04kQ+SVWs3iNE
|
||||
StoyEXT+oaT8u1vAxiY7fzPpQX1pnlHBUXn+v1J6LQL5Bwi5CTqOyDSyaFfgU0gQwTReFjS6L4Fs
|
||||
Cv+2cFwbJBGIzr1aI4DLbzSelkmVm4hbOVeET4DJVlUVhhIyy6ZfoXiTEG6s0jMB2JdRGIl0EUQR
|
||||
RMsQdABqdt5jU2VjcmV0IG1zZ9MUIIP4SPiU2pM/nF/A1hrltMhn/ZI=
|
||||
=Mkdj
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
-- successful decrypt
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=1;
|
||||
@@ -600,6 +614,13 @@ from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
Secret msg
|
||||
(1 row)
|
||||
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
pgp_pub_decrypt
|
||||
-----------------
|
||||
Secret msg
|
||||
(1 row)
|
||||
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=3 and encdata.id=3;
|
||||
pgp_pub_decrypt
|
||||
@@ -654,3 +675,12 @@ from keytbl, encdata where keytbl.id=5 and encdata.id=1;
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=6 and encdata.id=5;
|
||||
ERROR: Wrong key or corrupt data
|
||||
-- Check that ignore-cipher-failure can strip faulty encryption if OpenSSL
|
||||
-- doesn't support the cipher. (The decryption will correctly fail both times if
|
||||
-- OpenSSL does support it.)
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
ERROR: Wrong key or corrupt data
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
ERROR: Wrong key or corrupt data
|
||||
|
||||
@@ -585,6 +585,20 @@ blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
|
||||
=PHJ1
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
-- CVE-2026-14663. This message was created with cipher-algo=bf using an OpenSSL
|
||||
-- that didn't actually support Blowfish.
|
||||
insert into encdata (id, data) values (6, '
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
wcBOA9k2z2S7c/RmEAP8DYbU6AeEo6riMMdnf2G62BM9gC0Z32ODydewy3Ki8AnSzpwBDAHuDMcr
|
||||
P6RJDWvBOVOwgxHEwR7ZHMoFRDJEXdo6rQ9dQpDtbasMLyi6Lm1q+PbEefVd9WkU7fvFAFQx8k3t
|
||||
lxrlWg/byoNplc7/hFxIFO8bN+FIlLgilAdApNcD/3Mg2/nd7pczovsYoryf9ib04kQ+SVWs3iNE
|
||||
StoyEXT+oaT8u1vAxiY7fzPpQX1pnlHBUXn+v1J6LQL5Bwi5CTqOyDSyaFfgU0gQwTReFjS6L4Fs
|
||||
Cv+2cFwbJBGIzr1aI4DLbzSelkmVm4hbOVeET4DJVlUVhhIyy6ZfoXiTEG6s0jMB2JdRGIl0EUQR
|
||||
RMsQdABqdt5jU2VjcmV0IG1zZ9MUIIP4SPiU2pM/nF/A1hrltMhn/ZI=
|
||||
=Mkdj
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
-- successful decrypt
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=1;
|
||||
@@ -596,6 +610,9 @@ from keytbl, encdata where keytbl.id=1 and encdata.id=1;
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
ERROR: encrypt error: Cipher cannot be initialized
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
ERROR: Wrong key or corrupt data
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=3 and encdata.id=3;
|
||||
pgp_pub_decrypt
|
||||
@@ -650,3 +667,16 @@ from keytbl, encdata where keytbl.id=5 and encdata.id=1;
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=6 and encdata.id=5;
|
||||
ERROR: Wrong key or corrupt data
|
||||
-- Check that ignore-cipher-failure can strip faulty encryption if OpenSSL
|
||||
-- doesn't support the cipher. (The decryption will correctly fail both times if
|
||||
-- OpenSSL does support it.)
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
ERROR: encrypt error: Cipher cannot be initialized
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
pgp_pub_decrypt
|
||||
-----------------
|
||||
Secret msg
|
||||
(1 row)
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ struct PGP_CFB
|
||||
int pos;
|
||||
int block_no;
|
||||
int resync;
|
||||
int ignore_decrypt_cipher_failure; /* for CVE-2026-14663 recovery */
|
||||
uint8 fr[PGP_MAX_BLOCK];
|
||||
uint8 fre[PGP_MAX_BLOCK];
|
||||
uint8 encbuf[PGP_MAX_BLOCK];
|
||||
@@ -50,7 +51,7 @@ struct PGP_CFB
|
||||
|
||||
int
|
||||
pgp_cfb_create(PGP_CFB **ctx_p, int algo, const uint8 *key, int key_len,
|
||||
int resync, uint8 *iv)
|
||||
int resync, uint8 *iv, int ignore_decrypt_cipher_failure)
|
||||
{
|
||||
int res;
|
||||
PX_Cipher *ciph;
|
||||
@@ -71,6 +72,7 @@ pgp_cfb_create(PGP_CFB **ctx_p, int algo, const uint8 *key, int key_len,
|
||||
ctx->ciph = ciph;
|
||||
ctx->block_size = px_cipher_block_size(ciph);
|
||||
ctx->resync = resync;
|
||||
ctx->ignore_decrypt_cipher_failure = ignore_decrypt_cipher_failure;
|
||||
|
||||
if (iv)
|
||||
memcpy(ctx->fr, iv, ctx->block_size);
|
||||
@@ -195,7 +197,7 @@ mix_decrypt_resync(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
|
||||
*/
|
||||
static int
|
||||
cfb_process(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst,
|
||||
mix_data_t mix_data)
|
||||
mix_data_t mix_data, int ignore_cipher_failure)
|
||||
{
|
||||
int n;
|
||||
int res;
|
||||
@@ -224,7 +226,14 @@ cfb_process(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst,
|
||||
int err;
|
||||
|
||||
err = px_cipher_encrypt(ctx->ciph, 0, ctx->fr, ctx->block_size, ctx->fre, &rlen);
|
||||
if (err)
|
||||
|
||||
/*
|
||||
* XXX Ignoring cipher failures is dangerous, but we allow it during
|
||||
* decryption to return to the behavior prior to the fix for
|
||||
* CVE-2026-14663. This lets users recover data from a badly-encrypted
|
||||
* message.
|
||||
*/
|
||||
if (err && !ignore_cipher_failure)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
|
||||
errmsg("encrypt error: %s", px_strerror(err))));
|
||||
@@ -259,7 +268,8 @@ pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
|
||||
{
|
||||
mix_data_t mix = ctx->resync ? mix_encrypt_resync : mix_encrypt_normal;
|
||||
|
||||
return cfb_process(ctx, data, len, dst, mix);
|
||||
return cfb_process(ctx, data, len, dst, mix,
|
||||
0 /* never ignore cipher failures for encrypt */ );
|
||||
}
|
||||
|
||||
int
|
||||
@@ -267,5 +277,6 @@ pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
|
||||
{
|
||||
mix_data_t mix = ctx->resync ? mix_decrypt_resync : mix_decrypt_normal;
|
||||
|
||||
return cfb_process(ctx, data, len, dst, mix);
|
||||
return cfb_process(ctx, data, len, dst, mix,
|
||||
ctx->ignore_decrypt_cipher_failure);
|
||||
}
|
||||
|
||||
@@ -595,7 +595,8 @@ decrypt_key(PGP_Context *ctx, const uint8 *src, int len)
|
||||
PGP_CFB *cfb;
|
||||
|
||||
res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
|
||||
ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
|
||||
ctx->s2k.key, ctx->s2k.key_len, 0, NULL,
|
||||
ctx->ignore_cipher_failure);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@@ -983,7 +984,8 @@ parse_symenc_data(PGP_Context *ctx, PullFilter *pkt, MBuf *dst)
|
||||
PullFilter *pf_prefix = NULL;
|
||||
|
||||
res = pgp_cfb_create(&cfb, ctx->cipher_algo,
|
||||
ctx->sess_key, ctx->sess_key_len, 1, NULL);
|
||||
ctx->sess_key, ctx->sess_key_len, 1, NULL,
|
||||
ctx->ignore_cipher_failure);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
@@ -1026,7 +1028,8 @@ parse_symenc_mdc_data(PGP_Context *ctx, PullFilter *pkt, MBuf *dst)
|
||||
}
|
||||
|
||||
res = pgp_cfb_create(&cfb, ctx->cipher_algo,
|
||||
ctx->sess_key, ctx->sess_key_len, 0, NULL);
|
||||
ctx->sess_key, ctx->sess_key_len, 0, NULL,
|
||||
ctx->ignore_cipher_failure);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -174,7 +174,8 @@ encrypt_init(PushFilter *next, void *init_arg, void **priv_p)
|
||||
return res;
|
||||
}
|
||||
res = pgp_cfb_create(&ciph, ctx->cipher_algo,
|
||||
ctx->sess_key, ctx->sess_key_len, resync, NULL);
|
||||
ctx->sess_key, ctx->sess_key_len, resync, NULL,
|
||||
0 /* never ignore cipher failures for encrypt */ );
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@@ -505,7 +506,8 @@ symencrypt_sesskey(PGP_Context *ctx, uint8 *dst)
|
||||
uint8 algo = ctx->cipher_algo;
|
||||
|
||||
res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
|
||||
ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
|
||||
ctx->s2k.key, ctx->s2k.key_len, 0, NULL,
|
||||
0 /* never ignore cipher failures for encrypt */ );
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
|
||||
@@ -192,6 +192,8 @@ set_arg(PGP_Context *ctx, char *key, char *val,
|
||||
res = pgp_set_convert_crlf(ctx, atoi(val));
|
||||
else if (strcmp(key, "unicode-mode") == 0)
|
||||
res = pgp_set_unicode_mode(ctx, atoi(val));
|
||||
else if (strcmp(key, "ignore-cipher-failure") == 0)
|
||||
res = pgp_set_ignore_cipher_failure(ctx, atoi(val));
|
||||
|
||||
/*
|
||||
* The remaining options are for debugging/testing and are therefore not
|
||||
|
||||
@@ -382,8 +382,15 @@ process_secret_key(PullFilter *pkt, PGP_PubKey **pk_p,
|
||||
|
||||
/*
|
||||
* create decrypt filter
|
||||
*
|
||||
* ignore-cipher-failure doesn't apply here; pgcrypto didn't encrypt
|
||||
* the secret key to begin with, and any stored encrypted data was
|
||||
* generated using the public key, so users don't have a reason to
|
||||
* want to incorrectly decrypt this. We'll ignore failures during
|
||||
* decryption with the session key, instead.
|
||||
*/
|
||||
res = pgp_cfb_create(&cfb, cipher_algo, s2k.key, s2k.key_len, 0, iv);
|
||||
res = pgp_cfb_create(&cfb, cipher_algo, s2k.key, s2k.key_len, 0, iv,
|
||||
0 /* don't ignore cipher failures */ );
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
|
||||
|
||||
@@ -49,6 +49,7 @@ static int def_use_sess_key = 0;
|
||||
static int def_text_mode = 0;
|
||||
static int def_unicode_mode = 0;
|
||||
static int def_convert_crlf = 0;
|
||||
static int def_ignore_cipher_failure = 0;
|
||||
|
||||
struct digest_info
|
||||
{
|
||||
@@ -204,6 +205,7 @@ pgp_init(PGP_Context **ctx_p)
|
||||
ctx->unicode_mode = def_unicode_mode;
|
||||
ctx->convert_crlf = def_convert_crlf;
|
||||
ctx->text_mode = def_text_mode;
|
||||
ctx->ignore_cipher_failure = def_ignore_cipher_failure;
|
||||
|
||||
*ctx_p = ctx;
|
||||
return 0;
|
||||
@@ -349,6 +351,13 @@ pgp_set_unicode_mode(PGP_Context *ctx, int mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pgp_set_ignore_cipher_failure(PGP_Context *ctx, int ignore)
|
||||
{
|
||||
ctx->ignore_cipher_failure = ignore ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pgp_set_symkey(PGP_Context *ctx, const uint8 *key, int len)
|
||||
{
|
||||
|
||||
@@ -150,6 +150,9 @@ struct PGP_Context
|
||||
int convert_crlf;
|
||||
int unicode_mode;
|
||||
|
||||
/* DANGEROUS recovery aid for CVE-2026-14663. Applies only to decryption. */
|
||||
int ignore_cipher_failure;
|
||||
|
||||
/*
|
||||
* internal variables
|
||||
*/
|
||||
@@ -258,6 +261,7 @@ int pgp_set_compress_level(PGP_Context *ctx, int level);
|
||||
int pgp_set_text_mode(PGP_Context *ctx, int mode);
|
||||
int pgp_set_unicode_mode(PGP_Context *ctx, int mode);
|
||||
int pgp_get_unicode_mode(PGP_Context *ctx);
|
||||
int pgp_set_ignore_cipher_failure(PGP_Context *ctx, int ignore);
|
||||
|
||||
int pgp_set_symkey(PGP_Context *ctx, const uint8 *key, int len);
|
||||
int pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
|
||||
@@ -278,7 +282,8 @@ int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int key_len);
|
||||
|
||||
typedef struct PGP_CFB PGP_CFB;
|
||||
int pgp_cfb_create(PGP_CFB **ctx_p, int algo,
|
||||
const uint8 *key, int key_len, int resync, uint8 *iv);
|
||||
const uint8 *key, int key_len, int resync, uint8 *iv,
|
||||
int ignore_decrypt_cipher_failure);
|
||||
void pgp_cfb_free(PGP_CFB *ctx);
|
||||
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
|
||||
int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
|
||||
|
||||
@@ -328,3 +328,29 @@ UCAAw2JRIISttRHMfDpDuZJpvYo=
|
||||
=AZ9M
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'key', 'debug=1');
|
||||
|
||||
-- Check ignore-cipher-failure. This message isn't actually encrypted; it was
|
||||
-- created with cipher-algo=bf using an OpenSSL that didn't actually support
|
||||
-- Blowfish. After the fix for CVE-2026-14663, we no longer create these broken
|
||||
-- ciphertexts, but we allow users to return to the previous behavior during
|
||||
-- decryption so that the bad wrapper can be stripped.
|
||||
--
|
||||
-- Note that if Blowfish is supported by the linked OpenSSL, both decryptions
|
||||
-- will fail.
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key');
|
||||
|
||||
select pgp_sym_decrypt(dearmor('
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
ww0EBAMC8wIKbtvzJtxi0jABUleCwFJWGCkYKcsNdABqdtXaU2VjcmV0LtMUlnPH3A2QBmZrcucm
|
||||
1GPb/s2Bkdg=
|
||||
=6aqD
|
||||
-----END PGP MESSAGE-----
|
||||
'), 'wrong key', 'ignore-cipher-failure=1');
|
||||
|
||||
@@ -601,6 +601,21 @@ blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
|
||||
-- CVE-2026-14663. This message was created with cipher-algo=bf using an OpenSSL
|
||||
-- that didn't actually support Blowfish.
|
||||
insert into encdata (id, data) values (6, '
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
wcBOA9k2z2S7c/RmEAP8DYbU6AeEo6riMMdnf2G62BM9gC0Z32ODydewy3Ki8AnSzpwBDAHuDMcr
|
||||
P6RJDWvBOVOwgxHEwR7ZHMoFRDJEXdo6rQ9dQpDtbasMLyi6Lm1q+PbEefVd9WkU7fvFAFQx8k3t
|
||||
lxrlWg/byoNplc7/hFxIFO8bN+FIlLgilAdApNcD/3Mg2/nd7pczovsYoryf9ib04kQ+SVWs3iNE
|
||||
StoyEXT+oaT8u1vAxiY7fzPpQX1pnlHBUXn+v1J6LQL5Bwi5CTqOyDSyaFfgU0gQwTReFjS6L4Fs
|
||||
Cv+2cFwbJBGIzr1aI4DLbzSelkmVm4hbOVeET4DJVlUVhhIyy6ZfoXiTEG6s0jMB2JdRGIl0EUQR
|
||||
RMsQdABqdt5jU2VjcmV0IG1zZ9MUIIP4SPiU2pM/nF/A1hrltMhn/ZI=
|
||||
=Mkdj
|
||||
-----END PGP MESSAGE-----
|
||||
');
|
||||
|
||||
-- successful decrypt
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=1;
|
||||
@@ -608,6 +623,9 @@ from keytbl, encdata where keytbl.id=1 and encdata.id=1;
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=2 and encdata.id=2;
|
||||
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=3 and encdata.id=3;
|
||||
|
||||
@@ -645,3 +663,12 @@ from keytbl, encdata where keytbl.id=5 and encdata.id=1;
|
||||
-- test for a short read from prefix_init
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=6 and encdata.id=5;
|
||||
|
||||
-- Check that ignore-cipher-failure can strip faulty encryption if OpenSSL
|
||||
-- doesn't support the cipher. (The decryption will correctly fail both times if
|
||||
-- OpenSSL does support it.)
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
|
||||
select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '', 'ignore-cipher-failure=1')
|
||||
from keytbl, encdata where keytbl.id=1 and encdata.id=6;
|
||||
|
||||
@@ -921,6 +921,38 @@ Applies to: pgp_sym_encrypt
|
||||
Values: 0, 1
|
||||
Default: 0
|
||||
Applies to: pgp_sym_encrypt, pgp_pub_encrypt
|
||||
</literallayout>
|
||||
</sect4>
|
||||
|
||||
<sect4 id="pgcrypto-pgp-enc-funcs-opts-ignore-cipher-failure">
|
||||
<title>ignore-cipher-failure</title>
|
||||
|
||||
<para>
|
||||
Dangerous! Instructs pgcrypto to use an incorrect decryption algorithm
|
||||
matching the historical behavior prior to the fix for CVE-2026-14663, by
|
||||
completely ignoring failures from the OpenSSL cipher in use. This is
|
||||
intended only for users who need to recover incorrectly-encrypted messages
|
||||
created when the <literal>cipher-algo</literal> was unavailable under the
|
||||
OpenSSL configuration in use. Such faulty messages do not require the
|
||||
correct decryption key when <literal>ignore-cipher-failure</literal> is
|
||||
enabled, so there is no guarantee that the decrypted plaintext actually
|
||||
originated from a holder of the key.
|
||||
</para>
|
||||
<para>
|
||||
Contrast the case of a message which was correctly encrypted, but the cipher
|
||||
that produced it is unavailable under the current <productname>OpenSSL</productname>
|
||||
configuration. Recovering such plaintext via <filename>pgcrypto</filename>
|
||||
requires making the actual cipher available to OpenSSL by, for example,
|
||||
enabling the appropriate provider. <literal>ignore-cipher-failure</literal>
|
||||
is not necessary or helpful for that scenario. If <quote>decryption</quote>
|
||||
of a correctly encrypted message with this option happens to pass PGP
|
||||
integrity checks, that result is coincidental and does not make the
|
||||
recovered plaintext trustworthy.
|
||||
</para>
|
||||
<literallayout>
|
||||
Values: 0, 1
|
||||
Default: 0
|
||||
Applies to: pgp_sym_decrypt, pgp_pub_decrypt
|
||||
</literallayout>
|
||||
</sect4>
|
||||
</sect3>
|
||||
|
||||
Reference in New Issue
Block a user