Add CFB variant with 8-bit segment size.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov(a)gmail.com>
---
cfb.c | 72 ++++++++++++++++++++
cfb.h | 35 ++++++++++
nettle.texinfo | 106 ++++++++++++++++++++++++------
testsuite/cfb-test.c | 103 +++++++++++++++++++++++++++++
testsuite/testutils.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++
testsuite/testutils.h | 7 ++
6 files changed, 482 insertions(+), 19 deletions(-)
---
CFB8 support, contributed by Dmitry Eremin-Solenikov.
* cfb.c (cfb8_encrypt, cfb8_decrypt): New functions.
* cfb.h (CFB8_CTX, CFB8_SET_IV, CFB8_ENCRYPT, CFB8_DECRYPT): New
macros.
* testsuite/cfb-test.c: CFB8 test cases.
* testsuite/testutils.c (test_cipher_cfb8): New function.
* nettle.texinfo (CFB8): Documentation.
diff --git a/cfb.c b/cfb.c
index 805b8c4533a0..19cba4b5ba5d 100644
--- a/cfb.c
+++ b/cfb.c
@@ -162,3 +162,75 @@ cfb_decrypt(const void *ctx, nettle_cipher_func *f,
}
}
}
+
+/* CFB-8 uses slight optimization: it encrypts or decrypts up to block_size
+ * bytes and does memcpy/memxor afterwards */
+void
+cfb8_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)
+{
+ TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE * 2);
+ TMP_DECL(outbuf, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE);
+ TMP_ALLOC(buffer, block_size * 2);
+ TMP_ALLOC(outbuf, block_size);
+ uint8_t pos;
+
+ memcpy(buffer, iv, block_size);
+ pos = 0;
+ while (length)
+ {
+ uint8_t t;
+
+ if (pos == block_size)
+ {
+ memcpy(buffer, buffer + block_size, block_size);
+ pos = 0;
+ }
+
+ f(ctx, block_size, outbuf, buffer + pos);
+ t = *(dst++) = *(src++) ^ outbuf[0];
+ buffer[pos + block_size] = t;
+ length--;
+ pos ++;
+ }
+ memcpy(iv, buffer + pos, block_size);
+}
+
+void
+cfb8_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)
+{
+ TMP_DECL(buffer, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE * 2);
+ TMP_DECL(outbuf, uint8_t, NETTLE_MAX_CIPHER_BLOCK_SIZE * 2);
+ TMP_ALLOC(buffer, block_size * 2);
+ TMP_ALLOC(outbuf, block_size * 2);
+ uint8_t i = 0;
+
+ memcpy(buffer, iv, block_size);
+ memcpy(buffer + block_size, src,
+ length < block_size ? length : block_size);
+
+ while (length)
+ {
+
+ for (i = 0; i < length && i < block_size; i++)
+ f(ctx, block_size, outbuf + i, buffer + i);
+
+ memxor3(dst, src, outbuf, i);
+
+ length -= i;
+ src += i;
+ dst += i;
+
+ memcpy(buffer, buffer + block_size, block_size);
+ memcpy(buffer + block_size, src,
+ length < block_size ? length : block_size);
+
+ }
+
+ memcpy(iv, buffer + i, block_size);
+}
diff --git a/cfb.h b/cfb.h
index 16660df9b8ab..782ac133aa1c 100644
--- a/cfb.h
+++ b/cfb.h
@@ -45,6 +45,9 @@ extern "C" {
#define cfb_encrypt nettle_cfb_encrypt
#define cfb_decrypt nettle_cfb_decrypt
+#define cfb8_encrypt nettle_cfb8_encrypt
+#define cfb8_decrypt nettle_cfb8_decrypt
+
void
cfb_encrypt(const void *ctx, nettle_cipher_func *f,
size_t block_size, uint8_t *iv,
@@ -57,12 +60,28 @@ cfb_decrypt(const void *ctx, nettle_cipher_func *f,
size_t length, uint8_t *dst,
const uint8_t *src);
+void
+cfb8_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
+cfb8_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))
+#define CFB8_CTX CFB_CTX
+#define CFB8_SET_IV CFB_SET_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, \
@@ -80,6 +99,22 @@ memcpy((ctx)->iv, (data), sizeof((ctx)->iv))
sizeof((self)->iv), (self)->iv, \
(length), (dst), (src)))
+#define CFB8_ENCRYPT(self, f, length, dst, src) \
+ (0 ? ((f)(&(self)->ctx, ~(size_t) 0, \
+ (uint8_t *) 0, (const uint8_t *) 0)) \
+ : cfb8_encrypt((void *) &(self)->ctx, \
+ (nettle_cipher_func *) (f), \
+ sizeof((self)->iv), (self)->iv, \
+ (length), (dst), (src)))
+
+#define CFB8_DECRYPT(self, f, length, dst, src) \
+ (0 ? ((f)(&(self)->ctx, ~(size_t) 0, \
+ (uint8_t *) 0, (const uint8_t *) 0)) \
+ : cfb8_decrypt((void *) &(self)->ctx, \
+ (nettle_cipher_func *) (f), \
+ sizeof((self)->iv), (self)->iv, \
+ (length), (dst), (src)))
+
#ifdef __cplusplus
}
#endif
diff --git a/nettle.texinfo b/nettle.texinfo
index aa374449c527..f501cfbeb2d7 100644
--- a/nettle.texinfo
+++ b/nettle.texinfo
@@ -93,7 +93,7 @@ Cipher modes
* CBC::
* CTR::
-* CFB::
+* CFB and CFB8::
* GCM::
* CCM::
@@ -1904,21 +1904,21 @@ Book mode, @acronym{ECB}), leaks information.
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
+Feedback (@acronym{CFB} and @acronym{CFB8}) 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}, @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.
+Modes like @acronym{CBC}, @acronym{CTR}, @acronym{CFB} and @acronym{CFB8}
+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::
+* CFB and CFB8::
@end menu
@node CBC, CTR, Cipher modes, Cipher modes
@@ -2014,7 +2014,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, CFB, CBC, Cipher modes
+@node CTR, CFB and CFB8, CBC, Cipher modes
@comment node-name, next, previous, up
@subsection Counter mode
@@ -2090,18 +2090,21 @@ last three arguments define the source and destination area for the
operation.
@end deffn
-@node CFB, , CTR, Cipher modes
+@node CFB and CFB8, , CTR, Cipher modes
@comment node-name, next, previous, up
@subsection Cipher Feedback mode
@cindex Cipher Feedback Mode
-@cindex CFB Mode
+@cindex Cipher Feedback 8-bit Mode
+@cindex CFB Modes
+@cindex CFB8 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{}
+Cipher Feedback mode (@acronym{CFB}) and Cipher Feedback 8-bit mode
+(@acronym{CFB8}) being close relatives to both @acronym{CBC} mode and
+@acronym{CTR} mode borrow some characteristics from stream ciphers.
+
+For CFB 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.
@@ -2121,10 +2124,31 @@ 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.
+Cipher Feedback 8-bit mode (@acronym{CFB8}) transforms block cipher into a stream
+cipher. The message is encrypted byte after byte, not requiring any padding.
+
+If @code{E_k} is the encryption function of a block cipher, @code{b} is
+@code{E_k} block size, @code{IV} is the initialization vector, then the
+@code{n} plaintext bytes are transformed into @code{n} ciphertext bytes
+@code{C_1},@dots{} @code{C_n} as follows:
+
+@example
+I_1 = IV
+C_1 = E_k(I_1) [1..8] XOR M_1
+I_2 = I_1 [9..b] << 8 | C_1
+C_2 = E_k(I_2) [1..8] XOR M_2
+
+@dots{}
+
+I_(n-1) = I_(n-2) [9..b] << 8 | C_(n-2)
+C_(n-1) = E_k(I_(n-1)) [1..8] XOR M_(n-1)
+I_n = I_(n-1) [9..b] << 8 | C_(n-1)
+C_n = E_k(I_n) [1..8] XOR M_n
+@end example
+
+Nettle's includes functions for applying a block cipher in Cipher
+Feedback (@acronym{CFB}) and Cipher Feedback 8-bit (@acronym{CFB8})
+modes. 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})
@@ -2141,6 +2165,18 @@ When a message is encrypted using a sequence of calls to
is a multiple of the block size.
@end deftypefun
+@deftypefun {void} cfb8_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} cfb8_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{CFB8}
+mode. The final IV block processed is copied into @var{iv}
+before returning, so that a large message can be processed by a sequence of
+calls to @code{cfb8_encrypt}. Note that for @acronym{CFB8} mode internally
+uses encryption only function and hence @var{f} should always be the
+encryption function for the underlying block cipher.
+
+@end deftypefun
+
Like for @acronym{CBC}, there are also a couple of helper macros.
@deffn Macro CFB_CTX (@var{context_type}, @var{block_size})
@@ -2175,6 +2211,38 @@ last three arguments define the source and destination area for the
operation.
@end deffn
+@deffn Macro CFB8_CTX (@var{context_type}, @var{block_size})
+Expands to
+@example
+@{
+ context_type ctx;
+ uint8_t iv[block_size];
+@}
+@end example
+@end deffn
+
+@deffn Macro CFB8_SET_IV(@var{ctx}, @var{iv})
+First argument is a pointer to a context struct as defined by
+@code{CFB8_CTX}, and the second is a pointer to an initialization vector
+that is copied into that context.
+@end deffn
+
+@deffn Macro CFB8_ENCRYPT (@var{ctx}, @var{f}, @var{length}, @var{dst}, @var{src})
+A simpler way to invoke @code{cfb8_encrypt}. The first argument is a
+pointer to a context struct as defined by @code{CFB8_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 CFB8_DECRYPT (@var{ctx}, @var{f}, @var{length}, @var{dst}, @var{src})
+A simpler way to invoke @code{cfb8_decrypt}. The first argument is a
+pointer to a context struct as defined by @code{CFB8_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/cfb-test.c b/testsuite/cfb-test.c
index b59bee225bf8..b83233830b5a 100644
--- a/testsuite/cfb-test.c
+++ b/testsuite/cfb-test.c
@@ -6,6 +6,7 @@
/* Test with more data and inplace decryption, to check that the
* cfb_decrypt buffering works. */
#define CFB_BULK_DATA 10000
+#define CFB8_BULK_DATA CFB_BULK_DATA
static void
test_cfb_bulk(void)
@@ -64,9 +65,110 @@ test_cfb_bulk(void)
ASSERT (MEMEQ(CFB_BULK_DATA, clear, cipher));
}
+static void
+test_cfb8_bulk(void)
+{
+ struct knuth_lfib_ctx random;
+
+ uint8_t clear[CFB8_BULK_DATA];
+
+ uint8_t cipher[CFB8_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("f84bfd48206f5803 6ef86f4e69e9aec0");
+
+ struct CFB8_CTX(struct aes_ctx, AES_BLOCK_SIZE) aes;
+
+ knuth_lfib_init(&random, CFB8_BULK_DATA);
+ knuth_lfib_random(&random, CFB8_BULK_DATA, clear);
+
+ /* Byte that should not be overwritten */
+ cipher[CFB8_BULK_DATA] = 17;
+
+ aes_set_encrypt_key(&aes.ctx, 32, key);
+ CFB8_SET_IV(&aes, start_iv);
+
+ CFB8_ENCRYPT(&aes, aes_encrypt, CFB8_BULK_DATA, cipher, clear);
+
+ ASSERT(cipher[CFB8_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);
+ CFB8_SET_IV(&aes, start_iv);
+ CFB8_DECRYPT(&aes, aes_encrypt, CFB8_BULK_DATA, cipher, cipher);
+
+ ASSERT(cipher[CFB8_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(CFB8_BULK_DATA, clear, cipher));
+}
+
void
test_main(void)
{
+ /* From NIST spec 800-38a on AES modes.
+ *
+ * F.3 CFB Example Vectors
+ * F.3.7 CFB8-AES128.Encrypt
+ */
+
+ test_cipher_cfb8(&nettle_aes128,
+ SHEX("2b7e151628aed2a6abf7158809cf4f3c"),
+ SHEX("6bc1bee22e409f96e93d7e117393172a"
+ "ae2d"),
+ SHEX("3b79424c9c0dd436bace9e0ed4586a4f"
+ "32b9"),
+ SHEX("000102030405060708090a0b0c0d0e0f"));
+
+ /* From NIST spec 800-38a on AES modes.
+ *
+ * F.3 CFB Example Vectors
+ * F.3.9 CFB8-AES192.Encrypt
+ */
+
+ test_cipher_cfb8(&nettle_aes192,
+ SHEX("8e73b0f7da0e6452c810f32b809079e5"
+ "62f8ead2522c6b7b"),
+ SHEX("6bc1bee22e409f96e93d7e117393172a"
+ "ae2d"),
+ SHEX("cda2521ef0a905ca44cd057cbf0d47a0"
+ "678a"),
+ SHEX("000102030405060708090a0b0c0d0e0f"));
+
+ /* From NIST spec 800-38a on AES modes.
+ *
+ * F.3 CFB Example Vectors
+ * F.3.11 CFB8-AES256.Encrypt
+ */
+
+ test_cipher_cfb8(&nettle_aes256,
+ SHEX("603deb1015ca71be2b73aef0857d7781"
+ "1f352c073b6108d72d9810a30914dff4"),
+ SHEX("6bc1bee22e409f96e93d7e117393172a"
+ "ae2d"),
+ SHEX("dc1f1a8520a64db55fcc8ac554844e88"
+ "9700"),
+ SHEX("000102030405060708090a0b0c0d0e0f"));
+
/* From NIST spec 800-38a on AES modes.
*
* F.3 CFB Example Vectors
@@ -139,6 +241,7 @@ test_main(void)
SHEX("000102030405060708090a0b0c0d0e0f"));
test_cfb_bulk();
+ test_cfb8_bulk();
}
/*
diff --git a/testsuite/testutils.c b/testsuite/testutils.c
index 08471958fbc0..5ee9eda35264 100644
--- a/testsuite/testutils.c
+++ b/testsuite/testutils.c
@@ -423,6 +423,184 @@ test_cipher_cfb(const struct nettle_cipher *cipher,
free(iv);
}
+void
+test_cipher_cfb8(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);
+
+ cfb8_encrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, cleartext->data);
+
+ if (!MEMEQ(length, data, ciphertext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_decrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data2, data);
+
+ if (!MEMEQ(length, data2, cleartext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_encrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, data);
+
+ if (!MEMEQ(length, data, ciphertext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_decrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, data);
+
+ if (!MEMEQ(length, data, cleartext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_encrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, cleartext->data);
+
+ if (!MEMEQ(length, data, ciphertext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_decrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data2, data);
+
+ if (!MEMEQ(length, data2, cleartext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_encrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, data);
+
+ if (!MEMEQ(length, data, ciphertext->data))
+ {
+ fprintf(stderr, "CFB8 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);
+
+ cfb8_decrypt(ctx, cipher->encrypt,
+ cipher->block_size, iv,
+ length, data, data);
+
+ if (!MEMEQ(length, data, cleartext->data))
+ {
+ fprintf(stderr, "CFB8 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,
diff --git a/testsuite/testutils.h b/testsuite/testutils.h
index fbbba7b9fab5..ded57db6ab4f 100644
--- a/testsuite/testutils.h
+++ b/testsuite/testutils.h
@@ -129,6 +129,13 @@ test_cipher_cfb(const struct nettle_cipher *cipher,
const struct tstring *ciphertext,
const struct tstring *iv);
+void
+test_cipher_cfb8(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,
--
2.15.1