mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
pgcrypto: Report errant decryption as "Wrong key or corrupt data".
This has been the predominant outcome. When the output of decrypting with a wrong key coincidentally resembled an OpenPGP packet header, pgcrypto could instead report "Corrupt data", "Not text data" or "Unsupported compression algorithm". The distinct "Corrupt data" message added no value. The latter two error messages misled when the decrypted payload also exhibited fundamental integrity problems. Worse, error message variance in other systems has enabled cryptologic attacks; see RFC 4880 section "14. Security Considerations". Whether these pgcrypto behaviors are likewise exploitable is unknown. In passing, document that pgcrypto does not resist side-channel attacks. Back-patch to 9.0 (all supported versions). Security: CVE-2015-3167
This commit is contained in:
@ -236,6 +236,8 @@ pgp_create_pkt_reader(PullFilter **pf_p, PullFilter *src, int len,
|
||||
|
||||
/*
|
||||
* Prefix check filter
|
||||
* https://tools.ietf.org/html/rfc4880#section-5.7
|
||||
* https://tools.ietf.org/html/rfc4880#section-5.13
|
||||
*/
|
||||
|
||||
static int
|
||||
@ -264,20 +266,7 @@ prefix_init(void **priv_p, void *arg, PullFilter *src)
|
||||
if (buf[len - 2] != buf[len] || buf[len - 1] != buf[len + 1])
|
||||
{
|
||||
px_debug("prefix_init: corrupt prefix");
|
||||
|
||||
/*
|
||||
* The original purpose of the 2-byte check was to show user a
|
||||
* friendly "wrong key" message. This made following possible:
|
||||
*
|
||||
* "An Attack on CFB Mode Encryption As Used By OpenPGP" by Serge
|
||||
* Mister and Robert Zuccherato
|
||||
*
|
||||
* To avoid being 'oracle', we delay reporting, which basically means
|
||||
* we prefer to run into corrupt packet header.
|
||||
*
|
||||
* We _could_ throw PXE_PGP_CORRUPT_DATA here, but there is
|
||||
* possibility of attack via timing, so we don't.
|
||||
*/
|
||||
/* report error in pgp_decrypt() */
|
||||
ctx->corrupt_prefix = 1;
|
||||
}
|
||||
px_memset(tmpbuf, 0, sizeof(tmpbuf));
|
||||
@ -788,12 +777,15 @@ parse_literal_data(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
|
||||
}
|
||||
px_memset(tmpbuf, 0, 4);
|
||||
|
||||
/* check if text */
|
||||
/*
|
||||
* If called from an SQL function that returns text, pgp_decrypt() rejects
|
||||
* inputs not self-identifying as text.
|
||||
*/
|
||||
if (ctx->text_mode)
|
||||
if (type != 't' && type != 'u')
|
||||
{
|
||||
px_debug("parse_literal_data: data type=%c", type);
|
||||
return PXE_PGP_NOT_TEXT;
|
||||
ctx->unexpected_binary = true;
|
||||
}
|
||||
|
||||
ctx->unicode_mode = (type == 'u') ? 1 : 0;
|
||||
@ -827,6 +819,7 @@ parse_compressed_data(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
|
||||
int res;
|
||||
uint8 type;
|
||||
PullFilter *pf_decompr;
|
||||
uint8 *discard_buf;
|
||||
|
||||
GETBYTE(pkt, type);
|
||||
|
||||
@ -850,7 +843,20 @@ parse_compressed_data(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
|
||||
|
||||
case PGP_COMPR_BZIP2:
|
||||
px_debug("parse_compressed_data: bzip2 unsupported");
|
||||
res = PXE_PGP_UNSUPPORTED_COMPR;
|
||||
/* report error in pgp_decrypt() */
|
||||
ctx->unsupported_compr = 1;
|
||||
|
||||
/*
|
||||
* Discard the compressed data, allowing it to first affect any
|
||||
* MDC digest computation.
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
res = pullf_read(pkt, 32 * 1024, &discard_buf);
|
||||
if (res <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1168,8 +1174,36 @@ pgp_decrypt(PGP_Context *ctx, MBuf *msrc, MBuf *mdst)
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
/*
|
||||
* Report a failure of the prefix_init() "quick check" now, rather than
|
||||
* upon detection, to hinder timing attacks. pgcrypto is not generally
|
||||
* secure against timing attacks, but this helps.
|
||||
*/
|
||||
if (!got_data || ctx->corrupt_prefix)
|
||||
res = PXE_PGP_CORRUPT_DATA;
|
||||
return PXE_PGP_CORRUPT_DATA;
|
||||
|
||||
/*
|
||||
* Code interpreting purportedly-decrypted data prior to this stage shall
|
||||
* report no error other than PXE_PGP_CORRUPT_DATA. (PXE_BUG is okay so
|
||||
* long as it remains unreachable.) This ensures that an attacker able to
|
||||
* choose a ciphertext and receive a corresponding decryption error
|
||||
* message cannot use that oracle to gather clues about the decryption
|
||||
* key. See "An Attack on CFB Mode Encryption As Used By OpenPGP" by
|
||||
* Serge Mister and Robert Zuccherato.
|
||||
*
|
||||
* A problematic value in the first octet of a Literal Data or Compressed
|
||||
* Data packet may indicate a simple user error, such as the need to call
|
||||
* pgp_sym_decrypt_bytea instead of pgp_sym_decrypt. Occasionally,
|
||||
* though, it is the first symptom of the encryption key not matching the
|
||||
* decryption key. When this was the only problem encountered, report a
|
||||
* specific error to guide the user; otherwise, we will have reported
|
||||
* PXE_PGP_CORRUPT_DATA before now. A key mismatch makes the other errors
|
||||
* into red herrings, and this avoids leaking clues to attackers.
|
||||
*/
|
||||
if (ctx->unsupported_compr)
|
||||
return PXE_PGP_UNSUPPORTED_COMPR;
|
||||
if (ctx->unexpected_binary)
|
||||
return PXE_PGP_NOT_TEXT;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
Reference in New Issue
Block a user