Hi Niels,
attached find two patches, comments inline.
On Tue, 2019-03-19 at 07:31 +0100, Niels Möller wrote:
> Simo Sorce <[email protected]> writes:
>
> > New patch attached, the diff has also been applied as an additional commit
> > to my xts_exploded tree in gitlab.
>
> Thanks. Looks pretty good, a few more comments below.
>
> Id din't look so closely at the tests, but it would be good with to test
> inplace opertion also with the aes-specific functions.
>
> For performance (which we don't need to focus that much on for the
> initial version), one thing to do would be to fill a buffer with
> consecutive T_k values, and do the encrypt/decrypt and memxor operations
> on several blocks at a time.
>
> > +For a plaintext length that is a perfect multiple of the XTS block size:
> > +@example
> > +T_1 = E_k2(IV) MUL a^0
> > +C_1 = E_k1(P_1 XOR T_1) XOR T_1
> > +
> > +@dots{}
> > +
> > +T_n = E_k2(IV) MUL a^(n-1)
> > +C_n = E_k1(P_n XOR T_n) XOR T_n
> > +@end example
>
> Nit: I'd write the T updates as
>
> T_1 = E_k2(IV)
> T_k = T_(k-1) MUL a
>
> since this is how to implement it, and no powering operation is needed.
Ok changed it.
> > +The functions @var{encf} @var{decf} are of type
> > +
> > +@code{void f (void *@var{ctx}, size_t @var{length}, uint8_t *@var{dst},
> > +const uint8_t *@var{src})},
>
> It seems the manual doesn't really have an entry for the
> nettle_cipher_func type. Maybe it should. Anyway, the type for the first
> argument is _const_ void *.
Fixed.
> > +/* shift one and XOR with 0x87. */
> > +/* src and dest can point to the same buffer for in-place operations */
> > +static void
> > +xts_shift(union nettle_block16 *dst,
> > + const union nettle_block16 *src)
> > +{
> > + uint8_t carry = src->b[15] >> 7;
> > + uint64_t b0 = LE_READ_UINT64(src->b);
> > + uint64_t b1 = LE_READ_UINT64(src->b+8);
> > + b1 = (b1 << 1) | (b0 >> 63);
> > + b0 = b0 << 1;
> > + LE_WRITE_UINT64(dst->b, b0);
> > + LE_WRITE_UINT64(dst->b+8, b1);
> > + dst->b[0] ^= 0x87 & -carry;
> > +}
>
> Looks right. It can be optimized later on.
Just for curiosity, would it be ok change,
LE_READ_UINT64/LE_WRITE_UINT64 to use uint64_t and have the macro use
__bswap64 if available or fall back to the original unoptimized code ?
(__bswap64 already knows how to use __builtin_bswap64 or falls back to
similar code as in LE_READ_UINT64)
Or do we need a new macro that expects aligned input/output and leave
the existing macro alone, to be used for unaligned input/output ?
> > +/*
> > + * prev is the block to steal from
> > + * curr is the input block to the last step
> > + * length is the partial block length
> > + * dst is the destination partial block
> > + * src is the source partial block
> > + *
> > + * In the Encryption case:
> > + * prev -> the output of the N-1 encryption step
> > + * curr -> the input to the Nth step (will be encrypted as Cn-1)
> > + * dst -> the final Cn partial block
> > + * src -> the final Pn partial block
> > + *
> > + * In the decryption case:
> > + * prev -> the output of the N-1 decryption step
> > + * curr -> the input to the Nth step (will be decrypted as Pn-1)
> > + * dst -> the final Pn partial block
> > + * src -> the final Cn partial block
> > + */
> > +static void
> > +xts_steal(uint8_t *prev, uint8_t *curr,
> > + size_t length, uint8_t *dst, const uint8_t *src)
> > +{
> > + /* copy the remaining in the current input block */
> > + memcpy(curr, src, length);
> > + /* fill the current block with the last blocksize - length
> > + * bytes of the previous block */
> > + memcpy(&curr[length], &prev[length], XTS_BLOCK_SIZE - length);
> > +
> > + /* This must be last or inplace operations will break
> > + * copy 'length' bytes of the previous block in the
> > + * destination block, which is the final partial block
> > + * returned to the caller */
> > + memcpy(dst, prev, length);
> > +}
>
> This is a bit confusing; first we write to the curr block, using data
> from src and prev. And next we copy from prev to dst; is that the
> swapping of the last two blocks? Maybe this could be made simpler, and a
> copy eliminatedif the main loops were
>
> for (;length >= 2*XTS_BLOCK_SIZE;
>
> For the arguments, it looks like prev could be const, and curr could be
> nettle_block16 (but changing the latter is perhaps a bit pointless,
> since we only treat it as a byte array with no obvious benefits from the
> alignment).
Ok, I took a stab at removing xts_steal completely in the second patch,
let me know what you think, I think I may like it better than my
original code and uses nettle_block16 for temporary storage to avoid a
copy.
> > +static void
> > +check_length(size_t length, uint8_t *dst)
> > +{
> > + assert(length >= XTS_BLOCK_SIZE);
> > + /* asserts may be compiled out, try to save the user by zeroing the dst
> > in
> > + * case the buffer contains sensitive data (like the clear text for
> > inplace
> > + * encryption) */
> > + if (length < XTS_BLOCK_SIZE)
> > + memxor(dst, dst, length);
> > +}
> > +
> > +/* works also for inplace encryption/decryption */
> > +
> > +void
> > +xts_encrypt_message(const void *enc_ctx, const void *twk_ctx,
> > + nettle_cipher_func *encf,
> > + const uint8_t *tweak, size_t length,
> > + uint8_t *dst, const uint8_t *src)
> > +{
> > + union nettle_block16 T;
> > + union nettle_block16 P;
> > +
> > + check_length(length, dst);
> > +
> > + encf(twk_ctx, XTS_BLOCK_SIZE, T.b, tweak);
> > +
> > + /* the zeroth power of alpha is the initial ciphertext value itself, so
> > we
> > + * skip shifting and do it at the end of each block operation instead */
> > + for (;length >= XTS_BLOCK_SIZE;
> > + length -= XTS_BLOCK_SIZE, src += XTS_BLOCK_SIZE, dst +=
> > XTS_BLOCK_SIZE)
> > + {
> > + memcpy(P.b, src, XTS_BLOCK_SIZE);
> > + memxor(P.b, T.b, XTS_BLOCK_SIZE); /* P -> PP */
>
> This is what memxor3 is for. (If it's more efficient in this case is not
> entirely
> obvious, though).
I did not know about memxor3, it makes the code better, esp in the
second patch, I think.
> > +/* XTS Mode with AES-128 */
> > +struct xts_aes128_ctx {
> > + struct aes128_ctx cipher;
> > + struct aes128_ctx tweak_cipher;
> > +};
>
> Could consider renaming it to xts_aes128_key, somewhat analogous to
> struct eax_key and struct gcm_key. This represents message-independent
> data, and then the name xts_aes128_ctx could be used in case we add an
> interface with a buffer and incremental encryption and decryption. Not
> clear to me if that's likely or not to ever be useful, though.
I did this, the reasoning makes sense to me.
HTH,
Simo.
--
Simo Sorce
Sr. Principal Software Engineer
Red Hat, Inc
From b8dfe527eb6f061baa202982c3bc495511a1535e Mon Sep 17 00:00:00 2001
From: Simo Sorce <[email protected]>
Date: Thu, 4 Oct 2018 14:38:50 -0400
Subject: [PATCH 1/2] Add support for XTS encryption mode
XEX encryption mode with tweak and ciphertext stealing (XTS) is
standardized in IEEE 1619 and generally used for storage devices.
Signed-off-by: Simo Sorce <[email protected]>
---
Makefile.in | 5 +-
nettle.texinfo | 147 ++++++++++++++++++++++++++-
testsuite/.gitignore | 1 +
testsuite/.test-rules.make | 3 +
testsuite/Makefile.in | 2 +-
testsuite/xts-test.c | 173 +++++++++++++++++++++++++++++++
xts-aes128.c | 77 ++++++++++++++
xts-aes256.c | 77 ++++++++++++++
xts.c | 202 +++++++++++++++++++++++++++++++++++++
xts.h | 124 +++++++++++++++++++++++
10 files changed, 806 insertions(+), 5 deletions(-)
create mode 100644 testsuite/xts-test.c
create mode 100644 xts-aes128.c
create mode 100644 xts-aes256.c
create mode 100644 xts.c
create mode 100644 xts.h
diff --git a/Makefile.in b/Makefile.in
index 83250cf3..440de9f7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -135,7 +135,8 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \
umac32.c umac64.c umac96.c umac128.c \
version.c \
write-be32.c write-le32.c write-le64.c \
- yarrow256.c yarrow_key_event.c
+ yarrow256.c yarrow_key_event.c \
+ xts.c xts-aes128.c xts-aes256.c
hogweed_SOURCES = sexp.c sexp-format.c \
sexp-transport.c sexp-transport-format.c \
@@ -206,7 +207,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \
pgp.h pkcs1.h pss.h pss-mgf1.h realloc.h ripemd160.h rsa.h \
salsa20.h sexp.h \
serpent.h sha.h sha1.h sha2.h sha3.h twofish.h \
- umac.h yarrow.h poly1305.h
+ umac.h yarrow.h xts.h poly1305.h
INSTALL_HEADERS = $(HEADERS) version.h @IF_MINI_GMP@ mini-gmp.h
diff --git a/nettle.texinfo b/nettle.texinfo
index 9806bdc1..e79cb08c 100644
--- a/nettle.texinfo
+++ b/nettle.texinfo
@@ -2001,7 +2001,8 @@ 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 @acronym{CFB8}) and a couple of @acronym{AEAD}
+Feedback (@acronym{CFB} and @acronym{CFB8}), XEX-based tweaked-codebook mode
+with ciphertext stealing (@acronym{XTS}) 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}
@@ -2016,6 +2017,7 @@ authenticate the message.
* CBC::
* CTR::
* CFB and CFB8::
+* XTS::
@end menu
@node CBC, CTR, Cipher modes, Cipher modes
@@ -2187,7 +2189,7 @@ last three arguments define the source and destination area for the
operation.
@end deffn
-@node CFB and CFB8, , CTR, Cipher modes
+@node CFB and CFB8, XTS, CTR, Cipher modes
@comment node-name, next, previous, up
@subsection Cipher Feedback mode
@@ -2340,6 +2342,147 @@ conventions. The last three arguments define the source and destination
area for the operation.
@end deffn
+@node XTS, , CFB and CFB8, Cipher modes
+@comment node-name, next, previous, up
+@subsection XEX-based tweaked-codebook mode with ciphertext stealing
+
+@cindex XEX-based tweaked-codebook mode with ciphertext stealing
+@cindex XTS Mode
+
+
+XEX-based tweaked-codebook mode with ciphertext stealing (@acronym{XTS}) is
+a block mode like (@acronym{CBC}) but tweaked to be able to encrypt partial
+blocks via a technique called ciphertext stealing, where the last complete
+block of ciphertext is split and part returned as the last block and part
+used as plaintext for the second to last block.
+This mode is principally used to encrypt data at rest where it is not possible
+to store additional metadata or blocks larger than the plain text. The most
+common usage is for disk encryption. Due to the fact that ciphertext expansion
+is not possible, data is not authenticated. This mode should not be used where
+authentication is critical.
+
+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.
+XTS always uses a fixed blocksize of 128 bit (16 bytes) length.
+
+Unlike other modes, the key is double the size of that for the used cipher mode
+(for example 256bit for AES-128 and 512bit for AES-256).
+
+@acronym{XTS} encryption mode operates given:
+@itemize
+@item A multiplication by a primitive element alpha.
+@code{MUL a^j} here represents the multiplication, where @code{j} is the power
+of alpha, and the input value is converted into a 16 bytes array
+@code{a_0[k], k = 0,1,..,15}. The multiplication is calculated as
+@code{a_(j+1)[0] = (2(a_j[0] mod 128)) XOR (135 * floor(a_j[15]/128)}
+@code{a_(j+1)[k] = (2(a_j[k] mod 128)) XOR (floor(a_j[k-1]/128), k = 1,2,..15}
+Note that this operation is practically a 1 bit left shift operation with carry
+propagating from one byte to the next, and if the last bit shift results in a
+carry the decimal value 135 is XORed into the first byte.
+
+@item The encryption key is provided as the @code{Key = K1 | K2}, where @code{|}
+denotes string concatenation.
+@code{E_k1} is the encryption function of the block cipher using @code{K1} as
+the key, and @code{E_k2} is the same encryption function using @code{K2}
+
+@item A 128 bit tweak value is provided as input and is denoted as @code{IV}
+@end itemize
+
+The @code{n} plaintext blocks are transformed into @code{n} ciphertext blocks
+@code{C_1},@dots{} @code{C_n} as follows.
+
+For a plaintext length that is a perfect multiple of the XTS block size:
+@example
+T_1 = E_k2(IV)
+C_1 = E_k1(P_1 XOR T_1) XOR T_1
+
+@dots{}
+
+T_n = T_(n-1) MUL a
+C_n = E_k1(P_n XOR T_n) XOR T_n
+@end example
+
+For any other plaintext lengths:
+@example
+T_1 = E_k2(IV)
+C_1 = E_k1(P_1 XOR T_1) XOR T_1
+
+@dots{}
+
+T_(n-2) = T_(n-3) MUL a
+C_(n-2) = E_k1(P_(n-2) XOR T_(n-2)) XOR T_(n-2)
+
+T_(n-1) = T_(n-2) MUL a
+CC_(n-1) = E_k1(P_(n-1) XOR T_(n-1)) XOR T_(n-1)
+
+T_n = T_(n-1) MUL a
+PP = [1..m]Pn | [m+1..128]CC_(n-1)
+C_(n-1) = E_k1(PP XOR T_n) XOR T_n
+
+C_n = [1..m]CC_(n-1)
+@end example
+
+@subsubsection General (@acronym{XTS}) interface.
+
+The two general functions to encrypt and decrypt using the @acronym{XTS} block
+cipher mode are the following:
+
+@deftypefun void xts_encrypt_message (const void *@var{enc_ctx}, const void *@var{twk_ctx}, nettle_cipher_func *@var{encf}, const uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+@deftypefunx void xts_decrypt_message (const void *@var{dec_ctx}, const void *@var{twk_ctx}, nettle_cipher_func *@var{decf}, nettle_cipher_func *@var{encf}, const uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+
+Applies the encryption function @var{encf} or the decryption function
+@var{decf} in @acronym{XTS} mode. At least one block (16 bytes) worth
+of data must be available therefore specifying a length less than 16
+bytes is illegal.
+
+The functions @var{encf} @var{decf} are of type
+
+@code{void f (const void *@var{ctx}, size_t @var{length}, uint8_t *@var{dst},
+const uint8_t *@var{src})},
+
+@noindent and the @code{xts_encrypt_message} and @code{xts_decrypt_message}
+functions pass their arguments @var{enc_ctx}, @var{twk_ctx} and @var{dec_ctx}
+to the functions @var{encf}, @var{decf} as @var{ctx}.
+@end deftypefun
+
+@subsubsection @acronym{XTS}-@acronym{AES} interface
+
+The @acronym{AES} @acronym{XTS} functions provide an API for using the
+@acronym{XTS} mode with the @acronym{AES} block ciphers. The parameters
+all have the same meaning as the general interface, except that the
+@var{enc_ctx}, @var{dec_ctx}, @var{twk_ctx}, @var{encf} and @var{decf} are
+replaced with an @acronym{AES} context structure called @var{ctx}, and a
+appropriate set-key function must be called before using any of the encryption
+or decryption functions in this interface.
+
+@deftp {Context struct} {struct xts_aes128_ctx}
+Holds state corresponding to the AES-128 block cipher.
+@end deftp
+
+@deftp {Context struct} {struct xts_aes256_ctx}
+Holds state corresponding to the AES-256 block cipher.
+@end deftp
+
+@deftypefun void xts_aes128_set_encrypt_key (struct xts_aes128_ctx *@var{ctx}, const uint8_t *@var{key})
+@deftypefunx void xts_aes256_set_encrypt_key (struct xts_aes256_ctx *@var{ctx}, const uint8_t *@var{key})
+@deftypefunx void xts_aes128_set_decrypt_key (struct xts_aes128_ctx *@var{ctx}, const uint8_t *@var{key})
+@deftypefunx void xts_aes256_set_decrypt_key (struct xts_aes256_ctx *@var{ctx}, const uint8_t *@var{key})
+Initializes the encryption or decryption key for the AES block cipher. The
+lenght of the key must be double the size of the key for the corresponding
+cipher (256 bits for AES-128 and 512 bits for AES-256). One of
+these functions must be called before any of the other functions.
+@end deftypefun
+
+@deftypefun void xts_aes128_encrypt_message(struct xts_aes128_ctx *@var{ctx}, uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+@deftypefunx void xts_aes256_encrypt_message(struct xts_aes256_ctx *@var{ctx}, uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+@deftypefunx void xts_aes128_decrypt_message(struct xts_aes128_ctx *@var{ctx}, uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+@deftypefunx void xts_aes256_decrypt_message(struct xts_aes256_ctx *@var{ctx}, uint8_t *@var{tweak}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src})
+These are identical to @code{xts_encrypt_message} and
+@code{xts_decrypt_message}, except that @var{enc_ctx}, @var{dec_ctx},
+@var{twk_ctx}, @var{encf} and @var{decf} are replaced by the @var{ctx} context
+structure.
+@end deftypefun
+
@node Authenticated encryption, Keyed hash functions, Cipher modes, Reference
@comment node-name, next, previous, up
diff --git a/testsuite/.gitignore b/testsuite/.gitignore
index f98a949a..c3fc5c11 100644
--- a/testsuite/.gitignore
+++ b/testsuite/.gitignore
@@ -95,6 +95,7 @@
/umac-test
/version-test
/yarrow-test
+/xts-test
/test.in
/test1.out
diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make
index f36a1f7f..6eee6e22 100644
--- a/testsuite/.test-rules.make
+++ b/testsuite/.test-rules.make
@@ -166,6 +166,9 @@ buffer-test$(EXEEXT): buffer-test.$(OBJEXT)
yarrow-test$(EXEEXT): yarrow-test.$(OBJEXT)
$(LINK) yarrow-test.$(OBJEXT) $(TEST_OBJS) -o yarrow-test$(EXEEXT)
+xts-test$(EXEEXT): xts-test.$(OBJEXT)
+ $(LINK) xts-test.$(OBJEXT) $(TEST_OBJS) -o xts-test$(EXEEXT)
+
pbkdf2-test$(EXEEXT): pbkdf2-test.$(OBJEXT)
$(LINK) pbkdf2-test.$(OBJEXT) $(TEST_OBJS) -o pbkdf2-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index e2982b66..287c4f75 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -32,7 +32,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \
hmac-test.c umac-test.c \
meta-hash-test.c meta-cipher-test.c\
meta-aead-test.c meta-armor-test.c \
- buffer-test.c yarrow-test.c pbkdf2-test.c
+ buffer-test.c yarrow-test.c xts-test.c pbkdf2-test.c
TS_HOGWEED_SOURCES = sexp-test.c sexp-format-test.c \
rsa2sexp-test.c sexp2rsa-test.c \
diff --git a/testsuite/xts-test.c b/testsuite/xts-test.c
new file mode 100644
index 00000000..723a5a12
--- /dev/null
+++ b/testsuite/xts-test.c
@@ -0,0 +1,173 @@
+#include "testutils.h"
+#include "aes.h"
+#include "xts.h"
+#include "nettle-internal.h"
+
+static void
+test_check_data(const char *operation,
+ const uint8_t *input, const uint8_t *output,
+ const uint8_t *expected, size_t length)
+{
+ if (!MEMEQ(length, output, expected))
+ {
+ fprintf(stderr, "XTS %s failed:\nInput:", operation);
+ print_hex(length, input);
+ fprintf(stderr, "\nOutput: ");
+ print_hex(length, output);
+ fprintf(stderr, "\nExpected:");
+ print_hex(length, expected);
+ fprintf(stderr, "\n");
+ FAIL();
+ }
+}
+
+static void
+test_cipher_xts(const struct nettle_cipher *cipher,
+ const struct tstring *key,
+ const struct tstring *tweak,
+ const struct tstring *cleartext,
+ const struct tstring *ciphertext)
+{
+ void *twk_ctx = xalloc(cipher->context_size);
+ void *ctx = xalloc(cipher->context_size);
+ uint8_t *data, *data2;
+ size_t length = cleartext->length;
+
+ ASSERT (cleartext->length == ciphertext->length);
+ ASSERT (key->length == cipher->key_size * 2);
+ ASSERT (tweak->length == XTS_BLOCK_SIZE);
+
+ data = xalloc(length);
+ data2 = xalloc(length);
+
+ cipher->set_encrypt_key(ctx, key->data);
+ cipher->set_encrypt_key(twk_ctx, &key->data[key->length / 2]);
+ xts_encrypt_message(ctx, twk_ctx, cipher->encrypt,
+ tweak->data, length, data, cleartext->data);
+ test_check_data("encrypt", cleartext->data, data, ciphertext->data, length);
+
+ cipher->set_decrypt_key(ctx, key->data);
+ cipher->set_encrypt_key(twk_ctx, &key->data[key->length / 2]);
+ xts_decrypt_message(ctx, twk_ctx, cipher->decrypt, cipher->encrypt,
+ tweak->data, length, data2, data);
+ test_check_data("decrypt", data, data2, cleartext->data, length);
+
+ memcpy(data, cleartext->data, length);
+
+ cipher->set_encrypt_key(ctx, key->data);
+ cipher->set_encrypt_key(twk_ctx, &key->data[key->length / 2]);
+ xts_encrypt_message(ctx, twk_ctx, cipher->encrypt,
+ tweak->data, length, data, data);
+ test_check_data("inplace encrypt",
+ cleartext->data, data, ciphertext->data, length);
+
+ cipher->set_decrypt_key(ctx, key->data);
+ cipher->set_encrypt_key(twk_ctx, &key->data[key->length / 2]);
+ xts_decrypt_message(ctx, twk_ctx, cipher->decrypt, cipher->encrypt,
+ tweak->data, length, data, data);
+ test_check_data("inplace decrypt", data, data, cleartext->data, length);
+
+ /* make sure AES128 specific functions also works the same */
+ if (cipher == &nettle_aes128) {
+ struct xts_aes128_key xts_key;
+
+ xts_aes128_set_encrypt_key(&xts_key, key->data);
+ xts_aes128_encrypt_message(&xts_key, tweak->data, length, data,
+ cleartext->data);
+ test_check_data("encrypt",
+ cleartext->data, data, ciphertext->data, length);
+
+ xts_aes128_set_decrypt_key(&xts_key, key->data);
+ xts_aes128_decrypt_message(&xts_key, tweak->data, length, data,
+ ciphertext->data);
+ test_check_data("decrypt",
+ ciphertext->data, data, cleartext->data, length);
+ }
+
+ /* make sure AES256 specific functions also works the same */
+ if (cipher == &nettle_aes256) {
+ struct xts_aes256_key xts_key;
+
+ xts_aes256_set_encrypt_key(&xts_key, key->data);
+ xts_aes256_encrypt_message(&xts_key, tweak->data, length, data,
+ cleartext->data);
+ test_check_data("encrypt",
+ cleartext->data, data, ciphertext->data, length);
+
+ xts_aes256_set_decrypt_key(&xts_key, key->data);
+ xts_aes256_decrypt_message(&xts_key, tweak->data, length, data,
+ ciphertext->data);
+ test_check_data("decrypt",
+ ciphertext->data, data, cleartext->data, length);
+ }
+
+ free(twk_ctx);
+ free(ctx);
+ free(data);
+ free(data2);
+}
+
+void
+test_main(void)
+{
+ /* From NIST CAVS 11.0,
+ *
+ * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/XTSTestVectors.zip
+ *
+ * Selection of testing vectors from the above CAVS set
+ */
+
+ /* AES-128 single block - exact block size multiple */
+ test_cipher_xts(&nettle_aes128,
+ SHEX("a1b90cba3f06ac353b2c343876081762"
+ "090923026e91771815f29dab01932f2f"),
+ SHEX("4faef7117cda59c66e4b92013e768ad5"),
+ SHEX("ebabce95b14d3c8d6fb350390790311c"),
+ SHEX("778ae8b43cb98d5a825081d5be471c63"));
+
+ /* AES-128 two blocks - exact block size multiple */
+ test_cipher_xts(&nettle_aes128,
+ SHEX("750372c3d82f63382867be6662acfa4a"
+ "259be3fa9bc662a1154ffaaed8b448a5"),
+ SHEX("93a29254c47e4260669621307d4f5cd3"),
+ SHEX("d8e3a56559a436ce0d8b212c80a88b23"
+ "af62b0e598f208e03c1f2e9fa563a54b"),
+ SHEX("495f7855535efd133464dc9a9abf8a0f"
+ "28facbce21bd3c22178ec489b799e491"));
+
+ /* AES-128 partial second block */
+ test_cipher_xts(&nettle_aes128,
+ SHEX("394c97881abd989d29c703e48a72b397"
+ "a7acf51b59649eeea9b33274d8541df4"),
+ SHEX("4b15c684a152d485fe9937d39b168c29"),
+ SHEX("2f3b9dcfbae729583b1d1ffdd16bb6fe"
+ "2757329435662a78f0"),
+ SHEX("f3473802e38a3ffef4d4fb8e6aa266eb"
+ "de553a64528a06463e"));
+
+ /* AES-256 two blocks - exact block size multiple */
+ test_cipher_xts(&nettle_aes256,
+ SHEX("1ea661c58d943a0e4801e42f4b094714"
+ "9e7f9f8e3e68d0c7505210bd311a0e7c"
+ "d6e13ffdf2418d8d1911c004cda58da3"
+ "d619b7e2b9141e58318eea392cf41b08"),
+ SHEX("adf8d92627464ad2f0428e84a9f87564"),
+ SHEX("2eedea52cd8215e1acc647e810bbc364"
+ "2e87287f8d2e57e36c0a24fbc12a202e"),
+ SHEX("cbaad0e2f6cea3f50b37f934d46a9b13"
+ "0b9d54f07e34f36af793e86f73c6d7db"));
+
+ /* AES-256 three blocks - exact block size multiple */
+ test_cipher_xts(&nettle_aes256,
+ SHEX("266c336b3b01489f3267f52835fd92f6"
+ "74374b88b4e1ebd2d36a5f457581d9d0"
+ "42c3eef7b0b7e5137b086496b4d9e6ac"
+ "658d7196a23f23f036172fdb8faee527"),
+ SHEX("06b209a7a22f486ecbfadb0f3137ba42"),
+ SHEX("ca7d65ef8d3dfad345b61ccddca1ad81"
+ "de830b9e86c7b426d76cb7db766852d9"
+ "81c6b21409399d78f42cc0b33a7bbb06"),
+ SHEX("c73256870cc2f4dd57acc74b5456dbd7"
+ "76912a128bc1f77d72cdebbf270044b7"
+ "a43ceed29025e1e8be211fa3c3ed002d"));
+}
diff --git a/xts-aes128.c b/xts-aes128.c
new file mode 100644
index 00000000..5b447bb9
--- /dev/null
+++ b/xts-aes128.c
@@ -0,0 +1,77 @@
+/* xts-aes128.c
+
+ XTS Mode using AES128 as the underlying cipher.
+
+ Copyright (C) 2018 Red Hat, Inc.
+
+ 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 "aes.h"
+#include "xts.h"
+
+
+void
+xts_aes128_set_encrypt_key(struct xts_aes128_key *xts_key, const uint8_t *key)
+{
+ aes128_set_encrypt_key(&xts_key->cipher, key);
+ aes128_set_encrypt_key(&xts_key->tweak_cipher, &key[AES128_KEY_SIZE]);
+}
+
+void
+xts_aes128_set_decrypt_key(struct xts_aes128_key *xts_key, const uint8_t *key)
+{
+ aes128_set_decrypt_key(&xts_key->cipher, key);
+ aes128_set_encrypt_key(&xts_key->tweak_cipher, &key[AES128_KEY_SIZE]);
+}
+
+void
+xts_aes128_encrypt_message(struct xts_aes128_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ xts_encrypt_message(&xts_key->cipher, &xts_key->tweak_cipher,
+ (nettle_cipher_func *) aes128_encrypt,
+ tweak, length, dst, src);
+}
+
+void
+xts_aes128_decrypt_message(struct xts_aes128_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ xts_decrypt_message(&xts_key->cipher, &xts_key->tweak_cipher,
+ (nettle_cipher_func *) aes128_decrypt,
+ (nettle_cipher_func *) aes128_encrypt,
+ tweak, length, dst, src);
+}
diff --git a/xts-aes256.c b/xts-aes256.c
new file mode 100644
index 00000000..2285b8c8
--- /dev/null
+++ b/xts-aes256.c
@@ -0,0 +1,77 @@
+/* xts-aes256.c
+
+ XTS Mode using AES256 as the underlying cipher.
+
+ Copyright (C) 2018 Red Hat, Inc.
+
+ 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 "aes.h"
+#include "xts.h"
+
+
+void
+xts_aes256_set_encrypt_key(struct xts_aes256_key *xts_key, const uint8_t *key)
+{
+ aes256_set_encrypt_key(&xts_key->cipher, key);
+ aes256_set_encrypt_key(&xts_key->tweak_cipher, &key[AES256_KEY_SIZE]);
+}
+
+void
+xts_aes256_set_decrypt_key(struct xts_aes256_key *xts_key, const uint8_t *key)
+{
+ aes256_set_decrypt_key(&xts_key->cipher, key);
+ aes256_set_encrypt_key(&xts_key->tweak_cipher, &key[AES256_KEY_SIZE]);
+}
+
+void
+xts_aes256_encrypt_message(struct xts_aes256_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ xts_encrypt_message(&xts_key->cipher, &xts_key->tweak_cipher,
+ (nettle_cipher_func *) aes256_encrypt,
+ tweak, length, dst, src);
+}
+
+void
+xts_aes256_decrypt_message(struct xts_aes256_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ xts_decrypt_message(&xts_key->cipher, &xts_key->tweak_cipher,
+ (nettle_cipher_func *) aes256_decrypt,
+ (nettle_cipher_func *) aes256_encrypt,
+ tweak, length, dst, src);
+}
diff --git a/xts.c b/xts.c
new file mode 100644
index 00000000..4eeb9d9b
--- /dev/null
+++ b/xts.c
@@ -0,0 +1,202 @@
+/* xts.c
+
+ XEX-based tweaked-codebook mode with ciphertext stealing (XTS)
+
+ Copyright (C) 2018 Red Hat, Inc.
+
+ 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 "xts.h"
+
+#include "macros.h"
+#include "memxor.h"
+#include "nettle-internal.h"
+
+/* shift one and XOR with 0x87. */
+/* src and dest can point to the same buffer for in-place operations */
+static void
+xts_shift(union nettle_block16 *dst,
+ const union nettle_block16 *src)
+{
+ uint8_t carry = src->b[15] >> 7;
+ uint64_t b0 = LE_READ_UINT64(src->b);
+ uint64_t b1 = LE_READ_UINT64(src->b+8);
+ b1 = (b1 << 1) | (b0 >> 63);
+ b0 = b0 << 1;
+ LE_WRITE_UINT64(dst->b, b0);
+ LE_WRITE_UINT64(dst->b+8, b1);
+ dst->b[0] ^= 0x87 & -carry;
+}
+
+/*
+ * prev is the block to steal from
+ * curr is the input block to the last step
+ * length is the partial block length
+ * dst is the destination partial block
+ * src is the source partial block
+ *
+ * In the Encryption case:
+ * prev -> the output of the N-1 encryption step
+ * curr -> the input to the Nth step (will be encrypted as Cn-1)
+ * dst -> the final Cn partial block
+ * src -> the final Pn partial block
+ *
+ * In the decryption case:
+ * prev -> the output of the N-1 decryption step
+ * curr -> the input to the Nth step (will be decrypted as Pn-1)
+ * dst -> the final Pn partial block
+ * src -> the final Cn partial block
+ */
+static void
+xts_steal(uint8_t *prev, uint8_t *curr,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ /* copy the remaining in the current input block */
+ memcpy(curr, src, length);
+ /* fill the current block with the last blocksize - length
+ * bytes of the previous block */
+ memcpy(&curr[length], &prev[length], XTS_BLOCK_SIZE - length);
+
+ /* This must be last or inplace operations will break
+ * copy 'length' bytes of the previous block in the
+ * destination block, which is the final partial block
+ * returned to the caller */
+ memcpy(dst, prev, length);
+}
+
+static void
+check_length(size_t length, uint8_t *dst)
+{
+ assert(length >= XTS_BLOCK_SIZE);
+ /* asserts may be compiled out, try to save the user by zeroing the dst in
+ * case the buffer contains sensitive data (like the clear text for inplace
+ * encryption) */
+ if (length < XTS_BLOCK_SIZE)
+ memxor(dst, dst, length);
+}
+
+/* works also for inplace encryption/decryption */
+
+void
+xts_encrypt_message(const void *enc_ctx, const void *twk_ctx,
+ nettle_cipher_func *encf,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ union nettle_block16 T;
+ union nettle_block16 P;
+
+ check_length(length, dst);
+
+ encf(twk_ctx, XTS_BLOCK_SIZE, T.b, tweak);
+
+ /* the zeroth power of alpha is the initial ciphertext value itself, so we
+ * skip shifting and do it at the end of each block operation instead */
+ for (;length >= XTS_BLOCK_SIZE;
+ length -= XTS_BLOCK_SIZE, src += XTS_BLOCK_SIZE, dst += XTS_BLOCK_SIZE)
+ {
+ memxor3(P.b, src, T.b, XTS_BLOCK_SIZE); /* P -> PP */
+ encf(enc_ctx, XTS_BLOCK_SIZE, dst, P.b); /* CC */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* CC -> C */
+
+ /* shift T for next block */
+ xts_shift(&T, &T);
+ }
+
+ /* if the last block is partial, handle via stealing */
+ if (length)
+ {
+ uint8_t *C = dst - XTS_BLOCK_SIZE;
+ /* C points to C(n-1) */
+ xts_steal(C, P.b, length, dst, src);
+ memxor(P.b, T.b, XTS_BLOCK_SIZE); /* P -> PP */
+ encf(enc_ctx, XTS_BLOCK_SIZE, C, P.b); /* CC */
+ memxor(C, T.b, XTS_BLOCK_SIZE);
+ }
+}
+
+void
+xts_decrypt_message(const void *dec_ctx, const void *twk_ctx,
+ nettle_cipher_func *decf, nettle_cipher_func *encf,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src)
+{
+ union nettle_block16 T;
+ union nettle_block16 C;
+
+ check_length(length, dst);
+
+ encf(twk_ctx, XTS_BLOCK_SIZE, T.b, tweak);
+
+ for (;length >= XTS_BLOCK_SIZE;
+ length -= XTS_BLOCK_SIZE, src += XTS_BLOCK_SIZE, dst += XTS_BLOCK_SIZE)
+ {
+ if (length > XTS_BLOCK_SIZE && length < 2 * XTS_BLOCK_SIZE)
+ break; /* must ciphersteal on last two blocks */
+
+ memxor3(C.b, src, T.b, XTS_BLOCK_SIZE); /* c -> CC */
+ decf(dec_ctx, XTS_BLOCK_SIZE, dst, C.b); /* PP */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* PP -> P */
+
+ xts_shift(&T, &T);
+ }
+
+ /* if the last block is partial, handle via stealing */
+ if (length)
+ {
+ union nettle_block16 T1;
+ uint8_t *P;
+
+ /* we need the last T(n) and save the T(n-1) for later */
+ xts_shift(&T1, &T);
+
+ P = dst; /* use P(n-1) as temp storage for partial P(n) */
+ memxor3(C.b, src, T1.b, XTS_BLOCK_SIZE); /* C -> CC */
+ decf(dec_ctx, XTS_BLOCK_SIZE, P, C.b); /* PP */
+ memxor(P, T1.b, XTS_BLOCK_SIZE); /* PP -> P */
+
+ /* process next block (Pn-1) */
+ length -= XTS_BLOCK_SIZE;
+ src += XTS_BLOCK_SIZE;
+ dst += XTS_BLOCK_SIZE;
+
+ /* Fill P(n) and prepare C, P still pointing to P(n-1) */
+ xts_steal(P, C.b, length, dst, src);
+ memxor(C.b, T.b, XTS_BLOCK_SIZE); /* C -> CC */
+ decf(dec_ctx, XTS_BLOCK_SIZE, P, C.b); /* PP */
+ memxor(P, T.b, XTS_BLOCK_SIZE); /* PP -> P */
+ }
+}
diff --git a/xts.h b/xts.h
new file mode 100644
index 00000000..a9ea970d
--- /dev/null
+++ b/xts.h
@@ -0,0 +1,124 @@
+/* xts.h
+
+ XEX-based tweaked-codebook mode with ciphertext stealing (XTS)
+
+ Copyright (C) 2005 Niels Möller
+ Copyright (C) 2018 Red Hat, Inc.
+
+ 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_XTS_H_INCLUDED
+#define NETTLE_XTS_H_INCLUDED
+
+#include "nettle-types.h"
+#include "aes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Name mangling */
+#define xts_encrypt_message nettle_xts_encrypt_message
+#define xts_decrypt_message nettle_xts_decrypt_message
+#define xts_aes128_set_encrypt_key nettle_xts_aes128_set_encrypt_key
+#define xts_aes128_set_decrypt_key nettle_xts_aes128_set_decrypt_key
+#define xts_aes128_encrypt_message nettle_xts_aes128_encrypt_message
+#define xts_aes128_decrypt_message nettle_xts_aes128_decrypt_message
+#define xts_aes256_set_encrypt_key nettle_xts_aes256_set_encrypt_key
+#define xts_aes256_set_decrypt_key nettle_xts_aes256_set_decrypt_key
+#define xts_aes256_encrypt_message nettle_xts_aes256_encrypt_message
+#define xts_aes256_decrypt_message nettle_xts_aes256_decrypt_message
+
+#define XTS_BLOCK_SIZE 16
+
+void
+xts_encrypt_message(const void *enc_ctx, const void *twk_ctx,
+ nettle_cipher_func *encf,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+void
+xts_decrypt_message(const void *dec_ctx, const void *twk_ctx,
+ nettle_cipher_func *decf, nettle_cipher_func *encf,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+
+/* XTS Mode with AES-128 */
+struct xts_aes128_key {
+ struct aes128_ctx cipher;
+ struct aes128_ctx tweak_cipher;
+};
+
+void
+xts_aes128_set_encrypt_key(struct xts_aes128_key *xts_key,
+ const uint8_t *key);
+
+void
+xts_aes128_set_decrypt_key(struct xts_aes128_key *xts_key,
+ const uint8_t *key);
+
+void
+xts_aes128_encrypt_message(struct xts_aes128_key *xtskey,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+
+void
+xts_aes128_decrypt_message(struct xts_aes128_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+
+/* XTS Mode with AES-256 */
+struct xts_aes256_key {
+ struct aes256_ctx cipher;
+ struct aes256_ctx tweak_cipher;
+};
+
+void
+xts_aes256_set_encrypt_key(struct xts_aes256_key *xts_key,
+ const uint8_t *key);
+
+void
+xts_aes256_set_decrypt_key(struct xts_aes256_key *xts_key,
+ const uint8_t *key);
+
+void
+xts_aes256_encrypt_message(struct xts_aes256_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+
+void
+xts_aes256_decrypt_message(struct xts_aes256_key *xts_key,
+ const uint8_t *tweak, size_t length,
+ uint8_t *dst, const uint8_t *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_XTS_H_INCLUDED */
+
--
2.20.1
From 1aa0cd00ccc9ede866cb382d43240e054d238d73 Mon Sep 17 00:00:00 2001
From: Simo Sorce <[email protected]>
Date: Tue, 19 Mar 2019 16:30:53 -0400
Subject: [PATCH 2/2] Inline ciphertext stealing
This avoids copying and may be somewhat more readable without the need
for so much explanation.
Signed-off-by: Simo Sorce <[email protected]>
---
xts.c | 113 ++++++++++++++++++++++++++++------------------------------
1 file changed, 54 insertions(+), 59 deletions(-)
diff --git a/xts.c b/xts.c
index 4eeb9d9b..7e97ebe2 100644
--- a/xts.c
+++ b/xts.c
@@ -61,42 +61,6 @@ xts_shift(union nettle_block16 *dst,
dst->b[0] ^= 0x87 & -carry;
}
-/*
- * prev is the block to steal from
- * curr is the input block to the last step
- * length is the partial block length
- * dst is the destination partial block
- * src is the source partial block
- *
- * In the Encryption case:
- * prev -> the output of the N-1 encryption step
- * curr -> the input to the Nth step (will be encrypted as Cn-1)
- * dst -> the final Cn partial block
- * src -> the final Pn partial block
- *
- * In the decryption case:
- * prev -> the output of the N-1 decryption step
- * curr -> the input to the Nth step (will be decrypted as Pn-1)
- * dst -> the final Pn partial block
- * src -> the final Cn partial block
- */
-static void
-xts_steal(uint8_t *prev, uint8_t *curr,
- size_t length, uint8_t *dst, const uint8_t *src)
-{
- /* copy the remaining in the current input block */
- memcpy(curr, src, length);
- /* fill the current block with the last blocksize - length
- * bytes of the previous block */
- memcpy(&curr[length], &prev[length], XTS_BLOCK_SIZE - length);
-
- /* This must be last or inplace operations will break
- * copy 'length' bytes of the previous block in the
- * destination block, which is the final partial block
- * returned to the caller */
- memcpy(dst, prev, length);
-}
-
static void
check_length(size_t length, uint8_t *dst)
{
@@ -125,7 +89,7 @@ xts_encrypt_message(const void *enc_ctx, const void *twk_ctx,
/* the zeroth power of alpha is the initial ciphertext value itself, so we
* skip shifting and do it at the end of each block operation instead */
- for (;length >= XTS_BLOCK_SIZE;
+ for (;length >= 2 * XTS_BLOCK_SIZE;
length -= XTS_BLOCK_SIZE, src += XTS_BLOCK_SIZE, dst += XTS_BLOCK_SIZE)
{
memxor3(P.b, src, T.b, XTS_BLOCK_SIZE); /* P -> PP */
@@ -137,14 +101,38 @@ xts_encrypt_message(const void *enc_ctx, const void *twk_ctx,
}
/* if the last block is partial, handle via stealing */
- if (length)
+ if (length == XTS_BLOCK_SIZE)
+ {
+ memxor3(P.b, src, T.b, XTS_BLOCK_SIZE); /* P -> PP */
+ encf(enc_ctx, XTS_BLOCK_SIZE, dst, P.b); /* CC */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* CC -> C */
+ }
+ else
{
- uint8_t *C = dst - XTS_BLOCK_SIZE;
- /* C points to C(n-1) */
- xts_steal(C, P.b, length, dst, src);
- memxor(P.b, T.b, XTS_BLOCK_SIZE); /* P -> PP */
- encf(enc_ctx, XTS_BLOCK_SIZE, C, P.b); /* CC */
- memxor(C, T.b, XTS_BLOCK_SIZE);
+ /* S Holds the real C(n-1) (Whole last block to steal from) */
+ union nettle_block16 S;
+
+ memxor3(P.b, src, T.b, XTS_BLOCK_SIZE); /* P -> PP */
+ encf(enc_ctx, XTS_BLOCK_SIZE, S.b, P.b); /* CC */
+ memxor(S.b, T.b, XTS_BLOCK_SIZE); /* CC -> S */
+
+ /* shift T for next block */
+ xts_shift(&T, &T);
+
+ length -= XTS_BLOCK_SIZE;
+ src += XTS_BLOCK_SIZE;
+
+ memxor3(P.b, src, T.b, length); /* P |.. */
+ /* steal ciphertext to complete block */
+ memxor3(P.b + length, S.b + length, T.b + length,
+ XTS_BLOCK_SIZE - length); /* ..| S_2 -> PP */
+
+ encf(enc_ctx, XTS_BLOCK_SIZE, dst, P.b); /* CC */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* CC -> C(n-1) */
+
+ /* Do this after we read src so inplace operations do not break */
+ dst += XTS_BLOCK_SIZE;
+ memcpy(dst, S.b, length); /* S_1 -> C(n) */
}
}
@@ -161,12 +149,9 @@ xts_decrypt_message(const void *dec_ctx, const void *twk_ctx,
encf(twk_ctx, XTS_BLOCK_SIZE, T.b, tweak);
- for (;length >= XTS_BLOCK_SIZE;
+ for (;length >= 2 * XTS_BLOCK_SIZE;
length -= XTS_BLOCK_SIZE, src += XTS_BLOCK_SIZE, dst += XTS_BLOCK_SIZE)
{
- if (length > XTS_BLOCK_SIZE && length < 2 * XTS_BLOCK_SIZE)
- break; /* must ciphersteal on last two blocks */
-
memxor3(C.b, src, T.b, XTS_BLOCK_SIZE); /* c -> CC */
decf(dec_ctx, XTS_BLOCK_SIZE, dst, C.b); /* PP */
memxor(dst, T.b, XTS_BLOCK_SIZE); /* PP -> P */
@@ -175,28 +160,38 @@ xts_decrypt_message(const void *dec_ctx, const void *twk_ctx,
}
/* if the last block is partial, handle via stealing */
- if (length)
+ if (length == XTS_BLOCK_SIZE)
+ {
+ memxor3(C.b, src, T.b, XTS_BLOCK_SIZE); /* c -> CC */
+ decf(dec_ctx, XTS_BLOCK_SIZE, dst, C.b); /* PP */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* PP -> P */
+ }
+ else
{
union nettle_block16 T1;
- uint8_t *P;
+ /* S Holds the real P(n) (with part of stolen ciphertext) */
+ union nettle_block16 S;
/* we need the last T(n) and save the T(n-1) for later */
xts_shift(&T1, &T);
- P = dst; /* use P(n-1) as temp storage for partial P(n) */
memxor3(C.b, src, T1.b, XTS_BLOCK_SIZE); /* C -> CC */
- decf(dec_ctx, XTS_BLOCK_SIZE, P, C.b); /* PP */
- memxor(P, T1.b, XTS_BLOCK_SIZE); /* PP -> P */
+ decf(dec_ctx, XTS_BLOCK_SIZE, S.b, C.b); /* PP */
+ memxor(S.b, T1.b, XTS_BLOCK_SIZE); /* PP -> S */
/* process next block (Pn-1) */
length -= XTS_BLOCK_SIZE;
src += XTS_BLOCK_SIZE;
- dst += XTS_BLOCK_SIZE;
- /* Fill P(n) and prepare C, P still pointing to P(n-1) */
- xts_steal(P, C.b, length, dst, src);
- memxor(C.b, T.b, XTS_BLOCK_SIZE); /* C -> CC */
- decf(dec_ctx, XTS_BLOCK_SIZE, P, C.b); /* PP */
- memxor(P, T.b, XTS_BLOCK_SIZE); /* PP -> P */
+ /* Prepare C, P holds the real P(n) */
+ memxor3(C.b, src, T.b, length); /* C_1 |.. */
+ memxor3(C.b + length, S.b + length, T.b + length,
+ XTS_BLOCK_SIZE - length); /* ..| S_2 -> CC */
+ decf(dec_ctx, XTS_BLOCK_SIZE, dst, C.b); /* PP */
+ memxor(dst, T.b, XTS_BLOCK_SIZE); /* PP -> P(n-1) */
+
+ /* Do this after we read src so inplace operations do not break */
+ dst += XTS_BLOCK_SIZE;
+ memcpy(dst, S.b, length); /* S_1 -> P(n) */
}
}
--
2.20.1
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs