[PATCH] Validate pgcrypto generator RSA exponents modulo lambda(n)

Started by 夏侯莯昍4 days ago1 messageshackers
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254341
psql -h localhost -U postgres

Built from patchset v1 (message #1), October 24, 2026 at 08:19 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t254341_1 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t254341_1 && git checkout t254341_1

Patchset v1 (message #1) is on t254341_1

Jump to latest
#1夏侯莯昍
xiahoumuxuan@gmail.com

Hi,

contrib/pgcrypto/scripts/pgp_session_data.py can reject valid RSA keys from
PyCryptodome and fail before producing the regression SQL. PyCryptodome
computes d as the inverse of e modulo lcm(p-1, q-1), whereas the script
requires the stronger condition modulo (p-1)*(q-1).

RFC 8017 section 3.2 requires e*d = 1 modulo lambda(n):
https://www.rfc-editor.org/rfc/rfc8017.html#section-3.2

The attached patch uses lambda(n) = phi(n)/gcd(p-1,q-1) for this check
and corrects the corresponding comments. Other component checks remain
unchanged. This only affects the regression-data generator; it changes no
pgcrypto database code and is not a report of a new runtime vulnerability.

The patch was prepared against master at
3cde90ecc5f85e7c6cb9fba29d473b2f6dd40ee1.

The attached standalone reproduction script generates fresh ephemeral keys
locally. It contains no pre-generated key material and does not print or
save private parameters, generated SQL, or serialized packets.

Validation with that script on Python 3.12.14, PyCryptodome 4.1.0,
pwntools 4.15.0 and OpenSSL 3.5.7:
- 20 freshly generated 2048-bit keys all pass patched validation, actual
message/key serialization, packet-field/checksum checks, and RSA
round-trips. The old validator rejects 13 of those same valid keys.
- Phi-inverse compatibility and rejection of invalid components pass.
- OpenSSL independently validates a freshly generated lambda-only key.
- The complete main() path, using that same ephemeral key and real
dependencies, fails with the original validator and succeeds after the
patch. main() also succeeds with unmodified random generation.
- git diff --check passes. PostgreSQL and pgcrypto compile and install
successfully from this base. Database regression execution was attempted
but could not start a temporary server because this environment blocks
Unix-domain sockets. The generated SQL has not been executed, and the
pgcrypto database regression suite is not claimed to pass.

Run the reproduction with an original and a patched copy of the generator:
python3 test_generated_rsa.py /path/to/original.py /path/to/patched.py

It requires the generator's usual PyCryptodome and pwntools dependencies,
plus the openssl command. It uses at most 32 key-generation attempts to find
a distinguishing lambda-only key and prints only a validation summary.

This was originally proposed to IvorySQL; its maintainers asked that this
inherited generator change be sent upstream first:
https://github.com/IvorySQL/IvorySQL/pull/2512

AI assistance was used to prepare the patch, tests, and this message.
The reported validation results are from actual local runs.

Regards,
zmuxuny

Attachments:

t254341_1
v1-0001-Validate-generated-RSA-private-exponents-modulo-lambda.patchtext/plain; charset=utf-8; name=v1-0001-Validate-generated-RSA-private-exponents-modulo-lambda.patchDownload+8-7
test_generated_rsa.pytext/plain; charset=utf-8; name=test_generated_rsa.pyDownload