Commit 58e54b2f authored by Dmitry Baryshkov's avatar Dmitry Baryshkov Committed by Niels Möller

Add CFB8 - Cipher Feedback 8-bit block cipher mode

Add CFB variant with 8-bit segment size.
Signed-off-by: Dmitry Baryshkov's avatarDmitry Eremin-Solenikov <dbaryshkov@gmail.com>
parent c4a814d7
......@@ -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);
}
......@@ -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
......
......@@ -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
......
......@@ -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();
}
/*
......
......@@ -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,
......
......@@ -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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment