Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1joLff-0000qT-7R for pgsql-bugs@arkaria.postgresql.org; Thu, 25 Jun 2020 06:47:40 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1joLfc-0007R8-U7 for pgsql-bugs@arkaria.postgresql.org; Thu, 25 Jun 2020 06:47:36 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1joLfb-0007R1-4X for pgsql-bugs@lists.postgresql.org; Thu, 25 Jun 2020 06:47:36 +0000 Received: from out5-smtp.messagingengine.com ([66.111.4.29]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1joLfX-0007Sm-7J for pgsql-bugs@lists.postgresql.org; Thu, 25 Jun 2020 06:47:34 +0000 Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id 0524F5C00DA; Thu, 25 Jun 2020 02:47:28 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute3.internal (MEProxy); Thu, 25 Jun 2020 02:47:28 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= date:from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=fm2; bh=M2s6wT+4CIwk1Dnrtf/791FH8r4 oZYjuRAW9UexTBGE=; b=uEfn3csmbfk7ACd7ge+9SOnv7euES01qOHmmLDTNunR x5DZJ30/UZt4MieZDUea/u2Z8rgyl2/EboxBkidr3wMaOQQvfz9Vl/4k3mjS7i7v XFlvpbYamlZCB0GZVfpWP8TMdw6m0OAT5uB0GGgYGPNvg7gvqrohBVId62AmwcGE JbGCxcGs/kw9xjTVIQtuxZpeRDZD9WvPh2+YoIjkmL0rf2ju0aX5LlaLt2kGxn+2 CH2fgZR0AoOb23r5EFdhuRiyVVQinUWlD3Z6np+yYB0c93RLCyfwr4DostC6NpiF Mh3IfAZ/K7+73ril663g2ko02bEWgebM62F5GMxIJBA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-proxy :x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; bh=M2s6wT +4CIwk1Dnrtf/791FH8r4oZYjuRAW9UexTBGE=; b=mJnrfA+yL3mQU6JCftmv6d oMyqSeF0ZL8d7JVHs2zZ/1X8F9oanJ632BelKYuDTs4aWWx7Xx/SbSJOv08sBX4I Khb+qxTB2jGEybbQdlTO6juztA82xHUcwh22VQF7tXNR9ODxCjOSoVTF5fSH5JQZ Saydv0N8Q0EfghGZt1qIWFpK3ygO+1RAZKp2zpft+HpQJlguYNyd/xefzXlUJBg3 45vRJxRT10GupCwmeHq+AQTswwNzxSxT594YPHEVxMX2tdZFle2NYh2CwM902iZL XxWb/ISQzJtNq6kHFMrJFqzJupfWx45EuKp2RIpyC2a8hnSRXIykvwWS1+5wlQlA == X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduhedrudekkedgudduvdcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfgh necuuegrihhlohhuthemuceftddtnecufghrlhcuvffnffculdejtddmnecujfgurhepff fhvffukfhfgggtuggjsehgtderredttddvnecuhfhrohhmpefoihgthhgrvghlucfrrghq uhhivghruceomhhitghhrggvlhesphgrqhhuihgvrhdrgiihiieqnecuggftrfgrthhtvg hrnhepvdegudeuhfdtueeltedtveejheehieevueeigeelteegleejleeiueeiheegvefh necukfhppeduuddurddutddvrddukedtrddukeehnecuvehluhhsthgvrhfuihiivgeptd enucfrrghrrghmpehmrghilhhfrhhomhepmhhitghhrggvlhesphgrqhhuihgvrhdrgiih ii X-ME-Proxy: Received: from paquier.xyz (ee0822lan1.rev.em-net.ne.jp [111.102.180.185]) by mail.messagingengine.com (Postfix) with ESMTPA id 95731328005D; Thu, 25 Jun 2020 02:46:26 -0400 (EDT) Date: Thu, 25 Jun 2020 15:45:41 +0900 From: Michael Paquier To: Kyotaro Horiguchi Cc: tgl@sss.pgh.pa.us, jeff.janes@gmail.com, frank.gagnepain@intm.fr, pgsql-bugs@lists.postgresql.org Subject: Re: BUG #16476: pgp_sym_encrypt_bytea with compress-level=6 : Wrong key or corrupt data Message-ID: <20200625064541.GH130132@paquier.xyz> References: <20200612.105131.63288858615945654.horikyota.ntt@gmail.com> <608105.1591927045@sss.pgh.pa.us> <20200612.145412.475791851624925277.horikyota.ntt@gmail.com> <20200624075921.GC130132@paquier.xyz> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="Rmm1Stw9KgbdL9/H" Content-Disposition: inline In-Reply-To: <20200624075921.GC130132@paquier.xyz> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --Rmm1Stw9KgbdL9/H Content-Type: multipart/mixed; boundary="HuscSE0D68UGttcd" Content-Disposition: inline --HuscSE0D68UGttcd Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Wed, Jun 24, 2020 at 04:59:21PM +0900, Michael Paquier wrote: > FYI, I have begun looking at this report, and I am reviewing the > proposed patch. Okay. First I found the test case you added a bit hard to parse, so I have refactored it with a CTE in charge of building the random string, with the main query doing the compression/decompression and a check making sure that the original string and the result match. I quite liked the logic with lpad() to append zeros if the computation with random()*256 returned a result less than 16, as well as the use of string_agg() for that purpose to build the string. I have also switched the second arguments of the functions to just use 'key', for readability. Using a random string sounds good to me here. It could always be possible that we finish with something less random, causing it to to become compressed but I'll believe in the rule of chaos here. Then, the fix you are proposing is to simply make sure that all the input from the source stream is properly consumed even after the zlib stream has ended in this corner case thanks to pullf_read(), and that sounds good to me. However, I had an idea slightly different than yours, consisting of simply reading the contents of the source before checking if there is any available in the decompressed buffer (the check on buf_data before the goto restart step). That makes the fix a bit simpler, without changing the logic. I am also attaching an extra script I used to validate this stuff based on the regression test of the patch, that has allowed me to check the logic for random strings up to 33kB, like that for example (this one took 8 mins on my laptop): select count(test_crypto(len)) from generate_series(0, 33000) as len; The inputs and outputs perfectly matched in all my tests, with the 16kB string being the only one failing in the range I have tested on HEAD, test passing with the patch of course. One or more extra pairs of eyes is welcome, so please feel free to look at the version attached. Thanks, -- Michael --HuscSE0D68UGttcd Content-Type: application/x-sql Content-Disposition: attachment; filename="crypto.sql" Content-Transfer-Encoding: quoted-printable =0ACREATE FUNCTION test_crypto(maxlen int) RETURNS bool AS $$=0AWITH random= _string AS=0A(=0A -- This generates a random string of 16366 bytes. This = is chosen=0A -- as random to not make it compressible, so as the decompres= sion=0A -- would work on a string with the same length, making the test=0A= -- predictible. lpad() ensures that the generated hexadecimal value=0A = -- is completed by extra characters if random() has generated a value=0A -= - strictly lower than 16.=0A SELECT string_agg(decode(lpad(to_hex((random(= )*256)::int), 2, '0'), 'hex'), '') as bytes=0A FROM generate_series(0, $= 1)=0A)=0ASELECT bytes =3D=0A pgp_sym_decrypt_bytea(=0A pgp_sym_encr= ypt_bytea(bytes, 'key',=0A 'compress-algo=3D1,co= mpress-level=3D1'),=0A 'key', 'expect-compress-a= lgo=3D1')=0A AS is_same=0A FROM random_string;=0A$$ LANGUAGE SQL STRICT= ;=0A --HuscSE0D68UGttcd Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="pgcrypto-zlib-v4.patch" Content-Transfer-Encoding: quoted-printable diff --git a/contrib/pgcrypto/expected/pgp-compression.out b/contrib/pgcryp= to/expected/pgp-compression.out index 32b350b8fe..d4c57feba3 100644 --- a/contrib/pgcrypto/expected/pgp-compression.out +++ b/contrib/pgcrypto/expected/pgp-compression.out @@ -48,3 +48,33 @@ select pgp_sym_decrypt( Secret message (1 row) =20 +-- check corner case involving an input string of 16kB, as per bug #16476. +SELECT setseed(0); + setseed=20 +--------- +=20 +(1 row) + +WITH random_string AS +( + -- This generates a random string of 16366 bytes. This is chosen + -- as random so that it does not get compressed, and the decompression + -- would work on a string with the same length as the origin, making the + -- test behavior more predictible. lpad() ensures that the generated + -- hexadecimal value is completed by extra zero characters if random() + -- has generated a value strictly lower than 16. + SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex= '), '') as bytes + FROM generate_series(0, 16365) +) +SELECT bytes =3D + pgp_sym_decrypt_bytea( + pgp_sym_encrypt_bytea(bytes, 'key', + 'compress-algo=3D1,compress-level=3D1'), + 'key', 'expect-compress-algo=3D1') + AS is_same + FROM random_string; + is_same=20 +--------- + t +(1 row) + diff --git a/contrib/pgcrypto/pgp-compress.c b/contrib/pgcrypto/pgp-compres= s.c index 0505bdee92..a37595dcc9 100644 --- a/contrib/pgcrypto/pgp-compress.c +++ b/contrib/pgcrypto/pgp-compress.c @@ -243,6 +243,20 @@ decompress_read(void *priv, PullFilter *src, int len, struct DecomprData *dec =3D priv; =20 restart: + if (dec->stream.avail_in =3D=3D 0) + { + uint8 *tmp; + + res =3D pullf_read(src, 8192, &tmp); + if (res < 0) + return res; + + Assert(res !=3D Z_STREAM_END || dec->buf_data > 0); + + dec->stream.next_in =3D tmp; + dec->stream.avail_in =3D res; + } + if (dec->buf_data > 0) { if (len > dec->buf_data) @@ -256,17 +270,6 @@ restart: if (dec->eof) return 0; =20 - if (dec->stream.avail_in =3D=3D 0) - { - uint8 *tmp; - - res =3D pullf_read(src, 8192, &tmp); - if (res < 0) - return res; - dec->stream.next_in =3D tmp; - dec->stream.avail_in =3D res; - } - dec->stream.next_out =3D dec->buf; dec->stream.avail_out =3D dec->buf_len; dec->pos =3D dec->buf; diff --git a/contrib/pgcrypto/sql/pgp-compression.sql b/contrib/pgcrypto/sq= l/pgp-compression.sql index ca9ee1fc00..87c59c6cab 100644 --- a/contrib/pgcrypto/sql/pgp-compression.sql +++ b/contrib/pgcrypto/sql/pgp-compression.sql @@ -28,3 +28,24 @@ select pgp_sym_decrypt( pgp_sym_encrypt('Secret message', 'key', 'compress-algo=3D2, compress-level=3D0'), 'key', 'expect-compress-algo=3D0'); + +-- check corner case involving an input string of 16kB, as per bug #16476. +SELECT setseed(0); +WITH random_string AS +( + -- This generates a random string of 16366 bytes. This is chosen + -- as random so that it does not get compressed, and the decompression + -- would work on a string with the same length as the origin, making the + -- test behavior more predictible. lpad() ensures that the generated + -- hexadecimal value is completed by extra zero characters if random() + -- has generated a value strictly lower than 16. + SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex= '), '') as bytes + FROM generate_series(0, 16365) +) +SELECT bytes =3D + pgp_sym_decrypt_bytea( + pgp_sym_encrypt_bytea(bytes, 'key', + 'compress-algo=3D1,compress-level=3D1'), + 'key', 'expect-compress-algo=3D1') + AS is_same + FROM random_string; --HuscSE0D68UGttcd-- --Rmm1Stw9KgbdL9/H Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAl70SBUACgkQnvQgOdby QH3rwBAAkeyGQxtbaBhRxONzb7aTr5fkePTFOdpENLHspMjsxM2TJgTmpQexfvAC 0dDd8XtcpgiVIdGlBbjJdxGm7TU/po9W5ov6U9jd+4tnJ3+y3IpVPh2Jj/bDVTi8 TUxXznHayQoaKo/XygqCqZdKgUom+WzpgogFuuSYL7/FEKV8Nhsv8qv+cYW2Jlwh 61CJj76BpIx0E7js1ym94aX+WbJnbg3lWMcfnEYsFvTB4/bp2656SERV32ADibKo KBHxCA26I6x3ZtQYJ3we1eaKD81g4mvSTtApDW9e23z0dm1W/xNHRNGQilCy8iuM hD/LbHseofGrKec/Sm6AqNT1MiL8tP5/YOvHmoOlipFNtHisWH+NwqyI0l1a4dgT 0lqALHbgGMWuGXal+N9/y7mEuOPELxx6wvFOBY307VDXb9P/x7hlZY6CiVBpzz60 xgMS+qMejXH9NE668L8YuSEkeK9YNbaf1M4TZvcVHv7lS6YDcYIWDkDKEmoT8HEk NIVpDMCV/ZTZ4PkpglxZbngM1Hiha5nXudiI5tv3BoFcfqfgxIqE5p+L7KhCGGIo BxLK/tPXxluxG0uAmYHU7kZvng/95ub4eUaS0/fdwt1nwpeM6xUjbQgLwZipbYVf dsvAKYIddUmCqt6Q5mfAdknj+htU0RgBZKbKm2eiN6HsOyLzVgc= =5ZfF -----END PGP SIGNATURE----- --Rmm1Stw9KgbdL9/H--