Signed-off-by: Dmitry Eremin-Solenikov dbaryshkov@gmail.com --- Makefile.in | 4 +- cfb.c | 176 +++++++++++++++++++++++++++ cfb.h | 87 ++++++++++++++ nettle.texinfo | 106 +++++++++++++++-- testsuite/.gitignore | 1 + testsuite/.test-rules.make | 3 + testsuite/Makefile.in | 2 +- testsuite/cfb-test.c | 287 +++++++++++++++++++++++++++++++++++++++++++++ testsuite/testutils.c | 179 ++++++++++++++++++++++++++++ testsuite/testutils.h | 7 ++ 10 files changed, 840 insertions(+), 12 deletions(-) create mode 100644 cfb.c create mode 100644 cfb.h create mode 100644 testsuite/cfb-test.c
diff --git a/Makefile.in b/Makefile.in index 2ff11c91..3be828ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -91,7 +91,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ camellia256-set-decrypt-key.c \ camellia256-meta.c \ cast128.c cast128-meta.c cbc.c \ - ccm.c ccm-aes128.c ccm-aes192.c ccm-aes256.c \ + ccm.c ccm-aes128.c ccm-aes192.c ccm-aes256.c cfb.c \ chacha-crypt.c chacha-core-internal.c \ chacha-poly1305.c chacha-poly1305-meta.c \ chacha-set-key.c chacha-set-nonce.c \ @@ -191,7 +191,7 @@ OPT_SOURCES = fat-x86_64.c fat-arm.c mini-gmp.c
HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ base16.h base64.h bignum.h buffer.h camellia.h cast128.h \ - cbc.h ccm.h chacha.h chacha-poly1305.h ctr.h \ + cbc.h ccm.h cfb.h chacha.h chacha-poly1305.h ctr.h \ curve25519.h des.h des-compat.h dsa.h dsa-compat.h eax.h \ ecc-curve.h ecc.h ecdsa.h eddsa.h \ gcm.h gost28147.h gosthash94.h hmac.h \ diff --git a/cfb.c b/cfb.c new file mode 100644 index 00000000..705568e7 --- /dev/null +++ b/cfb.c @@ -0,0 +1,176 @@ +/* cfb.c + + Cipher feedback mode. + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2001, 2011 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "cfb.h" + +#include "memxor.h" +#include "nettle-internal.h" + +void +cfb_encrypt(const void *ctx, nettle_cipher_func *f, + size_t block_size, uint8_t *iv, + size_t length, uint8_t *dst, + const uint8_t *src) +{ + if (src != dst) + { + uint8_t *p; + + for (p = iv; length >= block_size; p = dst, dst += block_size, src += block_size, length -= block_size) + { + f(ctx, block_size, dst, p); + memxor(dst, src, block_size); + } + + if (p != iv) + memcpy(iv, p, block_size); + + if (length) + { + TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE); + TMP_ALLOC(buffer, block_size); + + f(ctx, block_size, buffer, iv); + memxor3(dst, buffer, src, length); + memcpy(iv, dst, length); + } + } + else + { + TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE); + TMP_ALLOC(buffer, block_size); + uint8_t *p; + + for (p = iv; length >= block_size; p = dst, dst += block_size, length -= block_size) + { + f(ctx, block_size, buffer, p); + memxor(dst, buffer, block_size); + } + + if (p != iv) + memcpy(iv, p, block_size); + + if (length) + { + f(ctx, block_size, buffer, iv); + memxor(dst, buffer, length); + memcpy(iv, dst, length); + } + } +} + +/* Don't allocate any more space than this on the stack */ +#define CFB_BUFFER_LIMIT 512 + +void +cfb_decrypt(const void *ctx, nettle_cipher_func *f, + size_t block_size, uint8_t *iv, + size_t length, uint8_t *dst, + const uint8_t *src) +{ + if (!length) + return; + + if (src != dst) + { + size_t left = length % block_size; + + length -= left; + if (length >= block_size) + { + /* Decrypt in ECB mode */ + f(ctx, block_size, dst, iv); + f(ctx, length - block_size, dst + block_size, src); + memxor(dst, src, length); + memcpy(iv, src + length - block_size, block_size); + } + + if (left > 0) + { + TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE); + TMP_ALLOC(buffer, block_size); + + f(ctx, block_size, buffer, iv); + memxor3(dst + length, src + length, buffer, left); + memcpy(iv, src + length, left); + } + } + else + { + /* For in-place CFB, we decrypt into a temporary buffer of size + * at most CFB_BUFFER_LIMIT, and process that amount of data at + * a time. */ + + /* NOTE: We assume that block_size <= CFB_BUFFER_LIMIT */ + + TMP_DECL(buffer, uint8_t, CFB_BUFFER_LIMIT); + TMP_DECL(initial_iv, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE); + + size_t buffer_size; + size_t left; + + buffer_size = CFB_BUFFER_LIMIT - (CFB_BUFFER_LIMIT % block_size); + + TMP_ALLOC(buffer, buffer_size); + TMP_ALLOC(initial_iv, block_size); + + for ( ; length >= buffer_size; + length -= buffer_size, src += buffer_size, dst += buffer_size) + { + f(ctx, block_size, buffer, iv); + f(ctx, buffer_size - block_size, buffer + block_size, src); + memcpy(iv, src + buffer_size - block_size, block_size); + memxor3(dst, buffer, src, buffer_size); + } + + if (length % block_size) + left = length % block_size; + else + left = block_size; + f(ctx, block_size, buffer, iv); + f(ctx, length - left, buffer + block_size, src); + memcpy(iv, src + length - left, left); + if (length - left >= block_size) + memcpy(iv + left, src + length - left - block_size, block_size - left); + memxor3(dst, buffer, src, length); + } +} diff --git a/cfb.h b/cfb.h new file mode 100644 index 00000000..76134eeb --- /dev/null +++ b/cfb.h @@ -0,0 +1,87 @@ +/* cfb.h + + Cipher feedback mode. + + Copyright (C) 2015 Dmitry Eremin-Solenikov + Copyright (C) 2001 Niels Möller + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_CFB_H_INCLUDED +#define NETTLE_CFB_H_INCLUDED + +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define cfb_encrypt nettle_cfb_encrypt +#define cfb_decrypt nettle_cfb_decrypt + +void +cfb_encrypt(const void *ctx, nettle_cipher_func *f, + size_t block_size, uint8_t *iv, + size_t length, uint8_t *dst, + const uint8_t *src); + +void +cfb_decrypt(const void *ctx, nettle_cipher_func *f, + size_t block_size, uint8_t *iv, + size_t length, uint8_t *dst, + const uint8_t *src); + +#define CFB_CTX(type, size) \ +{ type ctx; uint8_t iv[size]; } + +#define CFB_SET_IV(ctx, data) \ +memcpy((ctx)->iv, (data), sizeof((ctx)->iv)) + +/* NOTE: Avoid using NULL, as we don't include anything defining it. */ +#define CFB_ENCRYPT(self, f, length, dst, src) \ + (0 ? ((f)(&(self)->ctx, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0)) \ + : cfb_encrypt((void *) &(self)->ctx, \ + (nettle_cipher_func *) (f), \ + sizeof((self)->iv), (self)->iv, \ + (length), (dst), (src))) + +#define CFB_DECRYPT(self, f, length, dst, src) \ + (0 ? ((f)(&(self)->ctx, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0)) \ + : cfb_decrypt((void *) &(self)->ctx, \ + (nettle_cipher_func *) (f), \ + sizeof((self)->iv), (self)->iv, \ + (length), (dst), (src))) + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_CFB_H_INCLUDED */ diff --git a/nettle.texinfo b/nettle.texinfo index b5f2701f..2195030d 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -93,6 +93,7 @@ Cipher modes
* CBC:: * CTR:: +* CFB:: * GCM:: * CCM::
@@ -1957,21 +1958,23 @@ a message that is larger than the cipher's block size. As explained in processing them independently with the block cipher (Electronic Code Book mode, @acronym{ECB}), leaks information.
-Besides @acronym{ECB}, Nettle provides a two other modes of operation: -Cipher Block Chaining (@acronym{CBC}), Counter mode (@acronym{CTR}), and -a couple of @acronym{AEAD} modes (@pxref{Authenticated encryption}). -@acronym{CBC} is widely used, but there are a few subtle issues of -information leakage, see, e.g., +Besides @acronym{ECB}, Nettle provides several other modes of operation: +Cipher Block Chaining (@acronym{CBC}), Counter mode (@acronym{CTR}), Cipher +Feedback (@acronym{CFB}) and a couple of @acronym{AEAD} modes +(@pxref{Authenticated encryption}). @acronym{CBC} is widely used, but +there are a few subtle issues of information leakage, see, e.g., @uref{http://www.kb.cert.org/vuls/id/958563, @acronym{SSH} @acronym{CBC} vulnerability}. Today, @acronym{CTR} is usually preferred over @acronym{CBC}.
-Modes like @acronym{CBC} and @acronym{CTR} provide @emph{no} message -authentication, and should always be used together with a @acronym{MAC} -(@pxref{Keyed hash functions}) or signature to authenticate the message. +Modes like @acronym{CBC}, @acronym{CTR} and @acronym{CFB} provide @emph{no} +message authentication, and should always be used together with a +@acronym{MAC} (@pxref{Keyed hash functions}) or signature to authenticate +the message.
@menu * CBC:: * CTR:: +* CFB:: @end menu
@node CBC, CTR, Cipher modes, Cipher modes @@ -2067,7 +2070,7 @@ These macros use some tricks to make the compiler display a warning if the types of @var{f} and @var{ctx} don't match, e.g. if you try to use an @code{struct aes_ctx} context with the @code{des_encrypt} function.
-@node CTR, , CBC, Cipher modes +@node CTR, CFB, CBC, Cipher modes @comment node-name, next, previous, up @subsection Counter mode
@@ -2143,6 +2146,91 @@ last three arguments define the source and destination area for the operation. @end deffn
+@node CFB, , CTR, Cipher modes +@comment node-name, next, previous, up +@subsection Cipher Feedback mode + +@cindex Cipher Feedback Mode +@cindex CFB Mode + +Cipher Feedback mode (@acronym{CFB}) being a close relative to both +@acronym{CBC} mode and @acronym{CTR} mode transforms block cipher into a +stream cipher. + +The message is divided into @code{n} blocks @code{M_1},@dots{} +@code{M_n}, where @code{M_n} is of size @code{m} which may be smaller +than the block size. Except for the last block, all the message blocks +must be of size equal to the cipher's block size. + +If @code{E_k} is the encryption function of a block cipher, @code{IV} is +the initialization vector, then the @code{n} plaintext blocks are +transformed into @code{n} ciphertext blocks @code{C_1},@dots{} +@code{C_n} as follows: + +@example +C_1 = E_k(IV) XOR M_1 +C_2 = E_k(C_1) XOR M_2 + +@dots{} + +C_(n-1) = E_k(C_(n - 2)) XOR M_(n-1) +C_n = E_k(C_(n - 1)) [1..m] XOR M_n +@end example + +Nettle's includes two functions for applying a block cipher in Cipher +Feedback Mode (@acronym{CFB}) mode, one for encryption and one for +decryption. These functions uses @code{void *} to pass cipher contexts +around. + +@deftypefun {void} cbc_encrypt (const void *@var{ctx}, nettle_cipher_func *@var{f}, size_t @var{block_size}, uint8_t *@var{iv}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +@deftypefunx {void} cbc_decrypt (const void *@var{ctx}, nettle_cipher_func *@var{f}, size_t @var{block_size}, uint8_t *@var{iv}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) + +Applies the encryption or decryption function @var{f} in @acronym{CBC} +mode. The final ciphertext block processed is copied into @var{iv} +before returning, so that a large message can be processed by a sequence of +calls to @code{cfb_encrypt}. Note that for @acronym{CFB} mode internally +uses encryption only function and hence @var{f} should always be the +encryption function for the underlying block cipher. + +When a message is encrypted using a sequence of calls to +@code{cfb_encrypt}, all but the last call @emph{must} use a length that is +a multiple of the block size. +@end deftypefun + +Like for @acronym{CFB}, there are also a couple of helper macros. + +@deffn Macro CFB_CTX (@var{context_type}, @var{block_size}) +Expands to +@example +@{ + context_type ctx; + uint8_t iv[block_size]; +@} +@end example +@end deffn + +@deffn Macro CFB_SET_IV(@var{ctx}, @var{iv}) +First argument is a pointer to a context struct as defined by +@code{CFB_CTX}, and the second is a pointer to an initialization vector +that is copied into that context. +@end deffn + +@deffn Macro CFB_ENCRYPT (@var{ctx}, @var{f}, @var{length}, @var{dst}, @var{src}) +A simpler way to invoke @code{cfb_encrypt}. The first argument is a +pointer to a context struct as defined by @code{CFB_CTX}, and the second +argument is an encryption function following Nettle's conventions. The +last three arguments define the source and destination area for the +operation. +@end deffn + +@deffn Macro CFB_DECRYPT (@var{ctx}, @var{f}, @var{length}, @var{dst}, @var{src}) +A simpler way to invoke @code{cfb_decrypt}. The first argument is a +pointer to a context struct as defined by @code{CFB_CTX}, and the second +argument is an encryption function following Nettle's conventions. The +last three arguments define the source and destination area for the +operation. +@end deffn + @node Authenticated encryption, Keyed hash functions, Cipher modes, Reference @comment node-name, next, previous, up
diff --git a/testsuite/.gitignore b/testsuite/.gitignore index e688a9c8..89d8c891 100644 --- a/testsuite/.gitignore +++ b/testsuite/.gitignore @@ -12,6 +12,7 @@ /cast128-test /cbc-test /ccm-test +/cfb-test /ctr-test /cxx-test /des-compat-test diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index d665330f..f6b3ebe2 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -115,6 +115,9 @@ knuth-lfib-test$(EXEEXT): knuth-lfib-test.$(OBJEXT) cbc-test$(EXEEXT): cbc-test.$(OBJEXT) $(LINK) cbc-test.$(OBJEXT) $(TEST_OBJS) -o cbc-test$(EXEEXT)
+cfb-test$(EXEEXT): cfb-test.$(OBJEXT) + $(LINK) cfb-test.$(OBJEXT) $(TEST_OBJS) -o cfb-test$(EXEEXT) + ctr-test$(EXEEXT): ctr-test.$(OBJEXT) $(LINK) ctr-test.$(OBJEXT) $(TEST_OBJS) -o ctr-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index d07268bd..d7b359e3 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -26,7 +26,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \ sha3-384-test.c sha3-512-test.c \ serpent-test.c twofish-test.c version-test.c \ knuth-lfib-test.c \ - cbc-test.c ctr-test.c gcm-test.c eax-test.c ccm-test.c \ + cbc-test.c ctr-test.c gcm-test.c eax-test.c ccm-test.c cfb-test.c \ poly1305-test.c chacha-poly1305-test.c \ hmac-test.c umac-test.c \ meta-hash-test.c meta-cipher-test.c\ diff --git a/testsuite/cfb-test.c b/testsuite/cfb-test.c new file mode 100644 index 00000000..37f0201f --- /dev/null +++ b/testsuite/cfb-test.c @@ -0,0 +1,287 @@ +#include "testutils.h" +#include "aes.h" +#include "cfb.h" +#include "knuth-lfib.h" + +/* Test with more data and inplace decryption, to check that the + * cfb_decrypt buffering works. */ +#define CFB_BULK_DATA 0x2710 /* 10000 */ + +static void +test_cfb_bulk(void) +{ + struct knuth_lfib_ctx random; + + uint8_t clear[CFB_BULK_DATA]; + + uint8_t cipher[CFB_BULK_DATA + 1]; + + const uint8_t *key = H("966c7bf00bebe6dc 8abd37912384958a" + "743008105a08657d dcaad4128eee38b3"); + + const uint8_t *start_iv = H("11adbff119749103 207619cfa0e8d13a"); + const uint8_t *end_iv = H("1fd0a9189b8480b7 b06a2b36ef5943ba"); + + struct CFB_CTX(struct aes_ctx, AES_BLOCK_SIZE) aes; + + knuth_lfib_init(&random, CFB_BULK_DATA); + knuth_lfib_random(&random, CFB_BULK_DATA, clear); + + /* Byte that should not be overwritten */ + cipher[CFB_BULK_DATA] = 17; + + aes_set_encrypt_key(&aes.ctx, 32, key); + CFB_SET_IV(&aes, start_iv); + + CFB_ENCRYPT(&aes, aes_encrypt, CFB_BULK_DATA, cipher, clear); + + ASSERT(cipher[CFB_BULK_DATA] == 17); + + if (verbose) + { + printf("IV after bulk encryption: "); + print_hex(AES_BLOCK_SIZE, aes.iv); + printf("\n"); + } + + ASSERT(MEMEQ(AES_BLOCK_SIZE, aes.iv, end_iv)); + + /* Decrypt, in place */ + aes_set_encrypt_key(&aes.ctx, 32, key); + CFB_SET_IV(&aes, start_iv); + CFB_DECRYPT(&aes, aes_encrypt, CFB_BULK_DATA, cipher, cipher); + + ASSERT(cipher[CFB_BULK_DATA] == 17); + + if (verbose) + { + printf("IV after bulk decryption: "); + print_hex(AES_BLOCK_SIZE, aes.iv); + printf("\n"); + } + + ASSERT (MEMEQ(AES_BLOCK_SIZE, aes.iv, end_iv)); + ASSERT (MEMEQ(CFB_BULK_DATA, clear, cipher)); +} + +void +test_main(void) +{ + /* From NIST spec 800-38a on AES modes. + * + * F.3 CFB Example Vectors + * F.3.13 CFB128-AES128.Encrypt + */ + + /* Intermediate values, blocks input to AES: + * + * 000102030405060708090a0b0c0d0e0f + * 3b3fd92eb72dad20333449f8e83cfb4a + * c8a64537a0b3a93fcde3cdad9f1ce58b + * 26751f67a3cbb140b1808cf187a4f4df + */ + test_cipher_cfb(&nettle_aes128, + SHEX("2b7e151628aed2a6abf7158809cf4f3c"), + SHEX("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"), + SHEX("3b3fd92eb72dad20333449f8e83cfb4a" + "c8a64537a0b3a93fcde3cdad9f1ce58b" + "26751f67a3cbb140b1808cf187a4f4df" + "c04b05357c5d1c0eeac4c66f9ff7f2e6"), + SHEX("000102030405060708090a0b0c0d0e0f")); + + /* F.2.3 CFB128-AES192.Encrypt */ + + /* Intermediate values, blocks input to AES: + * + * 000102030405060708090a0b0c0d0e0f + * cdc80d6fddf18cab34c25909c99a4174 + * 67ce7f7f81173621961a2b70171d3d7a + * 2e1e8a1dd59b88b1c8e60fed1efac4c9 + */ + + test_cipher_cfb(&nettle_aes192, + SHEX("8e73b0f7da0e6452c810f32b809079e5" + "62f8ead2522c6b7b"), + SHEX("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"), + SHEX("cdc80d6fddf18cab34c25909c99a4174" + "67ce7f7f81173621961a2b70171d3d7a" + "2e1e8a1dd59b88b1c8e60fed1efac4c9" + "c05f9f9ca9834fa042ae8fba584b09ff"), + SHEX("000102030405060708090a0b0c0d0e0f")); + + /* F.2.5 CFB128-AES256.Encrypt */ + + /* Intermediate values, blcoks input to AES: + * + * 000102030405060708090a0b0c0d0e0f + * dc7e84bfda79164b7ecd8486985d3860 + * 39ffed143b28b1c832113c6331e5407b + * df10132415e54b92a13ed0a8267ae2f9 + */ + + test_cipher_cfb(&nettle_aes256, + SHEX("603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4"), + SHEX("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"), + SHEX("dc7e84bfda79164b7ecd8486985d3860" + "39ffed143b28b1c832113c6331e5407b" + "df10132415e54b92a13ed0a8267ae2f9" + "75a385741ab9cef82031623d55b1e471"), + SHEX("000102030405060708090a0b0c0d0e0f")); + + test_cfb_bulk(); +} + +/* +F.3.13 CFB128-AES128.Encrypt +Key 2b7e151628aed2a6abf7158809cf4f3c +IV 000102030405060708090a0b0c0d0e0f +Segment #1 +Input Block 000102030405060708090a0b0c0d0e0f +Output Block 50fe67cc996d32b6da0937e99bafec60 +Plaintext 6bc1bee22e409f96e93d7e117393172a +Ciphertext 3b3fd92eb72dad20333449f8e83cfb4a +Segment #2 +Input Block 3b3fd92eb72dad20333449f8e83cfb4a +Output Block 668bcf60beb005a35354a201dab36bda +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Ciphertext c8a64537a0b3a93fcde3cdad9f1ce58b +Segment #3 +Input Block c8a64537a0b3a93fcde3cdad9f1ce58b +Output Block 16bd032100975551547b4de89daea630 +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Ciphertext 26751f67a3cbb140b1808cf187a4f4df +Segment #4 +Input Block 26751f67a3cbb140b1808cf187a4f4df +Output Block 36d42170a312871947ef8714799bc5f6 +Plaintext f69f2445df4f9b17ad2b417be66c3710 +Ciphertext c04b05357c5d1c0eeac4c66f9ff7f2e6 +F.3.14 CFB128-AES128.Decrypt +Key 2b7e151628aed2a6abf7158809cf4f3c +IV 000102030405060708090a0b0c0d0e0f +Segment #1 +Input Block 000102030405060708090a0b0c0d0e0f +Output Block 50fe67cc996d32b6da0937e99bafec60 +Ciphertext 3b3fd92eb72dad20333449f8e83cfb4a +Plaintext 6bc1bee22e409f96e93d7e117393172a +Segment #2 +Input Block 3b3fd92eb72dad20333449f8e83cfb4a +Output Block 668bcf60beb005a35354a201dab36bda +Ciphertext c8a64537a0b3a93fcde3cdad9f1ce58b +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Segment #3 +Input Block c8a64537a0b3a93fcde3cdad9f1ce58b +Output Block 16bd032100975551547b4de89daea630 +Ciphertext 26751f67a3cbb140b1808cf187a4f4df +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Segment #4 +Input Block 26751f67a3cbb140b1808cf187a4f4df +Output Block 36d42170a312871947ef8714799bc5f6 +Ciphertext c04b05357c5d1c0eeac4c66f9ff7f2e6 +Plaintext f69f2445df4f9b17ad2b417be66c3710 +F.3.15 CFB128-AES192.Encrypt +Key 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b +000102030405060708090a0b0c0d0e0f +Segment #1 +50 +IV +Input Block 000102030405060708090a0b0c0d0e0f +Output Block a609b38df3b1133dddff2718ba09565e +Plaintext 6bc1bee22e409f96e93d7e117393172a +Ciphertext cdc80d6fddf18cab34c25909c99a4174 +Segment #2 +Input Block cdc80d6fddf18cab34c25909c99a4174 +Output Block c9e3f5289f149abd08ad44dc52b2b32b +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Ciphertext 67ce7f7f81173621961a2b70171d3d7a +Segment #3 +Input Block 67ce7f7f81173621961a2b70171d3d7a +Output Block 1ed6965b76c76ca02d1dcef404f09626 +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Ciphertext 2e1e8a1dd59b88b1c8e60fed1efac4c9 +Segment #4 +Input Block 2e1e8a1dd59b88b1c8e60fed1efac4c9 +Output Block 36c0bbd976ccd4b7ef85cec1be273eef +Plaintext f69f2445df4f9b17ad2b417be66c3710 +Ciphertext c05f9f9ca9834fa042ae8fba584b09ff +F.3.16 CFB128-AES192.Decrypt +Key 8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b +IV 000102030405060708090a0b0c0d0e0f +Segment #1 +Input Block 000102030405060708090a0b0c0d0e0f +Output Block a609b38df3b1133dddff2718ba09565e +Ciphertext cdc80d6fddf18cab34c25909c99a4174 +Plaintext 6bc1bee22e409f96e93d7e117393172a +Segment #2 +Input Block cdc80d6fddf18cab34c25909c99a4174 +Output Block c9e3f5289f149abd08ad44dc52b2b32b +Ciphertext 67ce7f7f81173621961a2b70171d3d7a +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Segment #3 +Input Block 67ce7f7f81173621961a2b70171d3d7a +Output Block 1ed6965b76c76ca02d1dcef404f09626 +Ciphertext 2e1e8a1dd59b88b1c8e60fed1efac4c9 +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Segment #4 +Input Block 2e1e8a1dd59b88b1c8e60fed1efac4c9 +Output Block 36c0bbd976ccd4b7ef85cec1be273eef +Ciphertext c05f9f9ca9834fa042ae8fba584b09ff +Plaintext f69f2445df4f9b17ad2b417be66c3710 +F.3.17 CFB128-AES256.Encrypt +Key 603deb1015ca71be2b73aef0857d7781 +1f352c073b6108d72d9810a30914dff4 +IV 000102030405060708090a0b0c0d0e0f +Segment #1 +Input Block 000102030405060708090a0b0c0d0e0f +Output Block b7bf3a5df43989dd97f0fa97ebce2f4a +Plaintext 6bc1bee22e409f96e93d7e117393172a +Ciphertext dc7e84bfda79164b7ecd8486985d3860 +Segment #2 +Input Block dc7e84bfda79164b7ecd8486985d3860 +Output Block 97d26743252b1d54aca653cf744ace2a +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Ciphertext 39ffed143b28b1c832113c6331e5407b +Segment #3 +Input Block 39ffed143b28b1c832113c6331e5407b +Output Block efd80f62b6b9af8344c511b13c70b016 +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Ciphertext df10132415e54b92a13ed0a8267ae2f9 +Segment #4 +Input Block df10132415e54b92a13ed0a8267ae2f9 +Output Block 833ca131c5f655ef8d1a2346b3ddd361 +Plaintext f69f2445df4f9b17ad2b417be66c3710 +Ciphertext 75a385741ab9cef82031623d55b1e471 +F.3.18 CFB128-AES256.Decrypt +Key 603deb1015ca71be2b73aef0857d7781 +1f352c073b6108d72d9810a30914dff4 +IV 000102030405060708090a0b0c0d0e0f +Segment #1 +Input Block 000102030405060708090a0b0c0d0e0f +Output Block b7bf3a5df43989dd97f0fa97ebce2f4a +Ciphertext dc7e84bfda79164b7ecd8486985d3860 +Plaintext 6bc1bee22e409f96e93d7e117393172a +Segment #2 +Input Block dc7e84bfda79164b7ecd8486985d3860 +Output Block 97d26743252b1d54aca653cf744ace2a +Ciphertext 39ffed143b28b1c832113c6331e5407b +Plaintext ae2d8a571e03ac9c9eb76fac45af8e51 +Segment #3 +Input Block 39ffed143b28b1c832113c6331e5407b +Output Block efd80f62b6b9af8344c511b13c70b016 +Ciphertext df10132415e54b92a13ed0a8267ae2f9 +Plaintext 30c81c46a35ce411e5fbc1191a0a52ef +Segment #4 +Input Block df10132415e54b92a13ed0a8267ae2f9 +Output Block 833ca131c5f655ef8d1a2346b3ddd361 +Ciphertext 75a385741ab9cef82031623d55b1e471 +Plaintext f69f2445df4f9b17ad2b417be66c3710 +*/ diff --git a/testsuite/testutils.c b/testsuite/testutils.c index 6f897617..694298b7 100644 --- a/testsuite/testutils.c +++ b/testsuite/testutils.c @@ -4,6 +4,7 @@
#include "base16.h" #include "cbc.h" +#include "cfb.h" #include "ctr.h" #include "knuth-lfib.h" #include "macros.h" @@ -245,6 +246,184 @@ test_cipher_cbc(const struct nettle_cipher *cipher, }
void +test_cipher_cfb(const struct nettle_cipher *cipher, + const struct tstring *key, + const struct tstring *cleartext, + const struct tstring *ciphertext, + const struct tstring *iiv) +{ + void *ctx = xalloc(cipher->context_size); + uint8_t *data, *data2; + uint8_t *iv = xalloc(cipher->block_size); + size_t length; + + ASSERT (cleartext->length == ciphertext->length); + length = cleartext->length; + + ASSERT (key->length == cipher->key_size); + ASSERT (iiv->length == cipher->block_size); + + data = xalloc(length); + data2 = xalloc(length); + + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_encrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, cleartext->data); + + if (!MEMEQ(length, data, ciphertext->data)) + { + fprintf(stderr, "CFB encrypt failed:\nInput:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_decrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data2, data); + + if (!MEMEQ(length, data2, cleartext->data)) + { + fprintf(stderr, "CFB decrypt failed:\nInput:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data2); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + memcpy(data, cleartext->data, length); + + cfb_encrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, data); + + if (!MEMEQ(length, data, ciphertext->data)) + { + fprintf(stderr, "CFB inplace encrypt failed:\nInput:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_decrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, data); + + if (!MEMEQ(length, data, cleartext->data)) + { + fprintf(stderr, "CFB inplace decrypt failed:\nInput:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\n"); + FAIL(); + } + + /* Repeat all tests with incomplete last block */ + length -= 1; + + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_encrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, cleartext->data); + + if (!MEMEQ(length, data, ciphertext->data)) + { + fprintf(stderr, "CFB encrypt failed:\nInput:"); + print_hex(length, cleartext->data); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + print_hex(length, ciphertext->data); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_decrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data2, data); + + if (!MEMEQ(length, data2, cleartext->data)) + { + fprintf(stderr, "CFB decrypt failed:\nInput:"); + print_hex(length, ciphertext->data); + fprintf(stderr, "\nOutput: "); + print_hex(length, data2); + fprintf(stderr, "\nExpected:"); + print_hex(length, cleartext->data); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + memcpy(data, cleartext->data, length); + + cfb_encrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, data); + + if (!MEMEQ(length, data, ciphertext->data)) + { + fprintf(stderr, "CFB inplace encrypt failed:\nInput:"); + print_hex(length, cleartext->data); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + print_hex(length, ciphertext->data); + fprintf(stderr, "\n"); + FAIL(); + } + cipher->set_encrypt_key(ctx, key->data); + memcpy(iv, iiv->data, cipher->block_size); + + cfb_decrypt(ctx, cipher->encrypt, + cipher->block_size, iv, + length, data, data); + + if (!MEMEQ(length, data, cleartext->data)) + { + fprintf(stderr, "CFB inplace decrypt failed:\nInput:"); + print_hex(length, ciphertext->data); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + print_hex(length, cleartext->data); + fprintf(stderr, "\n"); + FAIL(); + } + + free(ctx); + free(data); + free(data2); + free(iv); +} + +void test_cipher_ctr(const struct nettle_cipher *cipher, const struct tstring *key, const struct tstring *cleartext, diff --git a/testsuite/testutils.h b/testsuite/testutils.h index 7c44772b..0a059557 100644 --- a/testsuite/testutils.h +++ b/testsuite/testutils.h @@ -123,6 +123,13 @@ test_cipher_cbc(const struct nettle_cipher *cipher, const struct tstring *iv);
void +test_cipher_cfb(const struct nettle_cipher *cipher, + const struct tstring *key, + const struct tstring *cleartext, + const struct tstring *ciphertext, + const struct tstring *iv); + +void test_cipher_ctr(const struct nettle_cipher *cipher, const struct tstring *key, const struct tstring *cleartext,