]> git.ipfire.org Git - thirdparty/nettle.git/commitdiff
Add CFB8 - Cipher Feedback 8-bit block cipher mode
authorDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Wed, 17 Jan 2018 14:17:18 +0000 (17:17 +0300)
committerNiels Möller <nisse@lysator.liu.se>
Sat, 20 Jan 2018 10:47:31 +0000 (11:47 +0100)
Add CFB variant with 8-bit segment size.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
cfb.c
cfb.h
nettle.texinfo
testsuite/cfb-test.c
testsuite/testutils.c
testsuite/testutils.h

diff --git a/cfb.c b/cfb.c
index 805b8c4533a0ae6a13b8686727c1f9ee0001c23a..19cba4b5ba5dbfbd9a2455f7e6e8b9e4469baf4a 100644 (file)
--- 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 16660df9b8ab8077b94052cd34e26a8b8296f3ac..782ac133aa1c8e06c3820c35ee3349937007dc3c 100644 (file)
--- 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
index aa374449c527f5afacda258bd6b6734e8fb77fd9..f501cfbeb2d743f71ebdaee3ab49baf75e995701 100644 (file)
@@ -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
 
index b59bee225bf87d03fe7bfa05256ee2c651eb1a24..b83233830b5a7bd4f447d503246185c9bdbc9a3a 100644 (file)
@@ -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();
 }
 
 /*
index 08471958fbc00c10d7e9a96bebe56d8de78a5d1c..5ee9eda3526497c782d84c748d40de7888563c30 100644 (file)
@@ -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,
index fbbba7b9fab5cabb34eb50f44eeb9a4d6002ae0f..ded57db6ab4f24edf6c3ac29a979677dcb9edb79 100644 (file)
@@ -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,