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.

> +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 *.

> +/* 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.

> +/*
> + * 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).

> +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).

> +/* 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.

Regards,
/Niels

-- 
Niels Möller. PGP-encrypted email is preferred. Keyid 368C6677.
Internet email is subject to wholesale government surveillance.
_______________________________________________
nettle-bugs mailing list
[email protected]
http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs

Reply via email to