1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Replace PostmasterRandom() with a stronger source, second attempt.

This adds a new routine, pg_strong_random() for generating random bytes,
for use in both frontend and backend. At the moment, it's only used in
the backend, but the upcoming SCRAM authentication patches need strong
random numbers in libpq as well.

pg_strong_random() is based on, and replaces, the existing implementation
in pgcrypto. It can acquire strong random numbers from a number of sources,
depending on what's available:

- OpenSSL RAND_bytes(), if built with OpenSSL
- On Windows, the native cryptographic functions are used
- /dev/urandom

Unlike the current pgcrypto function, the source is chosen by configure.
That makes it easier to test different implementations, and ensures that
we don't accidentally fall back to a less secure implementation, if the
primary source fails. All of those methods are quite reliable, it would be
pretty surprising for them to fail, so we'd rather find out by failing
hard.

If no strong random source is available, we fall back to using erand48(),
seeded from current timestamp, like PostmasterRandom() was. That isn't
cryptographically secure, but allows us to still work on platforms that
don't have any of the above stronger sources. Because it's not very secure,
the built-in implementation is only used if explicitly requested with
--disable-strong-random.

This replaces the more complicated Fortuna algorithm we used to have in
pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom,
so it doesn't seem worth the maintenance effort to keep that. pgcrypto
functions that require strong random numbers will be disabled with
--disable-strong-random.

Original patch by Magnus Hagander, tons of further work by Michael Paquier
and me.

Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com
This commit is contained in:
Heikki Linnakangas
2016-12-05 13:42:59 +02:00
parent 5dc851afde
commit fe0a0b5993
42 changed files with 1473 additions and 1105 deletions

View File

@ -1,7 +1,7 @@
# contrib/pgcrypto/Makefile
INT_SRCS = md5.c sha1.c sha2.c internal.c internal-sha2.c blf.c rijndael.c \
fortuna.c random.c pgp-mpi-internal.c imath.c
pgp-mpi-internal.c imath.c
INT_TESTS = sha2
OSSL_SRCS = openssl.c pgp-mpi-openssl.c

View File

@ -0,0 +1,42 @@
--
-- PGP compression support
--
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+
DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA==
=tbSn
-----END PGP MESSAGE-----
'), 'key', 'expect-compress-algo=1');
pgp_sym_decrypt
-----------------
Secret message
(1 row)
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'),
'key', 'expect-compress-algo=0');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'),
'key', 'expect-compress-algo=1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'),
'key', 'expect-compress-algo=2');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- level=0 should turn compression off
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret message', 'key',
'compress-algo=2, compress-level=0'),
'key', 'expect-compress-algo=0');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random

View File

@ -0,0 +1,424 @@
--
-- pgp_descrypt tests
--
-- Checking ciphers
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.blowfish.sha1.mdc.s2k3.z0
jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
=JcP+
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest
UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA==
=XtrP
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes192.sha1.mdc.s2k3.z0
jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI
5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g==
=rCZt
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes256.sha1.mdc.s2k3.z0
jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/
lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog==
=fB6S
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
-- Checking MDC modes
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.nomdc.s2k3.z0
jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+
u9YkgfJfsuRJmgQ9tmo=
=60ui
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE
8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q==
=moGf
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
-- Checking hashes
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.md5.mdc.s2k3.z0
jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO
KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA==
=NyLk
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m
/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw==
=FxbQ
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
-- Checking S2K modes
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k0.z0
jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw
Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw==
=YvkV
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k1.z0
jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK
4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz
=dbXm
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl
z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg==
=VJKg
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes192.sha1.mdc.s2k0.z0
jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E
Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw==
=cg+i
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes192.sha1.mdc.s2k1.z0
jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9
7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt
=aHmC
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes192.sha1.mdc.s2k3.z0
jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv
q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw==
=K0LS
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes256.sha1.mdc.s2k0.z0
jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu
rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w==
=RGts
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes256.sha1.mdc.s2k1.z0
jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO
+KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR
=SUrU
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes256.sha1.mdc.s2k3.z0
jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+
4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA==
=XZrG
-----END PGP MESSAGE-----
'), 'foobar');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
-- Checking longer passwords
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi
tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw==
=XKKG
-----END PGP MESSAGE-----
'), '0123456789abcdefghij');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6
CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg==
=gWDh
-----END PGP MESSAGE-----
'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt
FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q==
=OxOF
-----END PGP MESSAGE-----
'), 'x');
pgp_sym_decrypt
-----------------
Secret message.
(1 row)
-- Checking various data
select encode(digest(pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat1.aes.sha1.mdc.s2k3.z0
jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8
Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg==
=W/ik
-----END PGP MESSAGE-----
'), '0123456789abcdefghij'), 'sha1'), 'hex');
encode
------------------------------------------
0225e3ede6f2587b076d021a189ff60aad67e066
(1 row)
-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066
select encode(digest(pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat2.aes.sha1.mdc.s2k3.z0
jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB
SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
=Fxen
-----END PGP MESSAGE-----
'), '0123456789abcdefghij'), 'sha1'), 'hex');
encode
------------------------------------------
da39a3ee5e6b4b0d3255bfef95601890afd80709
(1 row)
-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709
select encode(digest(pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: dat3.aes.sha1.mdc.s2k3.z0
jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8
gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk
73Hb8m1yRhQK
=ivrD
-----END PGP MESSAGE-----
'), '0123456789abcdefghij'), 'sha1'), 'hex');
encode
------------------------------------------
5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
(1 row)
-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
-- Checking CRLF
select encode(digest(pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: crlf mess
ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
=mBP9
-----END PGP MESSAGE-----
'), 'key', 'convert-crlf=0'), 'sha1'), 'hex');
encode
------------------------------------------
9353062be7720f1446d30b9e75573a4833886784
(1 row)
-- expected: 9353062be7720f1446d30b9e75573a4833886784
select encode(digest(pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
Comment: crlf mess
ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
=mBP9
-----END PGP MESSAGE-----
'), 'key', 'convert-crlf=1'), 'sha1'), 'hex');
encode
------------------------------------------
7efefcab38467f7484d6fa43dc86cf5281bd78e2
(1 row)
-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2
-- check BUG #11905, problem with messages 6 less than a power of 2.
select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530);
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- expected: true
-- Negative tests
-- Decryption with a certain incorrect key yields an apparent Literal Data
-- packet reporting its content to be binary data. Ciphertext source:
-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave
-- rise to that property.
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV
VsxxqLSPzNLAeIspJk5G
=mSd/
-----END PGP MESSAGE-----
'), 'wrong-key', 'debug=1');
NOTICE: dbg: prefix_init: corrupt prefix
NOTICE: dbg: parse_literal_data: data type=b
NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
ERROR: Wrong key or corrupt data
-- Routine text/binary mismatch.
select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- Decryption with a certain incorrect key yields an apparent BZip2-compressed
-- plaintext. Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
-- until the random prefix gave rise to that property.
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP
GXsd65oYJZp3Khz0qfyn
=Nmpq
-----END PGP MESSAGE-----
'), 'wrong-key', 'debug=1');
NOTICE: dbg: prefix_init: corrupt prefix
NOTICE: dbg: parse_compressed_data: bzip2 unsupported
NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
ERROR: Wrong key or corrupt data
-- Routine use of BZip2 compression. Ciphertext source:
-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \
-- --personal-cipher-preferences aes --no-emit-version --batch \
-- --symmetric --passphrase key --armor
select pgp_sym_decrypt(dearmor('
-----BEGIN PGP MESSAGE-----
jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe
QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R
UCAAw2JRIISttRHMfDpDuZJpvYo=
=AZ9M
-----END PGP MESSAGE-----
'), 'key', 'debug=1');
NOTICE: dbg: parse_compressed_data: bzip2 unsupported
ERROR: Unsupported compression algorithm

View File

@ -0,0 +1,161 @@
--
-- PGP encrypt
--
-- ensure consistent test output regardless of the default bytea format
SET bytea_output TO escape;
select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- check whether the defaults are ok
select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
'key', 'expect-cipher-algo=aes128,
expect-disable-mdc=0,
expect-sess-key=0,
expect-s2k-mode=3,
expect-s2k-digest-algo=sha1,
expect-compress-algo=0
');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- maybe the expect- stuff simply does not work
select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
'key', 'expect-cipher-algo=bf,
expect-disable-mdc=1,
expect-sess-key=1,
expect-s2k-mode=0,
expect-s2k-digest-algo=md5,
expect-compress-algo=1
');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- bytea as text
select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- text as bytea
select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- algorithm change
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'),
'key', 'expect-cipher-algo=bf');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'),
'key', 'expect-cipher-algo=aes128');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'),
'key', 'expect-cipher-algo=aes192');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- s2k change
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'),
'key', 'expect-s2k-mode=0');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'),
'key', 'expect-s2k-mode=1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'),
'key', 'expect-s2k-mode=3');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- s2k count change
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-count=1024'),
'key', 'expect-s2k-count=1024');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- s2k_count rounds up
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'),
'key', 'expect-s2k-count=65000000');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- s2k digest change
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
'key', 'expect-s2k-digest-algo=md5');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'),
'key', 'expect-s2k-digest-algo=sha1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- sess key
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'),
'key', 'expect-sess-key=0');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'),
'key', 'expect-sess-key=1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'),
'key', 'expect-sess-key=1, expect-cipher-algo=bf');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'),
'key', 'expect-sess-key=1, expect-cipher-algo=aes192');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'),
'key', 'expect-sess-key=1, expect-cipher-algo=aes256');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- no mdc
select pgp_sym_decrypt(
pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'),
'key', 'expect-disable-mdc=1');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- crlf
select encode(pgp_sym_decrypt_bytea(
pgp_sym_encrypt(E'1\n2\n3\r\n', 'key', 'convert-crlf=1'),
'key'), 'hex');
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- conversion should be lossless
select encode(digest(pgp_sym_decrypt(
pgp_sym_encrypt(E'\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
'key', 'convert-crlf=1'), 'sha1'), 'hex') as result,
encode(digest(E'\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random

View File

@ -0,0 +1,62 @@
--
-- PGP Public Key Encryption
--
-- ensure consistent test output regardless of the default bytea format
SET bytea_output TO escape;
-- successful encrypt/decrypt
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=1;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=2;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=3;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=6;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- try with rsa-sign only
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=4;
ERROR: No encryption key found
-- try with secret key
select pgp_pub_decrypt(
pgp_pub_encrypt('Secret msg', dearmor(seckey)),
dearmor(seckey))
from keytbl where keytbl.id=1;
ERROR: Refusing to encrypt with secret key
-- does text-to-bytea works
select pgp_pub_decrypt_bytea(
pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=1;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random
-- and bytea-to-text?
select pgp_pub_decrypt(
pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)),
dearmor(seckey))
from keytbl where keytbl.id=1;
ERROR: pg_random_bytes() is not supported by this build
DETAIL: This functionality requires a source of strong random numbers
HINT: You need to rebuild PostgreSQL using --enable-strong-random

View File

@ -1,463 +0,0 @@
/*
* fortuna.c
* Fortuna-like PRNG.
*
* Copyright (c) 2005 Marko Kreen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* contrib/pgcrypto/fortuna.c
*/
#include "postgres.h"
#include <sys/time.h>
#include <time.h>
#include "px.h"
#include "rijndael.h"
#include "sha2.h"
#include "fortuna.h"
/*
* Why Fortuna-like: There does not seem to be any definitive reference
* on Fortuna in the net. Instead this implementation is based on
* following references:
*
* http://en.wikipedia.org/wiki/Fortuna_(PRNG)
* - Wikipedia article
* http://jlcooke.ca/random/
* - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
*/
/*
* There is some confusion about whether and how to carry forward
* the state of the pools. Seems like original Fortuna does not
* do it, resetting hash after each request. I guess expecting
* feeding to happen more often that requesting. This is absolutely
* unsuitable for pgcrypto, as nothing asynchronous happens here.
*
* J.L. Cooke fixed this by feeding previous hash to new re-initialized
* hash context.
*
* Fortuna predecessor Yarrow requires ability to query intermediate
* 'final result' from hash, without affecting it.
*
* This implementation uses the Yarrow method - asking intermediate
* results, but continuing with old state.
*/
/*
* Algorithm parameters
*/
/*
* How many pools.
*
* Original Fortuna uses 32 pools, that means 32'th pool is
* used not earlier than in 13th year. This is a waste in
* pgcrypto, as we have very low-frequancy seeding. Here
* is preferable to have all entropy usable in reasonable time.
*
* With 23 pools, 23th pool is used after 9 days which seems
* more sane.
*
* In our case the minimal cycle time would be bit longer
* than the system-randomness feeding frequency.
*/
#define NUM_POOLS 23
/* in microseconds */
#define RESEED_INTERVAL 100000 /* 0.1 sec */
/* for one big request, reseed after this many bytes */
#define RESEED_BYTES (1024*1024)
/*
* Skip reseed if pool 0 has less than this many
* bytes added since last reseed.
*/
#define POOL0_FILL (256/8)
/*
* Algorithm constants
*/
/* Both cipher key size and hash result size */
#define BLOCK 32
/* cipher block size */
#define CIPH_BLOCK 16
/* for internal wrappers */
#define MD_CTX SHA256_CTX
#define CIPH_CTX rijndael_ctx
struct fortuna_state
{
uint8 counter[CIPH_BLOCK];
uint8 result[CIPH_BLOCK];
uint8 key[BLOCK];
MD_CTX pool[NUM_POOLS];
CIPH_CTX ciph;
unsigned reseed_count;
struct timeval last_reseed_time;
unsigned pool0_bytes;
unsigned rnd_pos;
int tricks_done;
};
typedef struct fortuna_state FState;
/*
* Use our own wrappers here.
* - Need to get intermediate result from digest, without affecting it.
* - Need re-set key on a cipher context.
* - Algorithms are guaranteed to exist.
* - No memory allocations.
*/
static void
ciph_init(CIPH_CTX * ctx, const uint8 *key, int klen)
{
rijndael_set_key(ctx, (const uint32 *) key, klen, 1);
}
static void
ciph_encrypt(CIPH_CTX * ctx, const uint8 *in, uint8 *out)
{
rijndael_encrypt(ctx, (const uint32 *) in, (uint32 *) out);
}
static void
md_init(MD_CTX * ctx)
{
SHA256_Init(ctx);
}
static void
md_update(MD_CTX * ctx, const uint8 *data, int len)
{
SHA256_Update(ctx, data, len);
}
static void
md_result(MD_CTX * ctx, uint8 *dst)
{
SHA256_CTX tmp;
memcpy(&tmp, ctx, sizeof(*ctx));
SHA256_Final(dst, &tmp);
px_memset(&tmp, 0, sizeof(tmp));
}
/*
* initialize state
*/
static void
init_state(FState *st)
{
int i;
memset(st, 0, sizeof(*st));
for (i = 0; i < NUM_POOLS; i++)
md_init(&st->pool[i]);
}
/*
* Endianess does not matter.
* It just needs to change without repeating.
*/
static void
inc_counter(FState *st)
{
uint32 *val = (uint32 *) st->counter;
if (++val[0])
return;
if (++val[1])
return;
if (++val[2])
return;
++val[3];
}
/*
* This is called 'cipher in counter mode'.
*/
static void
encrypt_counter(FState *st, uint8 *dst)
{
ciph_encrypt(&st->ciph, st->counter, dst);
inc_counter(st);
}
/*
* The time between reseed must be at least RESEED_INTERVAL
* microseconds.
*/
static int
enough_time_passed(FState *st)
{
int ok;
struct timeval tv;
struct timeval *last = &st->last_reseed_time;
gettimeofday(&tv, NULL);
/* check how much time has passed */
ok = 0;
if (tv.tv_sec > last->tv_sec + 1)
ok = 1;
else if (tv.tv_sec == last->tv_sec + 1)
{
if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
ok = 1;
}
else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
ok = 1;
/* reseed will happen, update last_reseed_time */
if (ok)
memcpy(last, &tv, sizeof(tv));
px_memset(&tv, 0, sizeof(tv));
return ok;
}
/*
* generate new key from all the pools
*/
static void
reseed(FState *st)
{
unsigned k;
unsigned n;
MD_CTX key_md;
uint8 buf[BLOCK];
/* set pool as empty */
st->pool0_bytes = 0;
/*
* Both #0 and #1 reseed would use only pool 0. Just skip #0 then.
*/
n = ++st->reseed_count;
/*
* The goal: use k-th pool only 1/(2^k) of the time.
*/
md_init(&key_md);
for (k = 0; k < NUM_POOLS; k++)
{
md_result(&st->pool[k], buf);
md_update(&key_md, buf, BLOCK);
if (n & 1 || !n)
break;
n >>= 1;
}
/* add old key into mix too */
md_update(&key_md, st->key, BLOCK);
/* now we have new key */
md_result(&key_md, st->key);
/* use new key */
ciph_init(&st->ciph, st->key, BLOCK);
px_memset(&key_md, 0, sizeof(key_md));
px_memset(buf, 0, BLOCK);
}
/*
* Pick a random pool. This uses key bytes as random source.
*/
static unsigned
get_rand_pool(FState *st)
{
unsigned rnd;
/*
* This slightly prefers lower pools - that is OK.
*/
rnd = st->key[st->rnd_pos] % NUM_POOLS;
st->rnd_pos++;
if (st->rnd_pos >= BLOCK)
st->rnd_pos = 0;
return rnd;
}
/*
* update pools
*/
static void
add_entropy(FState *st, const uint8 *data, unsigned len)
{
unsigned pos;
uint8 hash[BLOCK];
MD_CTX md;
/* hash given data */
md_init(&md);
md_update(&md, data, len);
md_result(&md, hash);
/*
* Make sure the pool 0 is initialized, then update randomly.
*/
if (st->reseed_count == 0)
pos = 0;
else
pos = get_rand_pool(st);
md_update(&st->pool[pos], hash, BLOCK);
if (pos == 0)
st->pool0_bytes += len;
px_memset(hash, 0, BLOCK);
px_memset(&md, 0, sizeof(md));
}
/*
* Just take 2 next blocks as new key
*/
static void
rekey(FState *st)
{
encrypt_counter(st, st->key);
encrypt_counter(st, st->key + CIPH_BLOCK);
ciph_init(&st->ciph, st->key, BLOCK);
}
/*
* Hide public constants. (counter, pools > 0)
*
* This can also be viewed as spreading the startup
* entropy over all of the components.
*/
static void
startup_tricks(FState *st)
{
int i;
uint8 buf[BLOCK];
/* Use next block as counter. */
encrypt_counter(st, st->counter);
/* Now shuffle pools, excluding #0 */
for (i = 1; i < NUM_POOLS; i++)
{
encrypt_counter(st, buf);
encrypt_counter(st, buf + CIPH_BLOCK);
md_update(&st->pool[i], buf, BLOCK);
}
px_memset(buf, 0, BLOCK);
/* Hide the key. */
rekey(st);
/* This can be done only once. */
st->tricks_done = 1;
}
static void
extract_data(FState *st, unsigned count, uint8 *dst)
{
unsigned n;
unsigned block_nr = 0;
/* Should we reseed? */
if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0)
if (enough_time_passed(st))
reseed(st);
/* Do some randomization on first call */
if (!st->tricks_done)
startup_tricks(st);
while (count > 0)
{
/* produce bytes */
encrypt_counter(st, st->result);
/* copy result */
if (count > CIPH_BLOCK)
n = CIPH_BLOCK;
else
n = count;
memcpy(dst, st->result, n);
dst += n;
count -= n;
/* must not give out too many bytes with one key */
block_nr++;
if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
{
rekey(st);
block_nr = 0;
}
}
/* Set new key for next request. */
rekey(st);
}
/*
* public interface
*/
static FState main_state;
static int init_done = 0;
void
fortuna_add_entropy(const uint8 *data, unsigned len)
{
if (!init_done)
{
init_state(&main_state);
init_done = 1;
}
if (!data || !len)
return;
add_entropy(&main_state, data, len);
}
void
fortuna_get_bytes(unsigned len, uint8 *dst)
{
if (!init_done)
{
init_state(&main_state);
init_done = 1;
}
if (!dst || !len)
return;
extract_data(&main_state, len, dst);
}

View File

@ -1,38 +0,0 @@
/*
* fortuna.c
* Fortuna PRNG.
*
* Copyright (c) 2005 Marko Kreen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* contrib/pgcrypto/fortuna.h
*/
#ifndef __FORTUNA_H
#define __FORTUNA_H
void fortuna_get_bytes(unsigned len, uint8 *dst);
void fortuna_add_entropy(const uint8 *data, unsigned len);
#endif

View File

@ -38,7 +38,6 @@
#include "sha1.h"
#include "blf.h"
#include "rijndael.h"
#include "fortuna.h"
/*
* System reseeds should be separated at least this much.
@ -615,65 +614,3 @@ px_find_cipher(const char *name, PX_Cipher **res)
*res = c;
return 0;
}
/*
* Randomness provider
*/
static time_t seed_time = 0;
static time_t check_time = 0;
static void
system_reseed(void)
{
uint8 buf[1024];
int n;
time_t t;
int skip = 1;
t = time(NULL);
if (seed_time == 0)
skip = 0;
else if ((t - seed_time) < SYSTEM_RESEED_MIN)
skip = 1;
else if ((t - seed_time) > SYSTEM_RESEED_MAX)
skip = 0;
else if (check_time == 0 ||
(t - check_time) > SYSTEM_RESEED_CHECK_TIME)
{
check_time = t;
/* roll dice */
px_get_random_bytes(buf, 1);
skip = buf[0] >= SYSTEM_RESEED_CHANCE;
}
/* clear 1 byte */
px_memset(buf, 0, sizeof(buf));
if (skip)
return;
n = px_acquire_system_randomness(buf);
if (n > 0)
fortuna_add_entropy(buf, n);
seed_time = t;
px_memset(buf, 0, sizeof(buf));
}
int
px_get_random_bytes(uint8 *dst, unsigned count)
{
system_reseed();
fortuna_get_bytes(count, dst);
return 0;
}
int
px_add_entropy(const uint8 *data, unsigned count)
{
system_reseed();
fortuna_add_entropy(data, count);
return 0;
}

View File

@ -712,49 +712,3 @@ px_find_cipher(const char *name, PX_Cipher **res)
*res = c;
return 0;
}
static int openssl_random_init = 0;
/*
* OpenSSL random should re-feeded occasionally. From /dev/urandom
* preferably.
*/
static void
init_openssl_rand(void)
{
if (RAND_get_rand_method() == NULL)
{
#ifdef HAVE_RAND_OPENSSL
RAND_set_rand_method(RAND_OpenSSL());
#else
RAND_set_rand_method(RAND_SSLeay());
#endif
}
openssl_random_init = 1;
}
int
px_get_random_bytes(uint8 *dst, unsigned count)
{
int res;
if (!openssl_random_init)
init_openssl_rand();
res = RAND_bytes(dst, count);
if (res == 1)
return count;
return PXE_OSSL_RAND_ERROR;
}
int
px_add_entropy(const uint8 *data, unsigned count)
{
/*
* estimate 0 bits
*/
RAND_add(data, count, 0);
return 0;
}

View File

@ -34,6 +34,7 @@
#include <ctype.h>
#include "parser/scansup.h"
#include "utils/backend_random.h"
#include "utils/builtins.h"
#include "utils/uuid.h"
@ -422,7 +423,7 @@ PG_FUNCTION_INFO_V1(pg_random_bytes);
Datum
pg_random_bytes(PG_FUNCTION_ARGS)
{
int err;
#ifdef HAVE_STRONG_RANDOM
int len = PG_GETARG_INT32(0);
bytea *res;
@ -435,13 +436,13 @@ pg_random_bytes(PG_FUNCTION_ARGS)
SET_VARSIZE(res, VARHDRSZ + len);
/* generate result */
err = px_get_random_bytes((uint8 *) VARDATA(res), len);
if (err < 0)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Random generator error: %s", px_strerror(err))));
if (!pg_strong_random(VARDATA(res), len))
px_THROW_ERROR(PXE_NO_RANDOM);
PG_RETURN_BYTEA_P(res);
#else
px_THROW_ERROR(PXE_NO_RANDOM);
#endif
}
/* SQL function: gen_random_uuid() returns uuid */
@ -451,14 +452,14 @@ Datum
pg_random_uuid(PG_FUNCTION_ARGS)
{
uint8 *buf = (uint8 *) palloc(UUID_LEN);
int err;
/* generate random bits */
err = px_get_random_bytes(buf, UUID_LEN);
if (err < 0)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("Random generator error: %s", px_strerror(err))));
/*
* Generate random bits. pg_backend_random() will do here, we don't
* promis UUIDs to be cryptographically random, when built with
* --disable-strong-random.
*/
if (!pg_backend_random((char *) buf, UUID_LEN))
px_THROW_ERROR(PXE_NO_RANDOM);
/*
* Set magic numbers for a "version 4" (pseudorandom) UUID, see

View File

@ -37,6 +37,8 @@
#include "px.h"
#include "pgp.h"
#include "utils/backend_random.h"
#define MDC_DIGEST_LEN 20
#define STREAM_ID 0xE0
@ -477,14 +479,14 @@ init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
static int
write_prefix(PGP_Context *ctx, PushFilter *dst)
{
#ifdef HAVE_STRONG_RANDOM
uint8 prefix[PGP_MAX_BLOCK + 2];
int res,
bs;
bs = pgp_get_cipher_block_size(ctx->cipher_algo);
res = px_get_random_bytes(prefix, bs);
if (res < 0)
return res;
if (!pg_backend_random((char *) prefix, bs))
return PXE_NO_RANDOM;
prefix[bs + 0] = prefix[bs - 2];
prefix[bs + 1] = prefix[bs - 1];
@ -492,6 +494,9 @@ write_prefix(PGP_Context *ctx, PushFilter *dst)
res = pushf_write(dst, prefix, bs + 2);
px_memset(prefix, 0, bs + 2);
return res < 0 ? res : 0;
#else
return PXE_NO_RANDOM;
#endif
}
/*
@ -578,14 +583,15 @@ init_s2k_key(PGP_Context *ctx)
static int
init_sess_key(PGP_Context *ctx)
{
int res;
if (ctx->use_sess_key || ctx->pub_key)
{
#ifdef HAVE_STRONG_RANDOM
ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len);
if (res < 0)
return res;
if (!pg_strong_random((char *) ctx->sess_key, ctx->sess_key_len))
return PXE_NO_RANDOM;
#else
return PXE_NO_RANDOM;
#endif
}
else
{

View File

@ -57,17 +57,16 @@ mp_clear_free(mpz_t *a)
static int
mp_px_rand(uint32 bits, mpz_t *res)
{
int err;
#ifdef HAVE_STRONG_RANDOM
unsigned bytes = (bits + 7) / 8;
int last_bits = bits & 7;
uint8 *buf;
buf = px_alloc(bytes);
err = px_get_random_bytes(buf, bytes);
if (err < 0)
if (!pg_strong_random((char *) buf, bytes))
{
px_free(buf);
return err;
return PXE_NO_RANDOM;
}
/* clear unnecessary bits and set last bit to one */
@ -84,6 +83,9 @@ mp_px_rand(uint32 bits, mpz_t *res)
px_free(buf);
return 0;
#else
return PXE_NO_RANDOM;
#endif
}
static void

View File

@ -61,65 +61,6 @@ PG_FUNCTION_INFO_V1(pg_armor);
PG_FUNCTION_INFO_V1(pg_dearmor);
PG_FUNCTION_INFO_V1(pgp_armor_headers);
/*
* Mix a block of data into RNG.
*/
static void
add_block_entropy(PX_MD *md, text *data)
{
uint8 sha1[20];
px_md_reset(md);
px_md_update(md, (uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ);
px_md_finish(md, sha1);
px_add_entropy(sha1, 20);
px_memset(sha1, 0, 20);
}
/*
* Mix user data into RNG. It is for user own interests to have
* RNG state shuffled.
*/
static void
add_entropy(text *data1, text *data2, text *data3)
{
PX_MD *md;
uint8 rnd[3];
if (!data1 && !data2 && !data3)
return;
if (px_get_random_bytes(rnd, 3) < 0)
return;
if (px_find_digest("sha1", &md) < 0)
return;
/*
* Try to make the feeding unpredictable.
*
* Prefer data over keys, as it's rather likely that key is same in
* several calls.
*/
/* chance: 7/8 */
if (data1 && rnd[0] >= 32)
add_block_entropy(md, data1);
/* chance: 5/8 */
if (data2 && rnd[1] >= 160)
add_block_entropy(md, data2);
/* chance: 5/8 */
if (data3 && rnd[2] >= 160)
add_block_entropy(md, data3);
px_md_free(md);
px_memset(rnd, 0, sizeof(rnd));
}
/*
* returns src in case of no conversion or error
*/
@ -432,11 +373,7 @@ init_work(PGP_Context **ctx_p, int is_text,
VARSIZE(args) - VARHDRSZ, ex);
if (err)
{
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(err))));
}
px_THROW_ERROR(err);
if (ex->debug)
px_set_debug_handler(show_debug);
@ -459,11 +396,6 @@ encrypt_internal(int is_pubenc, int is_text,
struct debug_expect ex;
text *tmp_data = NULL;
/*
* Add data and key info RNG.
*/
add_entropy(data, key, NULL);
init_work(&ctx, is_text, args, &ex);
if (is_text && pgp_get_unicode_mode(ctx))
@ -516,9 +448,7 @@ encrypt_internal(int is_pubenc, int is_text,
pgp_free(ctx);
mbuf_free(src);
mbuf_free(dst);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(err))));
px_THROW_ERROR(err);
}
/* res_len includes VARHDRSZ */
@ -605,9 +535,7 @@ decrypt_internal(int is_pubenc, int need_text, text *data,
{
px_set_debug_handler(NULL);
mbuf_free(dst);
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(err))));
px_THROW_ERROR(err);
}
res_len = mbuf_steal_data(dst, &restmp);
@ -629,11 +557,6 @@ decrypt_internal(int is_pubenc, int need_text, text *data,
}
px_set_debug_handler(NULL);
/*
* add successful decryptions also into RNG
*/
add_entropy(res, key, keypsw);
return res;
}
@ -985,9 +908,7 @@ pg_dearmor(PG_FUNCTION_ARGS)
ret = pgp_armor_decode((uint8 *) VARDATA(data), data_len, &buf);
if (ret < 0)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(ret))));
px_THROW_ERROR(ret);
res = palloc(VARHDRSZ + buf.len);
SET_VARSIZE(res, VARHDRSZ + buf.len);
memcpy(VARDATA(res), buf.data, buf.len);
@ -1041,9 +962,7 @@ pgp_armor_headers(PG_FUNCTION_ARGS)
&state->nheaders, &state->keys,
&state->values);
if (res < 0)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(res))));
px_THROW_ERROR(res);
MemoryContextSwitchTo(oldcontext);
funcctx->user_fctx = state;
@ -1092,9 +1011,7 @@ pgp_key_id_w(PG_FUNCTION_ARGS)
res_len = pgp_get_keyid(buf, VARDATA(res));
mbuf_free(buf);
if (res_len < 0)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(res_len))));
px_THROW_ERROR(res_len);
SET_VARSIZE(res, VARHDRSZ + res_len);
PG_FREE_IF_COPY(data, 0);

View File

@ -39,7 +39,7 @@
static int
pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
{
int res;
#ifdef HAVE_STRONG_RANDOM
uint8 *buf,
*p;
int pad_len = res_len - 2 - data_len;
@ -49,11 +49,11 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
buf = px_alloc(res_len);
buf[0] = 0x02;
res = px_get_random_bytes(buf + 1, pad_len);
if (res < 0)
if (!pg_strong_random((char *) buf + 1, pad_len))
{
px_free(buf);
return res;
return PXE_NO_RANDOM;
}
/* pad must not contain zero bytes */
@ -62,26 +62,26 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
{
if (*p == 0)
{
res = px_get_random_bytes(p, 1);
if (res < 0)
if (!pg_strong_random((char *) p, 1))
{
px_memset(buf, 0, res_len);
px_free(buf);
break;
}
}
if (*p != 0)
p++;
}
if (res < 0)
{
px_memset(buf, 0, res_len);
px_free(buf);
return res;
}
buf[pad_len + 1] = 0;
memcpy(buf + pad_len + 2, data, data_len);
*res_p = buf;
return 0;
#else
return PXE_NO_RANDOM;
#endif
}
static int

View File

@ -34,6 +34,8 @@
#include "px.h"
#include "pgp.h"
#include "utils/backend_random.h"
static int
calc_s2k_simple(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
unsigned key_len)
@ -233,15 +235,14 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count)
case PGP_S2K_SIMPLE:
break;
case PGP_S2K_SALTED:
res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT))
return PXE_NO_RANDOM;
break;
case PGP_S2K_ISALTED:
res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
if (res < 0)
break;
res = px_get_random_bytes(&tmp, 1);
if (res < 0)
break;
if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT))
return PXE_NO_RANDOM;
if (!pg_backend_random((char *) &tmp, 1))
return PXE_NO_RANDOM;
s2k->iter = decide_s2k_iter(tmp, count);
break;
default:

View File

@ -34,6 +34,7 @@
#include "px.h"
#include "px-crypt.h"
#include "utils/backend_random.h"
static char *
run_crypt_des(const char *psw, const char *salt,
@ -132,7 +133,6 @@ static struct generator gen_list[] = {
int
px_gen_salt(const char *salt_type, char *buf, int rounds)
{
int res;
struct generator *g;
char *p;
char rbuf[16];
@ -153,9 +153,8 @@ px_gen_salt(const char *salt_type, char *buf, int rounds)
return PXE_BAD_SALT_ROUNDS;
}
res = px_get_random_bytes((uint8 *) rbuf, g->input_len);
if (res < 0)
return res;
if (!pg_backend_random(rbuf, g->input_len))
return PXE_NO_RANDOM;
p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
px_memset(rbuf, 0, sizeof(rbuf));

View File

@ -51,7 +51,6 @@ static const struct error_desc px_err_list[] = {
{PXE_CIPHER_INIT, "Cipher cannot be initialized ?"},
{PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
{PXE_DEV_READ_ERROR, "Error reading from random device"},
{PXE_OSSL_RAND_ERROR, "OpenSSL PRNG error"},
{PXE_BUG, "pgcrypto bug"},
{PXE_ARGUMENT_ERROR, "Illegal argument to function"},
{PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"},
@ -86,6 +85,39 @@ static const struct error_desc px_err_list[] = {
{0, NULL},
};
/*
* Call ereport(ERROR, ...), with an error code and message corresponding to
* the PXE_* error code given as argument.
*
* This is similar to px_strerror(err), but for some errors, we fill in the
* error code and detail fields more appropriately.
*/
void
px_THROW_ERROR(int err)
{
if (err == PXE_NO_RANDOM)
{
#ifdef HAVE_STRONG_RANDOM
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not generate a random number")));
#else
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("pg_random_bytes() is not supported by this build"),
errdetail("This functionality requires a source of strong random numbers"),
errhint("You need to rebuild PostgreSQL using --enable-strong-random")));
#endif
}
else
{
/* For other errors, use the message from the above list. */
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
errmsg("%s", px_strerror(err))));
}
}
const char *
px_strerror(int err)
{

View File

@ -71,7 +71,6 @@ void px_free(void *p);
#define PXE_CIPHER_INIT -8
#define PXE_HASH_UNUSABLE_FOR_HMAC -9
#define PXE_DEV_READ_ERROR -10
#define PXE_OSSL_RAND_ERROR -11
#define PXE_BUG -12
#define PXE_ARGUMENT_ERROR -13
#define PXE_UNKNOWN_SALT_ALGO -14
@ -189,11 +188,7 @@ int px_find_hmac(const char *name, PX_HMAC **res);
int px_find_cipher(const char *name, PX_Cipher **res);
int px_find_combo(const char *name, PX_Combo **res);
int px_get_random_bytes(uint8 *dst, unsigned count);
int px_add_entropy(const uint8 *data, unsigned count);
unsigned px_acquire_system_randomness(uint8 *dst);
void px_THROW_ERROR(int err) pg_attribute_noreturn();
const char *px_strerror(int err);
const char *px_resolve_alias(const PX_Alias *aliases, const char *name);

View File

@ -1,247 +0,0 @@
/*
* random.c
* Acquire randomness from system. For seeding RNG.
*
* Copyright (c) 2001 Marko Kreen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* contrib/pgcrypto/random.c
*/
#include "postgres.h"
#include "px.h"
#include "utils/memdebug.h"
/* how many bytes to ask from system random provider */
#define RND_BYTES 32
/*
* Try to read from /dev/urandom or /dev/random on these OS'es.
*
* The list can be pretty liberal, as the device not existing
* is expected event.
*/
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
|| defined(__NetBSD__) || defined(__DragonFly__) \
|| defined(__darwin__) || defined(__SOLARIS__) \
|| defined(__hpux) || defined(__HPUX__) \
|| defined(__CYGWIN__) || defined(_AIX)
#define TRY_DEV_RANDOM
#include <fcntl.h>
#include <unistd.h>
static int
safe_read(int fd, void *buf, size_t count)
{
int done = 0;
char *p = buf;
int res;
while (count)
{
res = read(fd, p, count);
if (res <= 0)
{
if (errno == EINTR)
continue;
return PXE_DEV_READ_ERROR;
}
p += res;
done += res;
count -= res;
}
return done;
}
static uint8 *
try_dev_random(uint8 *dst)
{
int fd;
int res;
fd = open("/dev/urandom", O_RDONLY, 0);
if (fd == -1)
{
fd = open("/dev/random", O_RDONLY, 0);
if (fd == -1)
return dst;
}
res = safe_read(fd, dst, RND_BYTES);
close(fd);
if (res > 0)
dst += res;
return dst;
}
#endif
/*
* Try to find randomness on Windows
*/
#ifdef WIN32
#define TRY_WIN32_GENRAND
#define TRY_WIN32_PERFC
#include <windows.h>
#include <wincrypt.h>
/*
* this function is from libtomcrypt
*
* try to use Microsoft crypto API
*/
static uint8 *
try_win32_genrand(uint8 *dst)
{
int res;
HCRYPTPROV h = 0;
res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
(CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET));
if (!res)
res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET);
if (!res)
return dst;
res = CryptGenRandom(h, RND_BYTES, dst);
if (res == TRUE)
dst += RND_BYTES;
CryptReleaseContext(h, 0);
return dst;
}
static uint8 *
try_win32_perfc(uint8 *dst)
{
int res;
LARGE_INTEGER time;
res = QueryPerformanceCounter(&time);
if (!res)
return dst;
memcpy(dst, &time, sizeof(time));
return dst + sizeof(time);
}
#endif /* WIN32 */
/*
* If we are not on Windows, then hopefully we are
* on a unix-like system. Use the usual suspects
* for randomness.
*/
#ifndef WIN32
#define TRY_UNIXSTD
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
/*
* Everything here is predictible, only needs some patience.
*
* But there is a chance that the system-specific functions
* did not work. So keep faith and try to slow the attacker down.
*/
static uint8 *
try_unix_std(uint8 *dst)
{
pid_t pid;
int x;
PX_MD *md;
struct timeval tv;
int res;
/* process id */
pid = getpid();
memcpy(dst, (uint8 *) &pid, sizeof(pid));
dst += sizeof(pid);
/* time */
gettimeofday(&tv, NULL);
memcpy(dst, (uint8 *) &tv, sizeof(tv));
dst += sizeof(tv);
/* pointless, but should not hurt */
x = random();
memcpy(dst, (uint8 *) &x, sizeof(x));
dst += sizeof(x);
/* hash of uninitialized stack and heap allocations */
res = px_find_digest("sha1", &md);
if (res >= 0)
{
uint8 *ptr;
uint8 stack[8192];
int alloc = 32 * 1024;
VALGRIND_MAKE_MEM_DEFINED(stack, sizeof(stack));
px_md_update(md, stack, sizeof(stack));
ptr = px_alloc(alloc);
VALGRIND_MAKE_MEM_DEFINED(ptr, alloc);
px_md_update(md, ptr, alloc);
px_free(ptr);
px_md_finish(md, dst);
px_md_free(md);
dst += 20;
}
return dst;
}
#endif
/*
* try to extract some randomness for initial seeding
*
* dst should have room for 1024 bytes.
*/
unsigned
px_acquire_system_randomness(uint8 *dst)
{
uint8 *p = dst;
#ifdef TRY_DEV_RANDOM
p = try_dev_random(p);
#endif
#ifdef TRY_WIN32_GENRAND
p = try_win32_genrand(p);
#endif
#ifdef TRY_WIN32_PERFC
p = try_win32_perfc(p);
#endif
#ifdef TRY_UNIXSTD
p = try_unix_std(p);
#endif
return p - dst;
}