summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in4
-rw-r--r--cfb.c165
-rw-r--r--cfb.h87
-rw-r--r--nettle.texinfo106
-rw-r--r--testsuite/.gitignore1
-rw-r--r--testsuite/.test-rules.make3
-rw-r--r--testsuite/Makefile.in2
-rw-r--r--testsuite/cfb-test.c287
-rw-r--r--testsuite/testutils.c179
-rw-r--r--testsuite/testutils.h7
10 files changed, 829 insertions, 12 deletions
diff --git a/Makefile.in b/Makefile.in
index 1aa2b29d..92014513 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 \
@@ -188,7 +188,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 gosthash94.h hmac.h \
diff --git a/cfb.c b/cfb.c
new file mode 100644
index 00000000..e507f704
--- /dev/null
+++ b/cfb.c
@@ -0,0 +1,165 @@
+/* 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)
+{
+ uint8_t *p;
+ TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE);
+
+ TMP_ALLOC(buffer, block_size);
+
+ if (src != dst)
+ {
+ 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);
+ }
+ }
+ else
+ {
+ for (p = iv; length >= block_size; p = dst, dst += block_size, src += 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);
+ memxor3(dst, buffer, src, length);
+ /* We do not care about updating IV here. This is the last call in
+ * message sequence and one has to set IV afterwards anyway */
+ }
+}
+
+/* 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 (src != dst)
+ {
+ size_t left = length % block_size;
+
+ length -= left;
+ if (length > 0)
+ {
+ /* Decrypt in ECB mode */
+ f(ctx, block_size, dst, iv);
+ f(ctx, length - block_size, dst + block_size, src);
+ memcpy(iv, src + length - block_size, block_size);
+ memxor(dst, src, length);
+ }
+
+ 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);
+ }
+ }
+ 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);
+
+ left = length % block_size;
+ length -= left;
+
+ while (length > 0)
+ {
+ size_t part = length > buffer_size ? buffer_size : length;
+
+ /* length is greater that zero and is divided by block_size, so it is
+ * not less than block_size. So does part */
+
+ f(ctx, block_size, buffer, iv);
+ f(ctx, part - block_size, buffer + block_size, src);
+ memcpy(iv, src + part - block_size, block_size);
+ memxor(dst, buffer, part);
+
+ length -= part;
+ src += part;
+ dst += part;
+ }
+
+ if (left > 0)
+ {
+ f(ctx, block_size, buffer, iv);
+ memxor(dst, buffer, left);
+ }
+ }
+}
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 6eada3db..23eed335 100644
--- a/nettle.texinfo
+++ b/nettle.texinfo
@@ -93,6 +93,7 @@ Cipher modes
* CBC::
* CTR::
+* CFB::
* GCM::
* CCM::
@@ -1884,21 +1885,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
@@ -1994,7 +1997,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
@@ -2070,6 +2073,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 borrows some characteristics
+from stream ciphers.
+
+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 (@acronym{CFB}) mode, one for encryption and one for
+decryption. These functions uses @code{void *} to pass cipher contexts
+around.
+
+@deftypefun {void} cfb_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} cfb_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{CFB}
+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{CBC}, 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 5220d894..5db4e789 100644
--- a/testsuite/.gitignore
+++ b/testsuite/.gitignore
@@ -12,6 +12,7 @@
/cast128-test
/cbc-test
/ccm-test
+/cfb-test
/chacha-poly1305-test
/chacha-test
/ctr-test
diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make
index a9732694..1f780310 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 62760430..80233e83 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -25,7 +25,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 cfb-test.c ctr-test.c gcm-test.c eax-test.c ccm-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..b59bee22
--- /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 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.3.15 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.3.17 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 05130c19..6ce13c4e 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 eb520453..fbbba7b9 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,