Skip to content

Commit 9ad1b3d

Browse files
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 <postgresql.wizard@gmail.com> Reviewed-by: Daniel Gustafsson <daniel@yesql.se> Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org> Discussion: https://postgr.es/m/CAPBGcbxo9ASzq14VTpQp3mnUJ5omdgTWUJOvWV0L6nNigWE5jw@mail.gmail.com
1 parent 760bf58 commit 9ad1b3d

File tree

4 files changed

+217
-1
lines changed

4 files changed

+217
-1
lines changed

contrib/pgcrypto/expected/rijndael.out

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,121 @@ select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes')
135135
Lets try a longer message.
136136
(1 row)
137137

138+
-- cfb
139+
SELECT encrypt(
140+
'\x00112233445566778899aabbccddeeff',
141+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
142+
'aes-cfb/pad:none');
143+
encrypt
144+
------------------------------------
145+
\xf28122856e1cf9a7216a30d111f3997f
146+
(1 row)
147+
148+
-- without padding, input not multiple of block size
149+
SELECT encrypt(
150+
'\x00112233445566778899aabbccddeeff00',
151+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
152+
'aes-cfb/pad:none');
153+
encrypt
154+
--------------------------------------
155+
\xf28122856e1cf9a7216a30d111f3997fcb
156+
(1 row)
157+
158+
-- key padding
159+
SELECT encrypt(
160+
'\x0011223344',
161+
'\x000102030405',
162+
'aes-cfb');
163+
encrypt
164+
--------------
165+
\x8145d1a0ef
166+
(1 row)
167+
168+
SELECT encrypt(
169+
'\x0011223344',
170+
'\x000102030405060708090a0b0c0d0e0f10111213',
171+
'aes-cfb');
172+
encrypt
173+
--------------
174+
\x52642c3b9c
175+
(1 row)
176+
177+
SELECT encrypt(
178+
'\x0011223344',
179+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
180+
'aes-cfb');
181+
encrypt
182+
--------------
183+
\xc93b4468a4
184+
(1 row)
185+
186+
-- empty data
187+
select encrypt('', 'foo', 'aes-cfb');
188+
encrypt
189+
---------
190+
\x
191+
(1 row)
192+
193+
-- 10 bytes key
194+
select encrypt('foo', '0123456789', 'aes-cfb');
195+
encrypt
196+
----------
197+
\x6f8ced
198+
(1 row)
199+
200+
-- 22 bytes key
201+
select encrypt('foo', '0123456789012345678901', 'aes-cfb');
202+
encrypt
203+
----------
204+
\xfb47d2
205+
(1 row)
206+
207+
-- decrypt
208+
select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb'), '0123456', 'aes-cfb'), 'escape');
209+
encode
210+
--------
211+
foo
212+
(1 row)
213+
214+
-- data not multiple of block size
215+
select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb') || '\x00'::bytea, '0123456', 'aes-cfb'), 'escape');
216+
encode
217+
---------
218+
foo\337
219+
(1 row)
220+
221+
-- bad padding
222+
-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes-cfb')
223+
-- with the 16th byte changed (s/c5/d5/) to corrupt the padding of the last block.)
224+
select encode(decrypt_iv('\xf9ad6817cb58d31dd9ba6571fbc4f55d56f65b631f0f437cb828', '0123456', 'abcd', 'aes-cfb'), 'escape');
225+
encode
226+
-------------------------------------------------
227+
abcdefghijklmnoq\243:\205o\x7F\x05z\276\x07\332
228+
(1 row)
229+
230+
-- iv
231+
select encrypt_iv('foo', '0123456', 'abcd', 'aes-cfb');
232+
encrypt_iv
233+
------------
234+
\xfea064
235+
(1 row)
236+
237+
select encode(decrypt_iv('\xfea064', '0123456', 'abcd', 'aes-cfb'), 'escape');
238+
encode
239+
--------
240+
foo
241+
(1 row)
242+
243+
-- long message
244+
select encrypt('Lets try a longer message.', '0123456789', 'aes-cfb');
245+
encrypt
246+
--------------------------------------------------------
247+
\x4586f6c6351055051f723b1b0aad52c877eaf0c421d18fd73a28
248+
(1 row)
249+
250+
select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes-cfb'), '0123456789', 'aes-cfb'), 'escape');
251+
encode
252+
----------------------------
253+
Lets try a longer message.
254+
(1 row)
255+

contrib/pgcrypto/openssl.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,36 @@ ossl_aes_cbc_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv
617617
return err;
618618
}
619619

620+
static int
621+
ossl_aes_cfb_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
622+
{
623+
OSSLCipher *od = c->ptr;
624+
int err;
625+
626+
err = ossl_aes_init(c, key, klen, iv);
627+
if (err)
628+
return err;
629+
630+
switch (od->klen)
631+
{
632+
case 128 / 8:
633+
od->evp_ciph = EVP_aes_128_cfb();
634+
break;
635+
case 192 / 8:
636+
od->evp_ciph = EVP_aes_192_cfb();
637+
break;
638+
case 256 / 8:
639+
od->evp_ciph = EVP_aes_256_cfb();
640+
break;
641+
default:
642+
/* shouldn't happen */
643+
err = PXE_CIPHER_INIT;
644+
break;
645+
}
646+
647+
return err;
648+
}
649+
620650
/*
621651
* aliases
622652
*/
@@ -636,6 +666,7 @@ static PX_Alias ossl_aliases[] = {
636666
{"rijndael", "aes-cbc"},
637667
{"rijndael-cbc", "aes-cbc"},
638668
{"rijndael-ecb", "aes-ecb"},
669+
{"rijndael-cfb", "aes-cfb"},
639670
{NULL}
640671
};
641672

@@ -707,6 +738,13 @@ static const struct ossl_cipher ossl_aes_cbc = {
707738
128 / 8, 256 / 8
708739
};
709740

741+
static const struct ossl_cipher ossl_aes_cfb = {
742+
ossl_aes_cfb_init,
743+
NULL, /* EVP_aes_XXX_cfb(), determined in init
744+
* function */
745+
128 / 8, 256 / 8
746+
};
747+
710748
/*
711749
* Special handlers
712750
*/
@@ -728,6 +766,7 @@ static const struct ossl_cipher_lookup ossl_cipher_types[] = {
728766
{"cast5-cbc", &ossl_cast_cbc},
729767
{"aes-ecb", &ossl_aes_ecb},
730768
{"aes-cbc", &ossl_aes_cbc},
769+
{"aes-cfb", &ossl_aes_cfb},
731770
{NULL}
732771
};
733772

contrib/pgcrypto/sql/rijndael.sql

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,56 @@ select encode(decrypt_iv('\x2c24cb7da91d6d5699801268b0f5adad', '0123456', 'abcd'
7070
-- long message
7171
select encrypt('Lets try a longer message.', '0123456789', 'aes');
7272
select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes'), '0123456789', 'aes'), 'escape');
73+
74+
-- cfb
75+
SELECT encrypt(
76+
'\x00112233445566778899aabbccddeeff',
77+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
78+
'aes-cfb/pad:none');
79+
80+
-- without padding, input not multiple of block size
81+
SELECT encrypt(
82+
'\x00112233445566778899aabbccddeeff00',
83+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
84+
'aes-cfb/pad:none');
85+
86+
-- key padding
87+
88+
SELECT encrypt(
89+
'\x0011223344',
90+
'\x000102030405',
91+
'aes-cfb');
92+
93+
SELECT encrypt(
94+
'\x0011223344',
95+
'\x000102030405060708090a0b0c0d0e0f10111213',
96+
'aes-cfb');
97+
98+
SELECT encrypt(
99+
'\x0011223344',
100+
'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
101+
'aes-cfb');
102+
103+
-- empty data
104+
select encrypt('', 'foo', 'aes-cfb');
105+
-- 10 bytes key
106+
select encrypt('foo', '0123456789', 'aes-cfb');
107+
-- 22 bytes key
108+
select encrypt('foo', '0123456789012345678901', 'aes-cfb');
109+
110+
-- decrypt
111+
select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb'), '0123456', 'aes-cfb'), 'escape');
112+
-- data not multiple of block size
113+
select encode(decrypt(encrypt('foo', '0123456', 'aes-cfb') || '\x00'::bytea, '0123456', 'aes-cfb'), 'escape');
114+
-- bad padding
115+
-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes-cfb')
116+
-- with the 16th byte changed (s/c5/d5/) to corrupt the padding of the last block.)
117+
select encode(decrypt_iv('\xf9ad6817cb58d31dd9ba6571fbc4f55d56f65b631f0f437cb828', '0123456', 'abcd', 'aes-cfb'), 'escape');
118+
119+
-- iv
120+
select encrypt_iv('foo', '0123456', 'abcd', 'aes-cfb');
121+
select encode(decrypt_iv('\xfea064', '0123456', 'abcd', 'aes-cfb'), 'escape');
122+
123+
-- long message
124+
select encrypt('Lets try a longer message.', '0123456789', 'aes-cfb');
125+
select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes-cfb'), '0123456789', 'aes-cfb'), 'escape');

doc/src/sgml/pgcrypto.sgml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,11 @@ decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
10821082
<literal>cbc</literal> &mdash; next block depends on previous (default)
10831083
</para>
10841084
</listitem>
1085+
<listitem>
1086+
<para>
1087+
<literal>cfb</literal> &mdash; next block depends on previous encrypted block
1088+
</para>
1089+
</listitem>
10851090
<listitem>
10861091
<para>
10871092
<literal>ecb</literal> &mdash; each block is encrypted separately (for
@@ -1112,7 +1117,8 @@ encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
11121117
</para>
11131118
<para>
11141119
In <function>encrypt_iv</function> and <function>decrypt_iv</function>, the
1115-
<parameter>iv</parameter> parameter is the initial value for the CBC mode;
1120+
<parameter>iv</parameter> parameter is the initial value for the CBC and
1121+
CFB mode;
11161122
it is ignored for ECB.
11171123
It is clipped or padded with zeroes if not exactly block size.
11181124
It defaults to all zeroes in the functions without this parameter.

0 commit comments

Comments
 (0)