summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gustafsson2025-02-14 20:18:37 +0000
committerDaniel Gustafsson2025-02-14 20:18:37 +0000
commit9ad1b3d01f369f1b204324857e58d9283ff0a527 (patch)
tree2434e8750c56d26178a3178f67e9b5469ed747ab
parent760bf588de91daa5426e3d7aaf77de5abde063b4 (diff)
pgcrypto: Add support for CFB mode in AES encryption
Cipher Feedback Mode, CFB, is a self-synchronizing stream cipher which is very similar to CBC performed in reverse. Since OpenSSL supports it, we can easily plug it into the existing cipher selection code without any need for infrastructure changes. This patch was simultaneously submitted by Umar Hayat and Vladyslav Nebozhyn, the latter whom suggested the feauture. The committed patch is Umar's version. Author: Umar Hayat <[email protected]> Reviewed-by: Daniel Gustafsson <[email protected]> Reviewed-by: Álvaro Herrera <[email protected]> Discussion: https://postgr.es/m/CAPBGcbxo9ASzq14VTpQp3mnUJ5omdgTWUJOvWV0L6nNigWE5jw@mail.gmail.com
-rw-r--r--contrib/pgcrypto/expected/rijndael.out118
-rw-r--r--contrib/pgcrypto/openssl.c39
-rw-r--r--contrib/pgcrypto/sql/rijndael.sql53
-rw-r--r--doc/src/sgml/pgcrypto.sgml8
4 files changed, 217 insertions, 1 deletions
diff --git a/contrib/pgcrypto/expected/rijndael.out b/contrib/pgcrypto/expected/rijndael.out
index 015ba4430d9..28e28b989fd 100644
--- a/contrib/pgcrypto/expected/rijndael.out
+++ b/contrib/pgcrypto/expected/rijndael.out
@@ -135,3 +135,121 @@ select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes')
Lets try a longer message.
(1 row)
+-- cfb
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cfb/pad:none');
+ encrypt
+------------------------------------
+ \xf28122856e1cf9a7216a30d111f3997f
+(1 row)
+
+-- without padding, input not multiple of block size
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff00',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cfb/pad:none');
+ encrypt
+--------------------------------------
+ \xf28122856e1cf9a7216a30d111f3997fcb
+(1 row)
+
+-- key padding
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405',
+'aes-cfb');
+ encrypt
+--------------
+ \x8145d1a0ef
+(1 row)
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f10111213',
+'aes-cfb');
+ encrypt
+--------------
+ \x52642c3b9c
+(1 row)
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
+'aes-cfb');
+ encrypt
+--------------
+ \xc93b4468a4
+(1 row)
+
+-- empty data
+select encrypt('', 'foo', 'aes-cfb');
+ encrypt
+---------
+ \x
+(1 row)
+
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'aes-cfb');
+ encrypt
+----------
+ \x6f8ced
+(1 row)
+
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'aes-cfb');
+ encrypt
+----------
+ \xfb47d2
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb'), '0123456', 'aes-cfb'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- data not multiple of block size
+select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb') || '\x00'::bytea, '0123456', 'aes-cfb'), 'escape');
+ encode
+---------
+ foo\337
+(1 row)
+
+-- bad padding
+-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes-cfb')
+-- with the 16th byte changed (s/c5/d5/) to corrupt the padding of the last block.)
+select encode(decrypt_iv('\xf9ad6817cb58d31dd9ba6571fbc4f55d56f65b631f0f437cb828', '0123456', 'abcd', 'aes-cfb'), 'escape');
+ encode
+-------------------------------------------------
+ abcdefghijklmnoq\243:\205o\x7F\x05z\276\x07\332
+(1 row)
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'aes-cfb');
+ encrypt_iv
+------------
+ \xfea064
+(1 row)
+
+select encode(decrypt_iv('\xfea064', '0123456', 'abcd', 'aes-cfb'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'aes-cfb');
+ encrypt
+--------------------------------------------------------
+ \x4586f6c6351055051f723b1b0aad52c877eaf0c421d18fd73a28
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes-cfb'), '0123456789', 'aes-cfb'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
index 75f40a2d034..f179e80c842 100644
--- a/contrib/pgcrypto/openssl.c
+++ b/contrib/pgcrypto/openssl.c
@@ -617,6 +617,36 @@ ossl_aes_cbc_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv
return err;
}
+static int
+ossl_aes_cfb_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ int err;
+
+ err = ossl_aes_init(c, key, klen, iv);
+ if (err)
+ return err;
+
+ switch (od->klen)
+ {
+ case 128 / 8:
+ od->evp_ciph = EVP_aes_128_cfb();
+ break;
+ case 192 / 8:
+ od->evp_ciph = EVP_aes_192_cfb();
+ break;
+ case 256 / 8:
+ od->evp_ciph = EVP_aes_256_cfb();
+ break;
+ default:
+ /* shouldn't happen */
+ err = PXE_CIPHER_INIT;
+ break;
+ }
+
+ return err;
+}
+
/*
* aliases
*/
@@ -636,6 +666,7 @@ static PX_Alias ossl_aliases[] = {
{"rijndael", "aes-cbc"},
{"rijndael-cbc", "aes-cbc"},
{"rijndael-ecb", "aes-ecb"},
+ {"rijndael-cfb", "aes-cfb"},
{NULL}
};
@@ -707,6 +738,13 @@ static const struct ossl_cipher ossl_aes_cbc = {
128 / 8, 256 / 8
};
+static const struct ossl_cipher ossl_aes_cfb = {
+ ossl_aes_cfb_init,
+ NULL, /* EVP_aes_XXX_cfb(), determined in init
+ * function */
+ 128 / 8, 256 / 8
+};
+
/*
* Special handlers
*/
@@ -728,6 +766,7 @@ static const struct ossl_cipher_lookup ossl_cipher_types[] = {
{"cast5-cbc", &ossl_cast_cbc},
{"aes-ecb", &ossl_aes_ecb},
{"aes-cbc", &ossl_aes_cbc},
+ {"aes-cfb", &ossl_aes_cfb},
{NULL}
};
diff --git a/contrib/pgcrypto/sql/rijndael.sql b/contrib/pgcrypto/sql/rijndael.sql
index a2766419980..de4e4da5e29 100644
--- a/contrib/pgcrypto/sql/rijndael.sql
+++ b/contrib/pgcrypto/sql/rijndael.sql
@@ -70,3 +70,56 @@ select encode(decrypt_iv('\x2c24cb7da91d6d5699801268b0f5adad', '0123456', 'abcd'
-- long message
select encrypt('Lets try a longer message.', '0123456789', 'aes');
select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes'), '0123456789', 'aes'), 'escape');
+
+-- cfb
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cfb/pad:none');
+
+-- without padding, input not multiple of block size
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff00',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cfb/pad:none');
+
+-- key padding
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405',
+'aes-cfb');
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f10111213',
+'aes-cfb');
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
+'aes-cfb');
+
+-- empty data
+select encrypt('', 'foo', 'aes-cfb');
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'aes-cfb');
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'aes-cfb');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb'), '0123456', 'aes-cfb'), 'escape');
+-- data not multiple of block size
+select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb') || '\x00'::bytea, '0123456', 'aes-cfb'), 'escape');
+-- bad padding
+-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes-cfb')
+-- with the 16th byte changed (s/c5/d5/) to corrupt the padding of the last block.)
+select encode(decrypt_iv('\xf9ad6817cb58d31dd9ba6571fbc4f55d56f65b631f0f437cb828', '0123456', 'abcd', 'aes-cfb'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'aes-cfb');
+select encode(decrypt_iv('\xfea064', '0123456', 'abcd', 'aes-cfb'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'aes-cfb');
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes-cfb'), '0123456789', 'aes-cfb'), 'escape');
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index a4d035eabdd..f87668dfaed 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -1084,6 +1084,11 @@ decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
</listitem>
<listitem>
<para>
+ <literal>cfb</literal> &mdash; next block depends on previous encrypted block
+ </para>
+ </listitem>
+ <listitem>
+ <para>
<literal>ecb</literal> &mdash; each block is encrypted separately (for
testing only)
</para>
@@ -1112,7 +1117,8 @@ encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
</para>
<para>
In <function>encrypt_iv</function> and <function>decrypt_iv</function>, the
- <parameter>iv</parameter> parameter is the initial value for the CBC mode;
+ <parameter>iv</parameter> parameter is the initial value for the CBC and
+ CFB mode;
it is ignored for ECB.
It is clipped or padded with zeroes if not exactly block size.
It defaults to all zeroes in the functions without this parameter.