Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wDkJN-003IrQ-0s for pgsql-bugs@arkaria.postgresql.org; Fri, 17 Apr 2026 14:36:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wDkJM-00AIJx-1n for pgsql-bugs@arkaria.postgresql.org; Fri, 17 Apr 2026 14:36:48 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wDaiV-00886O-1G for pgsql-bugs@lists.postgresql.org; Fri, 17 Apr 2026 04:22:07 +0000 Received: from mahout.postgresql.org ([2001:4800:3e1:1::227]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.98.2) (envelope-from ) id 1wDaiT-00000001PvN-1EdL for pgsql-bugs@lists.postgresql.org; Fri, 17 Apr 2026 04:22:06 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=postgresql.org; s=20171124; h=Message-ID:Date:Reply-To:Cc:From:To:Subject: Content-Transfer-Encoding:MIME-Version:Content-Type:Sender:Content-ID: Content-Description:In-Reply-To:References; bh=W7LMYDib++aU59PgvSoPM9XJc7nQyz0HJJeB0/9l+8Y=; b=yte/YcKP4yms+mm1UPOXGWW0Ee dOg4nioTZd3zRp4JykjmnnZLPBaDsCfJyaTSV/9ITSVg3P+x3sgY+lOF4dgPKv4De0L/9dyqLSj/x 3JjX3vheE/o/zMrIMsr2m9J6NYFzLdx/DzCpRSHVJfUp/pDWo1OW4iJnglLupCCttpfLlGmTWwPwP qzwHOrCT2myg4zzol/3zJA7pxK9JxGQ8qHNtOdaEMflSFw3O+lCsPvJdbxOReBwmSNZT2tShaUoNL MtdEmbLiR1NXw3z2mCt9R+1CgbVVRlKA+mVVC4dSooZK88Q94Aq82H34HmYLrPrGR0H2KbAL2+k2u cL4Pm4CQ==; Received: from wrigleys.postgresql.org ([2a02:16a8:dc51::60]) by mahout.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wDaiS-0048ci-1E for pgsql-bugs@lists.postgresql.org; Fri, 17 Apr 2026 04:22:04 +0000 Received: from localhost ([127.0.0.1] helo=wrigleys.postgresql.org) by wrigleys.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wDaiQ-008u7c-1v for pgsql-bugs@lists.postgresql.org; Fri, 17 Apr 2026 04:22:03 +0000 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Subject: BUG #19457: RE: pgp_sym_encrypt silently accepts non-FIPS ciphers (bf, cast5, 3des) when OpenSSL is in FIPS mod To: pgsql-bugs@lists.postgresql.org From: PG Bug reporting form Cc: ansh01072001@gmail.com Reply-To: ansh01072001@gmail.com, pgsql-bugs@lists.postgresql.org Date: Fri, 17 Apr 2026 04:21:18 +0000 Message-ID: <19457-4bab15c17aea36c7@postgresql.org> X-Auto-Response-Suppress: All Auto-Submitted: auto-generated List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk The following bug has been logged on the website: Bug reference: 19457 Logged by: Shishir Sharma Email address: ansh01072001@gmail.com PostgreSQL version: 18.3 Operating system: AlmaLinux 8.10, x86_64 Description: =20 When PostgreSQL 18.3 is built against OpenSSL operating in FIPS mode, the pgp_sym_encrypt() (and pgp_pub_encrypt()) functions in the pgcrypto extension silently succeed when called with non-FIPS-approved cipher algorithms: bf (Blowfish), cast5 (CAST5), and 3des (Triple-DES). This is a FIPS compliance gap. Every other non-FIPS algorithm in pgcrypto is either blocked by OpenSSL (e.g. encrypt(..., 'bf'), digest(..., 'md5')) or, as of PostgreSQL 18, controlled by the new pgcrypto.builtin_crypto_enabled GUC (gen_salt(), crypt()). The PGP code path is the only one left completely unguarded. The result is that a deployment that has gone to the trouble of enabling OpenSSL FIPS mode expecting that prohibited algorithms cannot be used can still encrypt production data with Blowfish, CAST5, or 3DES via pgp_sym_encrypt(), with no error, no warning, and no indication that a FIPS violation has occurred. =3D=3D=3D Root Cause =3D=3D=3D The pgp_sym_encrypt() call chain is: =C2=A0 pgp_sym_encrypt() =C2=A0 =C2=A0 =E2=86=92 encrypt_internal() =C2=A0 =C2=A0 =C2=A0 =C2=A0[pgp-= pgsql.c] =C2=A0 =C2=A0 =C2=A0 =E2=86=92 pgp_set_cipher_algo() =C2=A0 [pgp.c] =C2=A0 = =C2=A0 =C2=A0 =C2=A0 -- maps 'cast5' =E2=86=92 PGP_SYM_CAST5 =C2=A0 =C2=A0 =C2=A0 =E2=86=92 pgp_encrypt() =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 [pgp-encrypt.c] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =E2=86=92 pgp_cfb_create() =C2=A0 =C2=A0 =C2=A0= [pgp-cfb.c] =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =E2=86=92 pgp_load_cipher() =C2=A0 [pgp.= c] =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- maps PGP_SYM_CAST5 =E2=86=92 "cast5-ecb" =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =E2=86=92 px_find_cipher() =C2=A0= [openssl.c] =C2=A0 =C2=A0 -- calls EVP_get_cipherbyname() The pgcrypto.builtin_crypto_enabled GUC introduced in PostgreSQL 18 (commits 924d89a and 035f99c) protects only px_crypt() and px_gen_salt() in px-crypt.c. The PGP encryption path never passes through px-crypt.c and is therefore not covered by the GUC, regardless of whether it is set to 'on', 'off', or 'fips'. =3D=3D=3D Steps to Reproduce =3D=3D=3D Prerequisites: =C2=A0 - PostgreSQL 18 built with OpenSSL =C2=A0 - OpenSSL configured in FIPS mode (fips_mode() returns true) =C2=A0 - pgcrypto extension installed =C2=A0 - FIPS enabled system -- Step 1: Verify FIPS mode is active SELECT fips_mode(); -- Expected: t -- Step 2: Confirm the GUC is set to 'fips' (the recommended FIPS setting) SET pgcrypto.builtin_crypto_enabled =3D 'fips'; SHOW pgcrypto.builtin_crypto_enabled; -- Expected: fips -- Step 3: Verify the GUC correctly blocks gen_salt/crypt (it does work there) SELECT gen_salt('bf'); -- Expected: ERROR: use of non-FIPS validated crypto not allowed... -- Actual: =C2=A0 ERROR: use of non-FIPS validated crypto not allowed... = =C2=A0=E2=9C=93 -- Step 4: Verify encrypt() correctly blocks non-FIPS ciphers (OpenSSL blocks these) SELECT encrypt('secret'::bytea, 'key12345'::bytea, 'bf'); -- Expected: ERROR -- Actual: =C2=A0 ERROR: encrypt error: Cipher cannot be initialized =C2=A0= =E2=9C=93 -- Step 5: THE BUG =E2=80=94 pgp_sym_encrypt silently succeeds with non-FIP= S ciphers SELECT pgp_sym_encrypt('secret', 'key', 'cipher-algo=3Dcast5, compress-algo=3D0'); -- Expected: ERROR (non-FIPS algorithm should be rejected) -- Actual: =C2=A0 \xc30d0403... (ciphertext returned silently =E2=80=94 FIP= S VIOLATION) SELECT pgp_sym_encrypt('secret', 'key', 'cipher-algo=3Dbf, compress-algo=3D= 0'); -- Expected: ERROR -- Actual: =C2=A0 \xc30d0404... (ciphertext returned silently =E2=80=94 FIP= S VIOLATION) SELECT pgp_sym_encrypt('secret', 'key', 'cipher-algo=3D3des, compress-algo=3D0'); -- Expected: ERROR -- Actual: =C2=A0 \xc30d0402... (ciphertext returned silently =E2=80=94 FIP= S VIOLATION) -- Step 6: Confirm FIPS-approved ciphers still work correctly SELECT pgp_sym_encrypt('secret', 'key', 'cipher-algo=3Daes256, compress-algo=3D0'); -- Actual: \xc30d0409... (correct =E2=80=94 AES-256 is FIPS approved) =C2= =A0=E2=9C=93 =3D=3D=3D Proposed Fix =3D=3D=3D I am happy to work on this. Add a FIPS cipher check in pgp_load_cipher() in contrib/pgcrypto/pgp.c. This function is the single chokepoint for all PGP cipher operations (encrypt, decrypt, session key encryption/decryption). A whitelist of FIPS 140-2/140-3 approved ciphers for PGP use would be: =C2=A0 PGP_SYM_AES_128, PGP_SYM_AES_192, PGP_SYM_AES_256 All other ciphers (PGP_SYM_BLOWFISH, PGP_SYM_CAST5, PGP_SYM_DES3, PGP_SYM_TWOFISH, etc.) should raise an error when CheckFIPSMode() returns true. The error message should be consistent with the one used for gen_salt/crypt: =C2=A0 ERROR: use of non-FIPS validated crypto not allowed when OpenSSL is = in FIPS mode Additionally, the pgcrypto documentation (doc/src/sgml/pgcrypto.sgml) should be updated to note which cipher-algo values for pgp_sym_encrypt are not FIPS 140-2/140-3 approved (bf, cast5, 3des) and that they will fail when OpenSSL FIPS mode is active with the fix applied. Related upstream work for reference: =C2=A0 - Commit 924d89a: Add fips_mode() SQL function =C2=A0 - Commit 035f99c: Add pgcrypto.builtin_crypto_enabled GUC Thank you for your time reviewing this.