pgcrypto: Remove internal padding implementation

Started by Peter Eisentrautabout 4 years ago7 messageshackers
Jump to latest
#1Peter Eisentraut
peter_e@gmx.net

This is a rebase of the patch from [0]/messages/by-id/b1a62889-bb45-e5e0-d138-7a370a0a334f@enterprisedb.com. It removes the internal padding
implementation in pgcrypto and lets OpenSSL do it. The internal
implementation was once applicable to the non-OpenSSL code paths, but
those have since been removed.

[0]: /messages/by-id/b1a62889-bb45-e5e0-d138-7a370a0a334f@enterprisedb.com
/messages/by-id/b1a62889-bb45-e5e0-d138-7a370a0a334f@enterprisedb.com

Attachments:

v2-0001-pgcrypto-Remove-internal-padding-implementation.patchtext/plain; charset=UTF-8; name=v2-0001-pgcrypto-Remove-internal-padding-implementation.patchDownload+25-133
#2Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Peter Eisentraut (#1)
Re: pgcrypto: Remove internal padding implementation

On Mon, 2022-02-14 at 10:42 +0100, Peter Eisentraut wrote:

This is a rebase of the patch from [0]. It removes the internal padding
implementation in pgcrypto and lets OpenSSL do it. The internal
implementation was once applicable to the non-OpenSSL code paths, but
those have since been removed.

These removed parts looked interesting to me:

- else if (bpos % bs)
- {
- /* ERROR? */
- pad = bs - (bpos % bs);
- for (i = 0; i < pad; i++)
- bbuf[bpos++] = 0;
- }

- /* unpad */
- if (bs > 1 && cx->padding)
- {
- pad = res[*rlen - 1];
- pad_ok = 0;
- if (pad > 0 && pad <= bs && pad <= *rlen)
- {
- pad_ok = 1;
- for (i = *rlen - pad; i < *rlen; i++)
- if (res[i] != pad)
- {
- pad_ok = 0;
- break;
- }
- }
-
- if (pad_ok)
- *rlen -= pad;
- }

After this patch, bad padding is no longer ignored during decryption,
and encryption without padding now requires the input size to be a
multiple of the block size. To see the difference you can try the
following queries with and without the patch:

select encrypt_iv('foo', '0123456', 'abcd', 'aes/pad:none');
select encode(decrypt_iv('\xa21a9c15231465964e3396d32095e67eb52bab05f556a581621dee1b85385789', '0123456', 'abcd', 'aes'), 'escape');

Both changes seem correct to me. I can imagine some system out there
being somehow dependent on the prior decryption behavior to avoid a
padding oracle -- but if that's a concern, hopefully you're not using
unauthenticated encryption in the first place? It might be worth a note
in the documentation.

--Jacob

#3Peter Eisentraut
peter_e@gmx.net
In reply to: Jacob Champion (#2)
Re: pgcrypto: Remove internal padding implementation

On 15.02.22 00:07, Jacob Champion wrote:

After this patch, bad padding is no longer ignored during decryption,
and encryption without padding now requires the input size to be a
multiple of the block size. To see the difference you can try the
following queries with and without the patch:

select encrypt_iv('foo', '0123456', 'abcd', 'aes/pad:none');
select encode(decrypt_iv('\xa21a9c15231465964e3396d32095e67eb52bab05f556a581621dee1b85385789', '0123456', 'abcd', 'aes'), 'escape');

Interesting. I have added test cases about this. Could you describe
how you arrived at the second test case?

Both changes seem correct to me. I can imagine some system out there
being somehow dependent on the prior decryption behavior to avoid a
padding oracle -- but if that's a concern, hopefully you're not using
unauthenticated encryption in the first place? It might be worth a note
in the documentation.

Hmm. I started reading up on "padding oracle attack". I don't
understand it well enough yet to know whether this is a concern here.

Is there any reasonable meaning of the previous behaviors? Does bad
padding even give correct answers on decryption? What does encryption
without padding even do on incorrect input sizes?

Attachments:

v3-0001-pgcrypto-Remove-internal-padding-implementation.patchtext/plain; charset=UTF-8; name=v3-0001-pgcrypto-Remove-internal-padding-implementation.patchDownload+48-135
#4Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Peter Eisentraut (#3)
Re: pgcrypto: Remove internal padding implementation

On Mon, 2022-02-21 at 11:42 +0100, Peter Eisentraut wrote:

On 15.02.22 00:07, Jacob Champion wrote:

After this patch, bad padding is no longer ignored during decryption,
and encryption without padding now requires the input size to be a
multiple of the block size. To see the difference you can try the
following queries with and without the patch:

select encrypt_iv('foo', '0123456', 'abcd', 'aes/pad:none');
select encode(decrypt_iv('\xa21a9c15231465964e3396d32095e67eb52bab05f556a581621dee1b85385789', '0123456', 'abcd', 'aes'), 'escape');

Interesting. I have added test cases about this. Could you describe
how you arrived at the second test case?

Sure -- that second ciphertext is the result of running

SELECT encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes');

and then incrementing the last byte of the first block (i.e. the 16th
byte) to corrupt the padding in the last block.

Both changes seem correct to me. I can imagine some system out there
being somehow dependent on the prior decryption behavior to avoid a
padding oracle -- but if that's a concern, hopefully you're not using
unauthenticated encryption in the first place? It might be worth a note
in the documentation.

Hmm. I started reading up on "padding oracle attack". I don't
understand it well enough yet to know whether this is a concern here.

I *think* the only reasonable way to prevent that attack is by
authenticating with a MAC or an authenticated cipher, to avoid running
decryption on corrupted ciphertext in the first place. But I'm out of
my expertise here.

Is there any reasonable meaning of the previous behaviors?

I definitely don't think the previous behavior was reasonable. It's
possible that someone built a strange system on top of that
unreasonable behavior, but I hope not.

Does bad padding even give correct answers on decryption?

No -- or rather, I'm not really sure how to define "correct answer" for
garbage input. I especially don't like that two different ciphertexts
can silently decrypt to the same plaintext.

What does encryption without padding even do on incorrect input sizes?

Looks like it's being silently zero-padded by the previous code, which
doesn't seem very helpful to me. The documentation says "data must be
multiple of cipher block size" for the pad:none case, though I suppose
it doesn't say what happens if you ignore the "must".

--Jacob

#5Peter Eisentraut
peter_e@gmx.net
In reply to: Jacob Champion (#4)
Re: pgcrypto: Remove internal padding implementation

Interesting. I have added test cases about this. Could you describe
how you arrived at the second test case?

Sure -- that second ciphertext is the result of running

SELECT encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes');

and then incrementing the last byte of the first block (i.e. the 16th
byte) to corrupt the padding in the last block.

I have added this information to the test file.

Is there any reasonable meaning of the previous behaviors?

I definitely don't think the previous behavior was reasonable. It's
possible that someone built a strange system on top of that
unreasonable behavior, but I hope not.

Does bad padding even give correct answers on decryption?

No -- or rather, I'm not really sure how to define "correct answer" for
garbage input. I especially don't like that two different ciphertexts
can silently decrypt to the same plaintext.

What does encryption without padding even do on incorrect input sizes?

Looks like it's being silently zero-padded by the previous code, which
doesn't seem very helpful to me. The documentation says "data must be
multiple of cipher block size" for the pad:none case, though I suppose
it doesn't say what happens if you ignore the "must".

Right, the previous behaviors were clearly faulty. I have updated the
commit message to call out the behavior change more clearly.

This patch is now complete from my perspective.

Attachments:

v4-0001-pgcrypto-Remove-internal-padding-implementation.patchtext/plain; charset=UTF-8; name=v4-0001-pgcrypto-Remove-internal-padding-implementation.patchDownload+52-135
#6Nathan Bossart
nathandbossart@gmail.com
In reply to: Peter Eisentraut (#5)
Re: pgcrypto: Remove internal padding implementation

On Wed, Mar 16, 2022 at 11:12:06AM +0100, Peter Eisentraut wrote:

Right, the previous behaviors were clearly faulty. I have updated the
commit message to call out the behavior change more clearly.

This patch is now complete from my perspective.

I took a look at this patch and found nothing of concern.

--
Nathan Bossart
Amazon Web Services: https://aws.amazon.com

#7Peter Eisentraut
peter_e@gmx.net
In reply to: Nathan Bossart (#6)
Re: pgcrypto: Remove internal padding implementation

On 22.03.22 00:51, Nathan Bossart wrote:

On Wed, Mar 16, 2022 at 11:12:06AM +0100, Peter Eisentraut wrote:

Right, the previous behaviors were clearly faulty. I have updated the
commit message to call out the behavior change more clearly.

This patch is now complete from my perspective.

I took a look at this patch and found nothing of concern.

committed