pgcrypto: Fix buffer overflow in pgp_pub_decrypt_bytea()

pgp_pub_decrypt_bytea() was missing a safeguard for the session key
length read from the message data, that can be given in input of
pgp_pub_decrypt_bytea().  This can result in the possibility of a buffer
overflow for the session key data, when the length specified is longer
than PGP_MAX_KEY, which is the maximum size of the buffer where the
session data is copied to.

A script able to rebuild the message and key data that can trigger the
overflow is included in this commit, based on some contents provided by
the reporter, heavily editted by me.  A SQL test is added, based on the
data generated by the script.

Reported-by: Team Xint Code as part of zeroday.cloud
Author: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Noah Misch <noah@leadboat.com>
Security: CVE-2026-2005
Backpatch-through: 14
This commit is contained in:
Michael Paquier 2026-02-09 08:01:09 +09:00
parent 7937856817
commit 527b730f41
8 changed files with 599 additions and 3 deletions

View file

@ -43,7 +43,8 @@ REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \
sha2 des 3des cast5 \
crypt-des crypt-md5 crypt-blowfish crypt-xdes \
pgp-armor pgp-decrypt pgp-encrypt $(CF_PGP_TESTS) \
pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-info
pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-pubkey-session \
pgp-info
EXTRA_CLEAN = gen-rtab

View file

@ -0,0 +1,47 @@
-- Test for overflow with session key at decrypt.
-- Data automatically generated by scripts/pgp_session_data.py.
-- See this file for details explaining how this data is generated.
SELECT pgp_pub_decrypt_bytea(
'\xc1c04c030000000000000000020800a46f5b9b1905b49457a6485474f71ed9b46c2527e1
da08e1f7871e12c3d38828f2076b984a595bf60f616599ca5729d547de06a258bfbbcd30
94a321e4668cd43010f0ca8ecf931e5d39bda1152c50c367b11c723f270729245d3ebdbd
0694d320c5a5aa6a405fb45182acb3d7973cbce398e0c5060af7603cfd9ed186ebadd616
3b50ae42bea5f6d14dda24e6d4687b434c175084515d562e896742b0ba9a1c87d5642e10
a5550379c71cc490a052ada483b5d96526c0a600fc51755052aa77fdf72f7b4989b920e7
b90f4b30787a46482670d5caecc7a515a926055ad5509d135702ce51a0e4c1033f2d939d
8f0075ec3428e17310da37d3d2d7ad1ce99adcc91cd446c366c402ae1ee38250343a7fcc
0f8bc28020e603d7a4795ef0dcc1c04c030000000000000000020800a46f5b9b1905b494
57a6485474f71ed9b46c2527e1da08e1f7871e12c3d38828f2076b984a595bf60f616599
ca5729d547de06a258bfbbcd3094a321e4668cd43010f0ca8ecf931e5d39bda1152c50c3
67b11c723f270729245d3ebdbd0694d320c5a5aa6a405fb45182acb3d7973cbce398e0c5
060af7603cfd9ed186ebadd6163b50ae42bea5f6d14dda24e6d4687b434c175084515d56
2e896742b0ba9a1c87d5642e10a5550379c71cc490a052ada483b5d96526c0a600fc5175
5052aa77fdf72f7b4989b920e7b90f4b30787a46482670d5caecc7a515a926055ad5509d
135702ce51a0e4c1033f2d939d8f0075ec3428e17310da37d3d2d7ad1ce99adc'::bytea,
'\xc7c2d8046965d657020800eef8bf1515adb1a3ee7825f75c668ea8dd3e3f9d13e958f6ad
9c55adc0c931a4bb00abe1d52cf7bb0c95d537949d277a5292ede375c6b2a67a3bf7d19f
f975bb7e7be35c2d8300dacba360a0163567372f7dc24000cc7cb6170bedc8f3b1f98c12
07a6cb4de870a4bc61319b139dcc0e20c368fd68f8fd346d2c0b69c5aed560504e2ec6f1
23086fe3c5540dc4dd155c0c67257c4ada862f90fe172ace344089da8135e92aca5c2709
f1c1bc521798bb8c0365841496e709bd184132d387e0c9d5f26dc00fd06c3a76ef66a75c
138285038684707a847b7bd33cfbefbf1d336be954a8048946af97a66352adef8e8b5ae4
c4748c6f2510265b7a8267bc370dbb00110100010007ff7e72d4f95d2d39901ac12ca5c5
18e767e719e72340c3fab51c8c5ab1c40f31db8eaffe43533fa61e2dbca2c3f4396c0847
e5434756acbb1f68128f4136bb135710c89137d74538908dac77967de9e821c559700dd9
de5a2727eec1f5d12d5d74869dd1de45ed369d94a8814d23861dd163f8c27744b26b98f0
239c2e6dd1e3493b8cc976fdc8f9a5e250f715aa4c3d7d5f237f8ee15d242e8fa941d1a0
ed9550ab632d992a97518d142802cb0a97b251319bf5742db8d9d8cbaa06cdfba2d75bc9
9d77a51ff20bd5ba7f15d7af6e85b904de2855d19af08d45f39deb85403033c69c767a8e
74a343b1d6c8911d34ea441ac3850e57808ed3d885835cbe6c79d10400ef16256f3d5c4c
3341516a2d2aa888df81b603f48a27f3666b40f992a857c1d11ff639cd764a9b42d5a1f8
58b4aeee36b85508bb5e8b91ef88a7737770b330224479d9b44eae8c631bc43628b69549
507c0a1af0be0dd7696015abea722b571eb35eefc4ab95595378ec12814727443f625fcd
183bb9b3bccf53b54dd0e5e7a50400ffe08537b2d4e6074e4a1727b658cfccdec8962302
25e300c05690de45f7065c3d40d86f544a64d51a3e94424f9851a16d1322ebdb41fa8a45
3131f3e2dc94e858e6396722643df382680f815e53bcdcde5da622f50530a83b217f1103
cdd6e5e9babe1e415bbff28d44bd18c95f43bbd04afeb2a2a99af38a571c7540de21df03
ff62c0a33d9143dd3f639893f47732c11c5a12c6052d1935f4d507b7ae1f76ab0e9a69b8
7305a7f7c19bd509daf4903bff614bc26d118f03e461469c72c12d3a2bb4f78e4d342ce8
487723649a01ed2b9eb11c662134502c098d55dfcd361939d8370873422c3da75a515a75
9ffedfe7df44fb3c20f81650801a30d43b5c90b98b3eee'::bytea);
ERROR: Public key too big

View file

@ -50,6 +50,7 @@ pgcrypto_regress = [
'pgp-encrypt',
'pgp-pubkey-decrypt',
'pgp-pubkey-encrypt',
'pgp-pubkey-session',
'pgp-info',
]

View file

@ -157,6 +157,7 @@ pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt)
uint8 *msg;
int msglen;
PGP_MPI *m;
unsigned sess_key_len;
pk = ctx->pub_key;
if (pk == NULL)
@ -220,11 +221,19 @@ pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt)
if (res < 0)
goto out;
sess_key_len = msglen - 3;
if (sess_key_len > PGP_MAX_KEY)
{
px_debug("incorrect session key length=%u", sess_key_len);
res = PXE_PGP_KEY_TOO_BIG;
goto out;
}
/*
* got sesskey
*/
ctx->cipher_algo = *msg;
ctx->sess_key_len = msglen - 3;
ctx->sess_key_len = sess_key_len;
memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len);
out:

View file

@ -65,6 +65,7 @@ static const struct error_desc px_err_list[] = {
{PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"},
{PXE_PGP_MATH_FAILED, "Math operation failed"},
{PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"},
{PXE_PGP_KEY_TOO_BIG, "Public key too big"},
{PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"},
{PXE_PGP_WRONG_KEY, "Wrong key"},
{PXE_PGP_MULTIPLE_KEYS,

View file

@ -75,7 +75,7 @@
/* -108 is unused */
#define PXE_PGP_MATH_FAILED -109
#define PXE_PGP_SHORT_ELGAMAL_KEY -110
/* -111 is unused */
#define PXE_PGP_KEY_TOO_BIG -111
#define PXE_PGP_UNKNOWN_PUBALGO -112
#define PXE_PGP_WRONG_KEY -113
#define PXE_PGP_MULTIPLE_KEYS -114

View file

@ -0,0 +1,491 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Generate PGP data to check the session key length of the input data provided
# to pgp_pub_decrypt_bytea().
#
# First, the crafted data is generated from valid RSA data, freshly generated
# by this script each time it is run, see generate_rsa_keypair().
# Second, the crafted PGP data is built, see build_message_data() and
# build_key_data(). Finally, the resulting SQL script is generated.
#
# This script generates in stdout the SQL file that is used in the regression
# tests of pgcrypto. The following command can be used to regenerate the file
# which should never be manually manipulated:
# python3 scripts/pgp_session_data.py > sql/pgp-pubkey-session.sql
import os
import re
import struct
import secrets
import sys
import time
# pwn for binary manipulation (p32, p64)
from pwn import *
# Cryptographic libraries, to craft the PGP data.
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse
# AES key used for session key encryption (16 bytes for AES-128)
AES_KEY = b'\x01' * 16
def generate_rsa_keypair(key_size: int = 2048) -> dict:
"""
Generate a fresh RSA key pair.
The generated key includes all components needed for PGP operations:
- n: public modulus (p * q)
- e: public exponent (typically 65537)
- d: private exponent (e^-1 mod phi(n))
- p, q: prime factors of n
- u: coefficient (p^-1 mod q) for CRT optimization
The caller can pass the wanted key size in input, for a default of 2048
bytes. This function returns the RSA key components, after performing
some validation on them.
"""
start_time = time.time()
# Generate RSA key
key = RSA.generate(key_size)
# Extract all key components
rsa_components = {
'n': key.n, # Public modulus (p * q)
'e': key.e, # Public exponent (typically 65537)
'd': key.d, # Private exponent (e^-1 mod phi(n))
'p': key.p, # First prime factor
'q': key.q, # Second prime factor
'u': inverse(key.p, key.q) # Coefficient for CRT: p^-1 mod q
}
# Validate key components for correctness
validate_rsa_key(rsa_components)
return rsa_components
def validate_rsa_key(rsa: dict) -> None:
"""
Validate a generated RSA key.
This function performs basic validation to ensure the RSA key is properly
constructed and all components are consistent, at least mathematically.
Validations performed:
1. n = p * q (modulus is product of primes)
2. gcd(e, phi(n)) = 1 (public exponent is coprime to phi(n))
3. (d * e) mod(phi(n)) = 1 (private exponent is multiplicative inverse)
4. (u * p) (mod q) = 1 (coefficient is correct for CRT)
"""
n, e, d, p, q, u = rsa['n'], rsa['e'], rsa['d'], rsa['p'], rsa['q'], rsa['u']
# Check that n = p * q
if n != p * q:
raise ValueError("RSA validation failed: n <> p * q")
# Check that p and q are different
if p == q:
raise ValueError("RSA validation failed: p = q (not allowed)")
# Calculate phi(n) = (p-1)(q-1)
phi_n = (p - 1) * (q - 1)
# Check that gcd(e, phi(n)) = 1
def gcd(a, b):
while b:
a, b = b, a % b
return a
if gcd(e, phi_n) != 1:
raise ValueError("RSA validation failed: gcd(e, phi(n)) <> 1")
# Check that (d * e) mod(phi(n)) = 1
if (d * e) % phi_n != 1:
raise ValueError("RSA validation failed: d * e <> 1 (mod phi(n))")
# Check that (u * p) (mod q) = 1
if (u * p) % q != 1:
raise ValueError("RSA validation failed: u * p <> 1 (mod q)")
def mpi_encode(x: int) -> bytes:
"""
Encode an integer as an OpenPGP Multi-Precision Integer (MPI).
Format (RFC 4880, Section 3.2):
- 2 bytes: bit length of the integer (big-endian)
- N bytes: the integer in big-endian format
This is used to encode RSA key components (n, e, d, p, q, u) in PGP
packets.
The integer to encode is given in input, returning an MPI-encoded
integer.
For example:
mpi_encode(65537) -> b'\x00\x11\x01\x00\x01'
(17 bits, value 0x010001)
"""
if x < 0:
raise ValueError("MPI cannot encode negative integers")
if x == 0:
# Special case: zero has 0 bits and empty magnitude
bits = 0
mag = b""
else:
# Calculate bit length and convert to bytes
bits = x.bit_length()
mag = x.to_bytes((bits + 7) // 8, 'big')
# Pack: 2-byte bit length + magnitude bytes
return struct.pack('>H', bits) + mag
def new_packet(tag: int, payload: bytes) -> bytes:
"""
Create a new OpenPGP packet with a proper header.
OpenPGP packet format (RFC 4880, Section 4.2):
- New packet format: 0xC0 | tag
- Length encoding depends on payload size:
* 0-191: single byte
* 192-8383: two bytes (192 + ((length - 192) >> 8), (length - 192) & 0xFF)
* 8384+: five bytes (0xFF + 4-byte big-endian length)
The packet is built from a "tag" (1-63) and some "payload" data. The
result generated is a complete OpenPGP packet.
For example:
new_packet(1, b'data') -> b'\xC1\x04data'
(Tag 1, length 4, payload 'data')
"""
# New packet format: set bit 7 and 6, clear bit 5, tag in bits 0-5
first = 0xC0 | (tag & 0x3F)
ln = len(payload)
# Encode length according to OpenPGP specification
if ln <= 191:
# Single byte length for small packets
llen = bytes([ln])
elif ln <= 8383:
# Two-byte length for medium packets
ln2 = ln - 192
llen = bytes([192 + (ln2 >> 8), ln2 & 0xFF])
else:
# Five-byte length for large packets
llen = bytes([255]) + struct.pack('>I', ln)
return bytes([first]) + llen + payload
def build_key_data(rsa: dict) -> bytes:
"""
Build the key data, containing an RSA private key.
The RSA contents should have been generated previously.
Format (see RFC 4880, Section 5.5.3):
- 1 byte: version (4)
- 4 bytes: creation time (current Unix timestamp)
- 1 byte: public key algorithm (2 = RSA encrypt)
- MPI: RSA public modulus n
- MPI: RSA public exponent e
- 1 byte: string-to-key usage (0 = no encryption)
- MPI: RSA private exponent d
- MPI: RSA prime p
- MPI: RSA prime q
- MPI: RSA coefficient u = p^-1 mod q
- 2 bytes: checksum of private key material
This function takes a set of RSA key components in input (n, e, d, p, q, u)
and returns a secret key packet.
"""
# Public key portion
ver = bytes([4]) # Version 4 key
ctime = struct.pack('>I', int(time.time())) # Current Unix timestamp
algo = bytes([2]) # RSA encrypt algorithm
n_mpi = mpi_encode(rsa['n']) # Public modulus
e_mpi = mpi_encode(rsa['e']) # Public exponent
pub = ver + ctime + algo + n_mpi + e_mpi
# Private key portion
hide_type = bytes([0]) # No string-to-key encryption
d_mpi = mpi_encode(rsa['d']) # Private exponent
p_mpi = mpi_encode(rsa['p']) # Prime p
q_mpi = mpi_encode(rsa['q']) # Prime q
u_mpi = mpi_encode(rsa['u']) # Coefficient u = p^-1 mod q
# Calculate checksum of private key material (simple sum mod 65536)
private_data = d_mpi + p_mpi + q_mpi + u_mpi
cksum = sum(private_data) & 0xFFFF
secret = hide_type + private_data + struct.pack('>H', cksum)
payload = pub + secret
return new_packet(7, payload)
def pgp_cfb_encrypt_resync(key, plaintext):
"""
Implement OpenPGP CFB mode with resync.
OpenPGP CFB mode is a variant of standard CFB with a resync operation
after the first two blocks.
Algorithm (RFC 4880, Section 13.9):
1. Block 1: FR=zeros, encrypt full block_size bytes
2. Block 2: FR=block1, encrypt only 2 bytes
3. Resync: FR = block1[2:] + block2
4. Remaining blocks: standard CFB mode
This function uses the following arguments:
- key: AES encryption key (16 bytes for AES-128)
- plaintext: Data to encrypt
"""
block_size = 16 # AES block size
cipher = AES.new(key[:16], AES.MODE_ECB) # Use ECB for manual CFB
ciphertext = b''
# Block 1: FR=zeros, encrypt full 16 bytes
FR = b'\x00' * block_size
FRE = cipher.encrypt(FR) # Encrypt the feedback register
block1 = bytes(a ^ b for a, b in zip(FRE, plaintext[0:16]))
ciphertext += block1
# Block 2: FR=block1, encrypt only 2 bytes
FR = block1
FRE = cipher.encrypt(FR)
block2 = bytes(a ^ b for a, b in zip(FRE[0:2], plaintext[16:18]))
ciphertext += block2
# Resync: FR = block1[2:16] + block2[0:2]
# This is the key difference from standard CFB mode
FR = block1[2:] + block2
# Block 3+: Continue with standard CFB mode
pos = 18
while pos < len(plaintext):
FRE = cipher.encrypt(FR)
chunk_len = min(block_size, len(plaintext) - pos)
chunk = plaintext[pos:pos+chunk_len]
enc_chunk = bytes(a ^ b for a, b in zip(FRE[:chunk_len], chunk))
ciphertext += enc_chunk
# Update feedback register for next iteration
if chunk_len == block_size:
FR = enc_chunk
else:
# Partial block: pad with old FR bytes
FR = enc_chunk + FR[chunk_len:]
pos += chunk_len
return ciphertext
def build_literal_data_packet(data: bytes) -> bytes:
"""
Build a literal data packet containing a message.
Format (RFC 4880, Section 5.9):
- 1 byte: data format ('b' = binary, 't' = text, 'u' = UTF-8 text)
- 1 byte: filename length (0 = no filename)
- N bytes: filename (empty in this case)
- 4 bytes: date (current Unix timestamp)
- M bytes: literal data
The data used to build the packet is given in input, with the generated
result returned.
"""
body = bytes([
ord('b'), # Binary data format
0, # Filename length (0 = no filename)
]) + struct.pack('>I', int(time.time())) + data # Current timestamp + data
return new_packet(11, body)
def build_symenc_data_packet(sess_key: bytes, cipher_algo: int, payload: bytes) -> bytes:
"""
Build a symmetrically-encrypted data packet using AES-128-CFB.
This packet contains encrypted data using the session key. The format
includes a random prefix, for security (see RFC 4880, Section 5.7).
Packet structure:
- Random prefix (block_size bytes)
- Prefix repeat (last 2 bytes of prefix repeated)
- Encrypted literal data packet
This function uses the following set of arguments:
- sess_key: Session key for encryption
- cipher_algo: Cipher algorithm identifier (7 = AES-128)
- payload: Data to encrypt (wrapped in literal data packet)
"""
block_size = 16 # AES-128 block size
key = sess_key[:16] # Use first 16 bytes for AES-128
# Create random prefix + repeat last 2 bytes (total 18 bytes)
# This is required by OpenPGP for integrity checking
prefix_random = secrets.token_bytes(block_size)
prefix = prefix_random + prefix_random[-2:] # 18 bytes total
# Wrap payload in literal data packet
literal_pkt = build_literal_data_packet(payload)
# Plaintext = prefix + literal data packet
plaintext = prefix + literal_pkt
# Encrypt using OpenPGP CFB mode with resync
ciphertext = pgp_cfb_encrypt_resync(key, plaintext)
return new_packet(9, ciphertext)
def build_tag1_packet(rsa: dict, sess_key: bytes) -> bytes:
"""
Build a public-key encrypted key.
This is a very important function, as it is able to create the packet
triggering the overflow check. This function can also be used to create
"legit" packet data.
Format (RFC 4880, Section 5.1):
- 1 byte: version (3)
- 8 bytes: key ID (0 = any key accepted)
- 1 byte: public key algorithm (2 = RSA encrypt)
- MPI: RSA-encrypted session key
This uses in arguments the generated RSA key pair, and the session key
to encrypt. The latter is manipulated to trigger the overflow.
This function returns a complete packet encrypted by a session key.
"""
# Calculate RSA modulus size in bytes
n_bytes = (rsa['n'].bit_length() + 7) // 8
# Session key message format:
# - 1 byte: symmetric cipher algorithm (7 = AES-128)
# - N bytes: session key
# - 2 bytes: checksum (simple sum of session key bytes)
algo_byte = bytes([7]) # AES-128 algorithm identifier
cksum = sum(sess_key) & 0xFFFF # 16-bit checksum
M = algo_byte + sess_key + struct.pack('>H', cksum)
# PKCS#1 v1.5 padding construction
# Format: 0x02 || PS || 0x00 || M
# Total padded message must be exactly n_bytes long.
total_len = n_bytes # Total length must equal modulus size in bytes
ps_len = total_len - len(M) - 2 # Subtract 2 for 0x02 and 0x00 bytes
if ps_len < 8:
raise ValueError(f"Padding string too short ({ps_len} bytes); need at least 8 bytes. "
f"Message length: {len(M)}, Modulus size: {n_bytes} bytes")
# Create padding string with *ALL* bytes being 0xFF (no zero separator!)
PS = bytes([0xFF]) * ps_len
# Construct the complete padded message
# Normal PKCS#1 v1.5 padding: 0x02 || PS || 0x00 || M
padded = bytes([0x02]) + PS + bytes([0x00]) + M
# Verify padding construction
if len(padded) != n_bytes:
raise ValueError(f"Padded message length ({len(padded)}) doesn't match RSA modulus size ({n_bytes})")
# Convert padded message to integer and encrypt with RSA
m_int = int.from_bytes(padded, 'big')
# Ensure message is smaller than modulus (required for RSA)
if m_int >= rsa['n']:
raise ValueError("Padded message is larger than RSA modulus")
# RSA encryption: c = m^e mod n
c_int = pow(m_int, rsa['e'], rsa['n'])
# Encode encrypted result as MPI
c_mpi = mpi_encode(c_int)
# Build complete packet
ver = bytes([3]) # Version 3 packet
key_id = b"\x00" * 8 # Key ID (0 = any key accepted)
algo = bytes([2]) # RSA encrypt algorithm
payload = ver + key_id + algo + c_mpi
return new_packet(1, payload)
def build_message_data(rsa: dict) -> bytes:
"""
This function creates a crafted message, with a long session key
length.
This takes in input the RSA key components generated previously,
returning a concatenated set of PGP packets crafted for the purpose
of this test.
"""
# Base prefix for session key (AES key + padding + size).
# Note that the crafted size is the important part for this test.
prefix = AES_KEY + b"\x00" * 16 + p32(0x10)
# Build encrypted data packet, legit.
sedata = build_symenc_data_packet(AES_KEY, cipher_algo=7, payload=b"\x0a\x00")
# Build multiple packets
packets = [
# First packet, legit.
build_tag1_packet(rsa, prefix),
# Encrypted data packet, legit.
sedata,
# Second packet: information payload.
#
# This packet contains a longer-crafted session key, able to trigger
# the overflow check in pgcrypto. This is the critical part, and
# and you are right to pay a lot of attention here if you are
# reading this code.
build_tag1_packet(rsa, prefix)
]
return b"".join(packets)
def main():
# Default key size.
# This number can be set to a higher number if wanted, like 4096. We
# just do not need to do that here.
key_size = 2048
# Generate fresh RSA key pair
rsa = generate_rsa_keypair(key_size)
# Generate the message data.
print("### Building message data", file=sys.stderr)
message_data = build_message_data(rsa)
# Build the key containing the RSA private key
print("### Building key data", file=sys.stderr)
key_data = build_key_data(rsa)
# Convert to hexadecimal, for the bytea used in the SQL file.
message_data = message_data.hex()
key_data = key_data.hex()
# Split each value into lines of 72 characters, for readability.
message_data = re.sub("(.{72})", "\\1\n", message_data, 0, re.DOTALL)
key_data = re.sub("(.{72})", "\\1\n", key_data, 0, re.DOTALL)
# Get the script filename for documentation
file_basename = os.path.basename(__file__)
# Output the SQL test case
print(f'''-- Test for overflow with session key at decrypt.
-- Data automatically generated by scripts/{file_basename}.
-- See this file for details explaining how this data is generated.
SELECT pgp_pub_decrypt_bytea(
'\\x{message_data}'::bytea,
'\\x{key_data}'::bytea);''',
file=sys.stdout)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,46 @@
-- Test for overflow with session key at decrypt.
-- Data automatically generated by scripts/pgp_session_data.py.
-- See this file for details explaining how this data is generated.
SELECT pgp_pub_decrypt_bytea(
'\xc1c04c030000000000000000020800a46f5b9b1905b49457a6485474f71ed9b46c2527e1
da08e1f7871e12c3d38828f2076b984a595bf60f616599ca5729d547de06a258bfbbcd30
94a321e4668cd43010f0ca8ecf931e5d39bda1152c50c367b11c723f270729245d3ebdbd
0694d320c5a5aa6a405fb45182acb3d7973cbce398e0c5060af7603cfd9ed186ebadd616
3b50ae42bea5f6d14dda24e6d4687b434c175084515d562e896742b0ba9a1c87d5642e10
a5550379c71cc490a052ada483b5d96526c0a600fc51755052aa77fdf72f7b4989b920e7
b90f4b30787a46482670d5caecc7a515a926055ad5509d135702ce51a0e4c1033f2d939d
8f0075ec3428e17310da37d3d2d7ad1ce99adcc91cd446c366c402ae1ee38250343a7fcc
0f8bc28020e603d7a4795ef0dcc1c04c030000000000000000020800a46f5b9b1905b494
57a6485474f71ed9b46c2527e1da08e1f7871e12c3d38828f2076b984a595bf60f616599
ca5729d547de06a258bfbbcd3094a321e4668cd43010f0ca8ecf931e5d39bda1152c50c3
67b11c723f270729245d3ebdbd0694d320c5a5aa6a405fb45182acb3d7973cbce398e0c5
060af7603cfd9ed186ebadd6163b50ae42bea5f6d14dda24e6d4687b434c175084515d56
2e896742b0ba9a1c87d5642e10a5550379c71cc490a052ada483b5d96526c0a600fc5175
5052aa77fdf72f7b4989b920e7b90f4b30787a46482670d5caecc7a515a926055ad5509d
135702ce51a0e4c1033f2d939d8f0075ec3428e17310da37d3d2d7ad1ce99adc'::bytea,
'\xc7c2d8046965d657020800eef8bf1515adb1a3ee7825f75c668ea8dd3e3f9d13e958f6ad
9c55adc0c931a4bb00abe1d52cf7bb0c95d537949d277a5292ede375c6b2a67a3bf7d19f
f975bb7e7be35c2d8300dacba360a0163567372f7dc24000cc7cb6170bedc8f3b1f98c12
07a6cb4de870a4bc61319b139dcc0e20c368fd68f8fd346d2c0b69c5aed560504e2ec6f1
23086fe3c5540dc4dd155c0c67257c4ada862f90fe172ace344089da8135e92aca5c2709
f1c1bc521798bb8c0365841496e709bd184132d387e0c9d5f26dc00fd06c3a76ef66a75c
138285038684707a847b7bd33cfbefbf1d336be954a8048946af97a66352adef8e8b5ae4
c4748c6f2510265b7a8267bc370dbb00110100010007ff7e72d4f95d2d39901ac12ca5c5
18e767e719e72340c3fab51c8c5ab1c40f31db8eaffe43533fa61e2dbca2c3f4396c0847
e5434756acbb1f68128f4136bb135710c89137d74538908dac77967de9e821c559700dd9
de5a2727eec1f5d12d5d74869dd1de45ed369d94a8814d23861dd163f8c27744b26b98f0
239c2e6dd1e3493b8cc976fdc8f9a5e250f715aa4c3d7d5f237f8ee15d242e8fa941d1a0
ed9550ab632d992a97518d142802cb0a97b251319bf5742db8d9d8cbaa06cdfba2d75bc9
9d77a51ff20bd5ba7f15d7af6e85b904de2855d19af08d45f39deb85403033c69c767a8e
74a343b1d6c8911d34ea441ac3850e57808ed3d885835cbe6c79d10400ef16256f3d5c4c
3341516a2d2aa888df81b603f48a27f3666b40f992a857c1d11ff639cd764a9b42d5a1f8
58b4aeee36b85508bb5e8b91ef88a7737770b330224479d9b44eae8c631bc43628b69549
507c0a1af0be0dd7696015abea722b571eb35eefc4ab95595378ec12814727443f625fcd
183bb9b3bccf53b54dd0e5e7a50400ffe08537b2d4e6074e4a1727b658cfccdec8962302
25e300c05690de45f7065c3d40d86f544a64d51a3e94424f9851a16d1322ebdb41fa8a45
3131f3e2dc94e858e6396722643df382680f815e53bcdcde5da622f50530a83b217f1103
cdd6e5e9babe1e415bbff28d44bd18c95f43bbd04afeb2a2a99af38a571c7540de21df03
ff62c0a33d9143dd3f639893f47732c11c5a12c6052d1935f4d507b7ae1f76ab0e9a69b8
7305a7f7c19bd509daf4903bff614bc26d118f03e461469c72c12d3a2bb4f78e4d342ce8
487723649a01ed2b9eb11c662134502c098d55dfcd361939d8370873422c3da75a515a75
9ffedfe7df44fb3c20f81650801a30d43b5c90b98b3eee'::bytea);