summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Möller <nisse@lysator.liu.se>2023-02-07 20:04:03 +0100
committerNiels Möller <nisse@lysator.liu.se>2023-02-07 20:34:38 +0100
commit9cf0e2d2675268a403194d85a78a44e8cbdf562b (patch)
treeb27e147fe172aaba9f1c1ad1aadada10dc27393b
parenteb48e209db6fb6d6ce0005de88ba362b6fcbe933 (diff)
downloadnettle-9cf0e2d2675268a403194d85a78a44e8cbdf562b.tar.gz
Implement OCB mode, RFC 7253.
-rw-r--r--ChangeLog36
-rw-r--r--Makefile.in3
-rw-r--r--block-internal.h8
-rw-r--r--nettle-internal.c60
-rw-r--r--nettle-internal.h15
-rw-r--r--ocb-aes128.c118
-rw-r--r--ocb.c432
-rw-r--r--ocb.h188
-rw-r--r--testsuite/Makefile.in2
-rw-r--r--testsuite/ocb-test.c235
10 files changed, 1095 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 618c57dc..94f78204 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+2023-02-07 Niels Möller <nisse@lysator.liu.se>
+
+ Implement OCB mode. RFC 7253.
+ * block-internal.h (block16_set): New function.
+
+ * ocb.c (ocb_set_key, ocb_set_nonce, ocb_update, ocb_encrypt)
+ (ocb_decrypt, ocb_encrypt_message, ocb_decrypt_message): New
+ public functions.
+ (MEM_ROTATE_RIGHT, MEM_MASK): New macros.
+ (extract, update_offset, pad_block, ocb_fill_n, ocb_crypt_n)
+ (ocb_checksum_n): New helper functions.
+ * ocb-aes128.c (ocb_aes128_set_encrypt_key)
+ (ocb_aes128_set_decrypt_key, ocb_aes128_set_nonce)
+ (ocb_aes128_update, ocb_aes128_encrypt, ocb_aes128_decrypt)
+ (ocb_aes128_digest, ocb_aes128_encrypt_message)
+ (ocb_aes128_decrypt_message): New file, new functions.
+ * ocb.h: Declare ocb functions.
+ (struct ocb_key): New struct.
+ (struct ocb_ctx): New struct.
+ (struct ocb_aes128_encrypt_key): New struct.
+ * Makefile.in (nettle_SOURCES): Add ocb.c ocb-aes128.c.
+ (HEADERS): Add ocb.h.
+
+ * nettle-internal.c (nettle_ocb_aes128)
+ (ocb_aes128_set_encrypt_key_wrapper)
+ (ocb_aes128_set_decrypt_key_wrapper)
+ (ocb_aes128_set_nonce_wrapper, ocb_aes128_update_wrapper)
+ (ocb_aes128_encrypt_wrapper, ocb_aes128_decrypt_wrapper)
+ (ocb_aes128_digest_wrapper): New aead algorithm, and
+ related wrapper functions.
+ * nettle-internal.h (OCB_NONCE_SIZE): New constant.
+ (struct ocb_aes128_ctx): New struct.
+
+ * testsuite/ocb-test.c: New tests.
+ * testsuite/Makefile.in (TS_NETTLE_SOURCES): Add ocb-test.c.
+
2023-02-06 Niels Möller <nisse@lysator.liu.se>
* testsuite/testutils.c (test_aead): Always use set_nonce function
diff --git a/Makefile.in b/Makefile.in
index cd4993e8..081337a8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -134,6 +134,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c aes-decrypt-table.c \
nettle-lookup-hash.c \
nettle-meta-aeads.c nettle-meta-armors.c \
nettle-meta-ciphers.c nettle-meta-hashes.c nettle-meta-macs.c \
+ ocb.c ocb-aes128.c \
pbkdf2.c pbkdf2-hmac-gosthash94.c pbkdf2-hmac-sha1.c \
pbkdf2-hmac-sha256.c pbkdf2-hmac-sha384.c pbkdf2-hmac-sha512.c \
poly1305-aes.c poly1305-internal.c poly1305-update.c \
@@ -238,7 +239,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h balloon.h \
md5.h md5-compat.h \
memops.h memxor.h \
nettle-meta.h nettle-types.h \
- pbkdf2.h \
+ ocb.h pbkdf2.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 sm3.h sm4.h streebog.h twofish.h \
diff --git a/block-internal.h b/block-internal.h
index e9c26ff6..b927f352 100644
--- a/block-internal.h
+++ b/block-internal.h
@@ -51,6 +51,14 @@ block16_zero (union nettle_block16 *r)
}
static inline void
+block16_set (union nettle_block16 *r,
+ const union nettle_block16 *x)
+{
+ r->u64[0] = x->u64[0];
+ r->u64[1] = x->u64[1];
+}
+
+static inline void
block16_xor (union nettle_block16 *r,
const union nettle_block16 *x)
{
diff --git a/nettle-internal.c b/nettle-internal.c
index dd293227..c68e4728 100644
--- a/nettle-internal.c
+++ b/nettle-internal.c
@@ -248,3 +248,63 @@ nettle_cbc_aes256 = {
NULL,
NULL,
};
+
+static void
+ocb_aes128_set_encrypt_key_wrapper (struct ocb_aes128_ctx *ctx, const uint8_t *key)
+{
+ ocb_aes128_set_encrypt_key(&ctx->key, key);
+}
+
+static void
+ocb_aes128_set_decrypt_key_wrapper (struct ocb_aes128_ctx *ctx, const uint8_t *key)
+{
+ ocb_aes128_set_decrypt_key(&ctx->key, &ctx->decrypt, key);
+}
+
+static void
+ocb_aes128_set_nonce_wrapper (struct ocb_aes128_ctx *ctx, const uint8_t *nonce)
+{
+ ocb_aes128_set_nonce (&ctx->ocb, &ctx->key,
+ OCB_DIGEST_SIZE, OCB_NONCE_SIZE, nonce);
+}
+
+static void
+ocb_aes128_update_wrapper (struct ocb_aes128_ctx *ctx,
+ size_t length, const uint8_t *data)
+{
+ ocb_aes128_update (&ctx->ocb, &ctx->key, length, data);
+}
+
+static void
+ocb_aes128_encrypt_wrapper (struct ocb_aes128_ctx *ctx,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ ocb_aes128_encrypt (&ctx->ocb, &ctx->key, length, dst, src);
+}
+
+static void
+ocb_aes128_decrypt_wrapper (struct ocb_aes128_ctx *ctx,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ ocb_aes128_decrypt (&ctx->ocb, &ctx->key, &ctx->decrypt, length, dst, src);
+}
+
+static void
+ocb_aes128_digest_wrapper (struct ocb_aes128_ctx *ctx, size_t length, uint8_t *digest)
+{
+ ocb_aes128_digest (&ctx->ocb, &ctx->key, length, digest);
+}
+
+const struct nettle_aead
+nettle_ocb_aes128 =
+ { "ocb_aes128", sizeof(struct ocb_aes128_ctx),
+ OCB_BLOCK_SIZE, AES128_KEY_SIZE,
+ OCB_NONCE_SIZE, OCB_DIGEST_SIZE,
+ (nettle_set_key_func *) ocb_aes128_set_encrypt_key_wrapper,
+ (nettle_set_key_func *) ocb_aes128_set_decrypt_key_wrapper,
+ (nettle_set_key_func *) ocb_aes128_set_nonce_wrapper,
+ (nettle_hash_update_func *) ocb_aes128_update_wrapper,
+ (nettle_crypt_func *) ocb_aes128_encrypt_wrapper,
+ (nettle_crypt_func *) ocb_aes128_decrypt_wrapper,
+ (nettle_hash_digest_func *) ocb_aes128_digest_wrapper
+ };
diff --git a/nettle-internal.h b/nettle-internal.h
index bf906c88..c41f3ee0 100644
--- a/nettle-internal.h
+++ b/nettle-internal.h
@@ -40,6 +40,8 @@
#include <stdlib.h>
#include "nettle-meta.h"
+#include "ocb.h"
+#include "aes.h"
/* For definition of NETTLE_MAX_HASH_CONTEXT_SIZE. */
#include "sha3.h"
@@ -127,4 +129,17 @@ extern const struct nettle_hash nettle_openssl_sha1;
extern const struct nettle_hash * const _nettle_hashes[];
+/* OCB-declarations to be moved to a public header file, once it's
+ settled which nonce and tag sizes to use. */
+#define OCB_NONCE_SIZE 12
+
+struct ocb_aes128_ctx
+{
+ struct ocb_ctx ocb;
+ struct ocb_aes128_encrypt_key key;
+ struct aes128_ctx decrypt;
+};
+
+extern const struct nettle_aead nettle_ocb_aes128;
+
#endif /* NETTLE_INTERNAL_H_INCLUDED */
diff --git a/ocb-aes128.c b/ocb-aes128.c
new file mode 100644
index 00000000..c72ada2c
--- /dev/null
+++ b/ocb-aes128.c
@@ -0,0 +1,118 @@
+/* ocb-aes128.c
+
+ Copyright (C) 2022 Niels Möller
+
+ 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 "ocb.h"
+
+void
+ocb_aes128_set_encrypt_key (struct ocb_aes128_encrypt_key *ocb_key, const uint8_t *key)
+{
+ aes128_set_encrypt_key (&ocb_key->encrypt, key);
+ ocb_set_key (&ocb_key->ocb, &ocb_key->encrypt, (nettle_cipher_func *) aes128_encrypt);
+}
+
+void
+ocb_aes128_set_decrypt_key (struct ocb_aes128_encrypt_key *ocb_key, struct aes128_ctx *decrypt,
+ const uint8_t *key)
+{
+ ocb_aes128_set_encrypt_key (ocb_key, key);
+ aes128_invert_key (decrypt, &ocb_key->encrypt);
+}
+
+void
+ocb_aes128_set_nonce (struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t tag_length, size_t nonce_length, const uint8_t *nonce)
+{
+ ocb_set_nonce (ctx, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ tag_length, nonce_length, nonce);
+}
+
+void
+ocb_aes128_update (struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, const uint8_t *data)
+{
+ ocb_update (ctx, &key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ length, data);
+}
+
+void
+ocb_aes128_encrypt(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ ocb_encrypt (ctx, &key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ length, dst, src);
+}
+
+void
+ocb_aes128_decrypt(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ const struct aes128_ctx *decrypt,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ ocb_decrypt (ctx, &key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ decrypt, (nettle_cipher_func *) aes128_decrypt,
+ length, dst, src);
+}
+
+void
+ocb_aes128_digest(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, uint8_t *digest)
+{
+ ocb_digest (ctx, &key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ length, digest);
+}
+
+void
+ocb_aes128_encrypt_message (const struct ocb_aes128_encrypt_key *key,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t clength, uint8_t *dst, const uint8_t *src)
+{
+ ocb_encrypt_message (&key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ nlength, nonce, alength, adata, tlength, clength, dst, src);
+}
+
+int
+ocb_aes128_decrypt_message (const struct ocb_aes128_encrypt_key *key,
+ const struct aes128_ctx *decrypt,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t mlength, uint8_t *dst, const uint8_t *src)
+{
+ return ocb_decrypt_message (&key->ocb, &key->encrypt, (nettle_cipher_func *) aes128_encrypt,
+ &decrypt, (nettle_cipher_func *) aes128_decrypt,
+ nlength, nonce, alength, adata,
+ tlength, mlength, dst, src);
+}
diff --git a/ocb.c b/ocb.c
new file mode 100644
index 00000000..9de90af7
--- /dev/null
+++ b/ocb.c
@@ -0,0 +1,432 @@
+/* ocb.c
+
+ OCB AEAD mode, RFC 7253
+
+ Copyright (C) 2021 Niels Möller
+
+ 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 <string.h>
+
+#include "ocb.h"
+#include "block-internal.h"
+#include "bswap-internal.h"
+#include "memops.h"
+
+#define OCB_MAX_BLOCKS 16
+
+/* Returns 64 bits from the concatenation (u0, u1), starting from bit offset. */
+static inline uint64_t
+extract(uint64_t u0, uint64_t u1, unsigned offset)
+{
+ if (offset == 0)
+ return u0;
+ u0 = bswap64_if_le(u0);
+ u1 = bswap64_if_le(u1);
+ return bswap64_if_le((u0 << offset) | (u1 >> (64 - offset)));
+}
+
+void
+ocb_set_key (struct ocb_key *key, const void *cipher, nettle_cipher_func *f)
+{
+ static const union nettle_block16 zero_block;
+ f (cipher, OCB_BLOCK_SIZE, key->L[0].b, zero_block.b);
+ block16_mulx_be (&key->L[1], &key->L[0]);
+ block16_mulx_be (&key->L[2], &key->L[1]);
+}
+
+/* Add x^k L[2], where k is the number of trailing zero bits in i. */
+static void
+update_offset(const struct ocb_key *key,
+ union nettle_block16 *offset, size_t i)
+{
+ if (i & 1)
+ block16_xor (offset, &key->L[2]);
+ else
+ {
+ assert (i > 0);
+ union nettle_block16 diff;
+ block16_mulx_be (&diff, &key->L[2]);
+ for (i >>= 1; !(i&1); i >>= 1)
+ block16_mulx_be (&diff, &diff);
+
+ block16_xor (offset, &diff);
+ }
+}
+
+static void
+pad_block (union nettle_block16 *block, size_t length, const uint8_t *data)
+{
+ memcpy (block->b, data, length);
+ block->b[length] = 0x80;
+ memset (block->b + length + 1, 0, OCB_BLOCK_SIZE - 1 - length);
+}
+
+void
+ocb_set_nonce (struct ocb_ctx *ctx,
+ const void *cipher, nettle_cipher_func *f,
+ size_t tag_length,
+ size_t nonce_length, const uint8_t *nonce)
+{
+ union nettle_block16 top;
+ uint64_t stretch;
+
+ unsigned bottom;
+ assert (nonce_length < 16);
+ assert (tag_length > 0);
+ assert (tag_length <= 16);
+
+ /* Bit size, or zero for tag_length == 16 */
+ top.b[0] = (tag_length & 15) << 4;
+ memset (top.b + 1, 0, 15 - nonce_length);
+ top.b[15 - nonce_length] |= 1;
+ memcpy (top.b + 16 - nonce_length, nonce, nonce_length);
+ bottom = top.b[15] & 0x3f;
+ top.b[15] &= 0xc0;
+
+ f (cipher, OCB_BLOCK_SIZE, top.b, top.b);
+
+ stretch = top.u64[0];
+#if WORDS_BIGENDIAN
+ stretch ^= (top.u64[0] << 8) | (top.u64[1] >> 56);
+#else
+ stretch ^= (top.u64[0] >> 8) | (top.u64[1] << 56);
+#endif
+
+ ctx->initial.u64[0] = extract(top.u64[0], top.u64[1], bottom);
+ ctx->initial.u64[1] = extract(top.u64[1], stretch, bottom);
+ ctx->sum.u64[0] = ctx->sum.u64[1] = 0;
+ ctx->checksum.u64[0] = ctx->checksum.u64[1] = 0;
+
+ ctx->data_count = ctx->message_count = 0;
+}
+
+static void
+ocb_fill_n (const struct ocb_key *key,
+ union nettle_block16 *offset, size_t count,
+ size_t n, union nettle_block16 *o)
+{
+ assert (n > 0);
+ union nettle_block16 *prev;
+ if (count & 1)
+ prev = offset;
+ else
+ {
+ /* Do a single block to align block count. */
+ count++; /* Always odd. */
+ block16_xor (offset, &key->L[2]);
+ block16_set (&o[0], offset);
+ prev = o;
+ n--; o++;
+ }
+
+ for (; n >= 2; n -= 2, o += 2)
+ {
+ size_t i;
+ count += 2; /* Always odd. */
+
+ /* Based on trailing zeros of ctx->message_count - 1, the
+ initial shift below discards a one bit. */
+ block16_mulx_be (&o[0], &key->L[2]);
+ for (i = count >> 1; !(i&1); i >>= 1)
+ block16_mulx_be (&o[0], &o[0]);
+
+ block16_xor (&o[0], prev);
+ block16_xor3 (&o[1], &o[0], &key->L[2]);
+ prev = &o[1];
+ }
+ block16_set(offset, prev);
+
+ if (n > 0)
+ {
+ update_offset (key, offset, ++count);
+ block16_set (o, offset);
+ }
+}
+
+void
+ocb_update (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, const uint8_t *data)
+{
+ union nettle_block16 block[OCB_MAX_BLOCKS];
+ size_t n = length / OCB_BLOCK_SIZE;
+ assert (ctx->message_count == 0);
+
+ if (ctx->data_count == 0)
+ ctx->offset.u64[0] = ctx->offset.u64[1] = 0;
+
+ while (n > 0)
+ {
+ size_t size, i;
+ size_t blocks = (n <= OCB_MAX_BLOCKS) ? n
+ : OCB_MAX_BLOCKS - 1 + (ctx->data_count & 1);
+
+ ocb_fill_n (key, &ctx->offset, ctx->data_count, blocks, block);
+ ctx->data_count += blocks;
+
+ size = blocks * OCB_BLOCK_SIZE;
+ memxor (block[0].b, data, size);
+ f (cipher, size, block[0].b, block[0].b);
+ for (i = 0; i < blocks; i++)
+ block16_xor(&ctx->sum, &block[i]);
+
+ n -= blocks; data += size;
+ }
+
+ length &= 15;
+ if (length > 0)
+ {
+ union nettle_block16 block;
+ pad_block (&block, length, data);
+ block16_xor (&ctx->offset, &key->L[0]);
+ block16_xor (&block, &ctx->offset);
+
+ f (cipher, OCB_BLOCK_SIZE, block.b, block.b);
+ block16_xor (&ctx->sum, &block);
+ }
+}
+
+static void
+ocb_crypt_n (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t n, uint8_t *dst, const uint8_t *src)
+{
+ union nettle_block16 o[OCB_MAX_BLOCKS], block[OCB_MAX_BLOCKS];
+ size_t size;
+
+ while (n > 0)
+ {
+ size_t blocks = (n <= OCB_MAX_BLOCKS) ? n
+ : OCB_MAX_BLOCKS - 1 + (ctx->message_count & 1);
+
+ ocb_fill_n (key, &ctx->offset, ctx->message_count, blocks, o);
+ ctx->message_count += n;
+
+ size = blocks * OCB_BLOCK_SIZE;
+ memxor3 (block[0].b, o[0].b, src, size);
+ f (cipher, size, block[0].b, block[0].b);
+ memxor3 (dst, block[0].b, o[0].b, size);
+
+ n -= blocks; src += size; dst -= size;
+ }
+}
+
+/* Rotate bytes c positions to the right, in memory order. */
+#if WORDS_BIGENDIAN
+# define MEM_ROTATE_RIGHT(c, s0, s1) do { \
+ uint64_t __rotate_t = ((s0) >> (8*(c))) | ((s1) << (64-8*(c))); \
+ (s1) = ((s1) >> (8*(c))) | ((s0) << (64-8*(c))); \
+ (s0) = __rotate_t; \
+ } while (0)
+#else
+# define MEM_ROTATE_RIGHT(c, s0, s1) do { \
+ uint64_t __rotate_t = ((s0) << (8*(c))) | ((s1) >> (64-8*(c))); \
+ (s1) = ((s1) << (8*(c))) | ((s0) >> (64-8*(c))); \
+ (s0) = __rotate_t; \
+ } while (0)
+#endif
+
+/* Mask for the first c bytes in memory */
+#if WORDS_BIGENDIAN
+# define MEM_MASK(c) (-((uint64_t) 1 << (64 - 8*(c))))
+#else
+# define MEM_MASK(c) (((uint64_t) 1 << (8*(c))) - 1)
+#endif
+
+/* Checksum of n complete blocks. */
+static void
+ocb_checksum_n (union nettle_block16 *checksum,
+ size_t n, const uint8_t *src)
+{
+ unsigned initial;
+ uint64_t edge_word = 0;
+ uint64_t s0, s1;
+
+ if (n == 1)
+ {
+ memxor (checksum->b, src, OCB_BLOCK_SIZE);
+ return;
+ }
+
+ /* Initial unaligned bytes. */
+ initial = -(uintptr_t) src & 7;
+
+ if (initial > 0)
+ {
+ /* Input not 64-bit aligned. Read initial bytes. */
+ unsigned i;
+ /* Edge word is read in big-endian order */
+ for (i = initial; i > 0; i--)
+ edge_word = (edge_word << 8) + *src++;
+ n--;
+ }
+
+ /* Now src is 64-bit aligned, so do 64-bit reads. */
+ for (s0 = s1 = 0 ; n > 0; n--, src += OCB_BLOCK_SIZE)
+ {
+ s0 ^= ((const uint64_t *) src)[0];
+ s1 ^= ((const uint64_t *) src)[1];
+ }
+ if (initial > 0)
+ {
+ unsigned i;
+ uint64_t mask;
+ s0 ^= ((const uint64_t *) src)[0];
+ for (i = 8 - initial, src += 8; i > 0; i--)
+ edge_word = (edge_word << 8) + *src++;
+
+ /* Rotate [s0, s1] right initial bytes. */
+ MEM_ROTATE_RIGHT(initial, s0, s1);
+ /* Add in the edge bytes. */
+ mask = MEM_MASK(initial);
+ edge_word = bswap64_if_le (edge_word);
+ s0 ^= (edge_word & mask);
+ s1 ^= (edge_word & ~mask);
+ }
+ checksum->u64[0] ^= s0;
+ checksum->u64[1] ^= s1;
+}
+
+void
+ocb_encrypt (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ size_t n = length / OCB_BLOCK_SIZE;
+
+ if (ctx->message_count == 0)
+ ctx->offset = ctx->initial;
+
+ if (n > 0)
+ {
+ ocb_checksum_n (&ctx->checksum, n, src);
+ ocb_crypt_n (ctx, key, cipher, f, n, dst, src);
+ length &= 15;
+ }
+ if (length > 0)
+ {
+ union nettle_block16 block;
+
+ src += n*OCB_BLOCK_SIZE; dst += n*OCB_BLOCK_SIZE;
+
+ pad_block (&block, length, src);
+ block16_xor (&ctx->checksum, &block);
+
+ block16_xor (&ctx->offset, &key->L[0]);
+ f (cipher, OCB_BLOCK_SIZE, block.b, ctx->offset.b);
+ memxor3 (dst, block.b, src, length);
+ ctx->message_count++;
+ }
+}
+
+void
+ocb_decrypt (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *encrypt_ctx, nettle_cipher_func *encrypt,
+ const void *decrypt_ctx, nettle_cipher_func *decrypt,
+ size_t length, uint8_t *dst, const uint8_t *src)
+{
+ size_t n = length / OCB_BLOCK_SIZE;
+
+ if (ctx->message_count == 0)
+ ctx->offset = ctx->initial;
+
+ if (n > 0)
+ {
+ ocb_crypt_n (ctx, key, decrypt_ctx, decrypt, n, dst, src);
+ ocb_checksum_n (&ctx->checksum, n, dst);
+ length &= 15;
+ }
+ if (length > 0)
+ {
+ union nettle_block16 block;
+
+ src += n*OCB_BLOCK_SIZE; dst += n*OCB_BLOCK_SIZE;
+
+ block16_xor (&ctx->offset, &key->L[0]);
+ encrypt (encrypt_ctx, OCB_BLOCK_SIZE, block.b, ctx->offset.b);
+ memxor3 (dst, block.b, src, length);
+
+ pad_block (&block, length, dst);
+ block16_xor (&ctx->checksum, &block);
+ ctx->message_count++;
+ }
+}
+
+void
+ocb_digest (const struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, uint8_t *digest)
+{
+ union nettle_block16 block;
+ assert (length <= OCB_DIGEST_SIZE);
+ block16_xor3 (&block, &key->L[1],
+ (ctx->message_count > 0) ? &ctx->offset : &ctx->initial);
+ block16_xor (&block, &ctx->checksum);
+ f (cipher, OCB_BLOCK_SIZE, block.b, block.b);
+ memxor3 (digest, block.b, ctx->sum.b, length);
+}
+
+void
+ocb_encrypt_message (const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t clength, uint8_t *dst, const uint8_t *src)
+{
+ struct ocb_ctx ctx;
+ assert (clength >= tlength);
+ ocb_set_nonce (&ctx, cipher, f, tlength, nlength, nonce);
+ ocb_update (&ctx, key, cipher, f, alength, adata);
+ ocb_encrypt (&ctx, key, cipher, f, clength - tlength, dst, src);
+ ocb_digest (&ctx, key, cipher, f, tlength, dst + clength - tlength);
+}
+
+int
+ocb_decrypt_message (const struct ocb_key *key,
+ const void *encrypt_ctx, nettle_cipher_func *encrypt,
+ const void *decrypt_ctx, nettle_cipher_func *decrypt,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t mlength, uint8_t *dst, const uint8_t *src)
+{
+ struct ocb_ctx ctx;
+ union nettle_block16 digest;
+ ocb_set_nonce (&ctx, encrypt_ctx, encrypt, tlength, nlength, nonce);
+ ocb_update (&ctx, key, encrypt_ctx, encrypt, alength, adata);
+ ocb_decrypt (&ctx, key, encrypt_ctx, encrypt, decrypt_ctx, decrypt,
+ mlength, dst, src);
+ ocb_digest (&ctx, key, encrypt_ctx, encrypt, tlength, digest.b);
+ return memeql_sec(digest.b, src + mlength, tlength);
+}
diff --git a/ocb.h b/ocb.h
new file mode 100644
index 00000000..8d79cdf6
--- /dev/null
+++ b/ocb.h
@@ -0,0 +1,188 @@
+/* ocb.h
+
+ OCB AEAD mode, RFC 7253
+
+ Copyright (C) 2021 Niels Möller
+
+ 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_OCB_H_INCLUDED
+#define NETTLE_OCB_H_INCLUDED
+
+#include "nettle-types.h"
+#include "aes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Name mangling */
+#define ocb_set_key nettle_ocb_set_key
+#define ocb_set_nonce nettle_ocb_set_nonce
+#define ocb_update nettle_ocb_update
+#define ocb_encrypt nettle_ocb_encrypt
+#define ocb_decrypt nettle_ocb_decrypt
+#define ocb_digest nettle_ocb_digest
+#define ocb_encrypt_message nettle_ocb_encrypt_message
+#define ocb_decrypt_message nettle_ocb_decrypt_message
+#define ocb_aes128_set_encrypt_key nettle_ocb_aes128_set_encrypt_key
+#define ocb_aes128_set_decrypt_key nettle_ocb_aes128_set_decrypt_key
+#define ocb_aes128_set_nonce nettle_ocb_aes128_set_nonce
+#define ocb_aes128_update nettle_ocb_aes128_update
+#define ocb_aes128_encrypt nettle_ocb_aes128_encrypt
+#define ocb_aes128_decrypt nettle_ocb_aes128_decrypt
+#define ocb_aes128_digest nettle_ocb_aes128_digest
+#define ocb_aes128_encrypt_message nettle_ocb_aes128_encrypt_message
+#define ocb_aes128_decrypt_message nettle_ocb_aes128_decrypt_message
+
+#define OCB_BLOCK_SIZE 16
+#define OCB_DIGEST_SIZE 16
+
+struct ocb_key {
+ /* L_*, L_$ and L_0, and one reserved entry */
+ union nettle_block16 L[4];
+};
+
+struct ocb_ctx {
+ /* Initial offset, Offset_0 in the spec. */
+ union nettle_block16 initial;
+ /* Offset, updated per block. */
+ union nettle_block16 offset;
+ /* Authentication for the associated data */
+ union nettle_block16 sum;
+ /* Authentication for the message */
+ union nettle_block16 checksum;
+ /* Count of processed blocks. */
+ size_t data_count;
+ size_t message_count;
+};
+
+void
+ocb_set_key (struct ocb_key *key, const void *cipher, nettle_cipher_func *f);
+
+void
+ocb_set_nonce (struct ocb_ctx *ctx,
+ const void *cipher, nettle_cipher_func *f,
+ size_t tag_length, size_t nonce_length, const uint8_t *nonce);
+
+void
+ocb_update (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, const uint8_t *data);
+
+void
+ocb_encrypt (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, uint8_t *dst, const uint8_t *src);
+
+void
+ocb_decrypt (struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *encrypt_ctx, nettle_cipher_func *encrypt,
+ const void *decrypt_ctx, nettle_cipher_func *decrypt,
+ size_t length, uint8_t *dst, const uint8_t *src);
+
+void
+ocb_digest (const struct ocb_ctx *ctx, const struct ocb_key *key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t length, uint8_t *digest);
+
+
+void
+ocb_encrypt_message (const struct ocb_key *ocb_key,
+ const void *cipher, nettle_cipher_func *f,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t clength, uint8_t *dst, const uint8_t *src);
+
+int
+ocb_decrypt_message (const struct ocb_key *ocb_key,
+ const void *encrypt_ctx, nettle_cipher_func *encrypt,
+ const void *decrypt_ctx, nettle_cipher_func *decrypt,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t mlength, uint8_t *dst, const uint8_t *src);
+
+/* OCB-AES */
+/* This struct represents an expanded key for ocb-aes encryption. For
+ decryption, a separate decryption context is needed as well. */
+struct ocb_aes128_encrypt_key
+{
+ struct ocb_key ocb;
+ struct aes128_ctx encrypt;
+};
+
+void
+ocb_aes128_set_encrypt_key (struct ocb_aes128_encrypt_key *ocb, const uint8_t *key);
+
+void
+ocb_aes128_set_decrypt_key (struct ocb_aes128_encrypt_key *ocb, struct aes128_ctx *decrypt,
+ const uint8_t *key);
+
+void
+ocb_aes128_set_nonce (struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t tag_length, size_t nonce_length, const uint8_t *nonce);
+
+void
+ocb_aes128_update (struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, const uint8_t *data);
+
+void
+ocb_aes128_encrypt(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, uint8_t *dst, const uint8_t *src);
+
+void
+ocb_aes128_decrypt(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ const struct aes128_ctx *decrypt,
+ size_t length, uint8_t *dst, const uint8_t *src);
+
+void
+ocb_aes128_digest(struct ocb_ctx *ctx, const struct ocb_aes128_encrypt_key *key,
+ size_t length, uint8_t *digest);
+
+void
+ocb_aes128_encrypt_message (const struct ocb_aes128_encrypt_key *key,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t clength, uint8_t *dst, const uint8_t *src);
+
+int
+ocb_aes128_decrypt_message (const struct ocb_aes128_encrypt_key *key,
+ const struct aes128_ctx *decrypt,
+ size_t nlength, const uint8_t *nonce,
+ size_t alength, const uint8_t *adata,
+ size_t tlength,
+ size_t mlength, uint8_t *dst, const uint8_t *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_OCB_H_INCLUDED */
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index 025ab72d..be0cb965 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -28,7 +28,7 @@ TS_NETTLE_SOURCES = aes-test.c aes-keywrap-test.c arcfour-test.c arctwo-test.c \
serpent-test.c twofish-test.c version-test.c \
knuth-lfib-test.c \
cbc-test.c cfb-test.c ctr-test.c gcm-test.c eax-test.c ccm-test.c \
- cmac-test.c siv-test.c siv-gcm-test.c \
+ cmac-test.c ocb-test.c siv-test.c siv-gcm-test.c \
poly1305-test.c chacha-poly1305-test.c \
hmac-test.c umac-test.c \
meta-hash-test.c meta-cipher-test.c\
diff --git a/testsuite/ocb-test.c b/testsuite/ocb-test.c
new file mode 100644
index 00000000..ecc73f62
--- /dev/null
+++ b/testsuite/ocb-test.c
@@ -0,0 +1,235 @@
+#include "testutils.h"
+#include "nettle-internal.h"
+
+/* For 96-bit tag */
+static void
+set_nonce_tag96 (struct ocb_aes128_ctx *ctx, size_t length, const uint8_t *nonce)
+{
+ assert (length == OCB_NONCE_SIZE);
+ ocb_aes128_set_nonce (&ctx->ocb, &ctx->key,
+ 12, OCB_NONCE_SIZE, nonce);
+}
+
+void
+test_main(void)
+{
+ /* From RFC 7253 */
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA99887766554433221100"), /* nonce */
+ SHEX("785407BFFFC8AD9EDCC5520AC9111EE6")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("0001020304050607"), /* auth data */
+ SHEX("0001020304050607"), /* plaintext */
+ SHEX("6820B3657B6F615A"), /* ciphertext */
+ SHEX("BBAA99887766554433221101"), /* nonce */
+ SHEX("5725BDA0D3B4EB3A257C9AF1F8F03009")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("0001020304050607"), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA99887766554433221102"), /* nonce */
+ SHEX("81017F8203F081277152FADE694A0A00")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("0001020304050607"), /* plaintext */
+ SHEX("45DD69F8F5AAE724"), /* ciphertext */
+ SHEX("BBAA99887766554433221103"), /* nonce */
+ SHEX("14054CD1F35D82760B2CD00D2F99BFA9")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* plaintext */
+ SHEX("571D535B60B277188BE5147170A9A22C"), /* ciphertext */
+ SHEX("BBAA99887766554433221104"), /* nonce */
+ SHEX("3AD7A4FF3835B8C5701C1CCEC8FC3358")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA99887766554433221105"), /* nonce */
+ SHEX("8CF761B6902EF764462AD86498CA6B97")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* plaintext */
+ SHEX("5CE88EC2E0692706A915C00AEB8B2396"), /* ciphertext */
+ SHEX("BBAA99887766554433221106"), /* nonce */
+ SHEX("F40E1C743F52436BDF06D8FA1ECA343D")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"), /* plaintext */
+ SHEX("1CA2207308C87C010756104D8840CE1952F09673A448A122"), /* ciphertext */
+ SHEX("BBAA99887766554433221107"), /* nonce */
+ SHEX("C92C62241051F57356D7F3C90BB0E07F")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA99887766554433221108"), /* nonce */
+ SHEX("6DC225A071FC1B9F7C69F93B0F1E10DE")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"), /* plaintext */
+ SHEX("221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3C"), /* ciphertext */
+ SHEX("BBAA99887766554433221109"), /* nonce */
+ SHEX("E725F32494B9F914D85C0B1EB38357FF")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F"), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F"), /* plaintext */
+ SHEX("BD6F6C496201C69296C11EFD138A467ABD3C707924B964DE"
+ "AFFC40319AF5A485"), /* ciphertext */
+ SHEX("BBAA9988776655443322110A"), /* nonce */
+ SHEX("40FBBA186C5553C68AD9F592A79A4240")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F"), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA9988776655443322110B"), /* nonce */
+ SHEX("FE80690BEE8A485D11F32965BC9D2A32")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F"), /* plaintext */
+ SHEX("2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF4"
+ "6040C53F1432BCDF"), /* ciphertext */
+ SHEX("BBAA9988776655443322110C"), /* nonce */
+ SHEX("B5E1DDE3BC18A5F840B52E653444D5DF")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* plaintext */
+ SHEX("D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460"
+ "6E59F9C1D0DDC54B65E8628E568BAD7A"), /* ciphertext */
+ SHEX("BBAA9988776655443322110D"), /* nonce */
+ SHEX("ED07BA06A4A69483A7035490C5769E60")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* auth data */
+ SHEX(""), /* plaintext */
+ SHEX(""), /* ciphertext */
+ SHEX("BBAA9988776655443322110E"), /* nonce */
+ SHEX("C5CD9D1850C141E358649994EE701B68")); /* tag */
+
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* plaintext */
+ SHEX("4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15"
+ "A5DDBFC5787E50B5CC55EE507BCB084E"), /* ciphertext */
+ SHEX("BBAA9988776655443322110F"), /* nonce */
+ SHEX("479AD363AC366B95 A98CA5F3000B1479")); /* tag */
+
+ /* Test with 96-bit tag. */
+ test_aead(&nettle_ocb_aes128, (nettle_hash_update_func *) set_nonce_tag96,
+ SHEX("0F0E0D0C0B0A09080706050403020100"), /* key */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* auth data */
+ SHEX("000102030405060708090A0B0C0D0E0F1011121314151617"
+ "18191A1B1C1D1E1F2021222324252627"), /* plaintext */
+ SHEX("1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1"
+ "A0124B0A55BAE884ED93481529C76B6A"), /* ciphertext */
+ SHEX("BBAA9988776655443322110D"), /* nonce */
+ SHEX("D0C515F4D1CDD4FDAC4F02AA")); /* tag */
+
+ /* 16 blocks, not verified with other implementations or any
+ authoritative test vector.not an authoritative test vector. */
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
+ SHEX("4412923493c57d5d e0d700f753cce0d1"
+ "d2d95060122e9f15 a5ddbfc5787e50b5"
+ "11dfb888da244711 f051dbce82b0b9a7"
+ "cb14869b164e55eb 578e41fa435ff220"
+ "25ed114f6ec18cd6 7b743ab299e596f6"
+ "6100fba539db164d 765eaff0bf489ace"
+ "90ff6af96d1c395b 8dd586b154a0ecea"
+ "504395c5592cf2f0 03a3878585a0bfd3"
+ "b4039d15bc47a6d6 4a51f7302a976bb0"
+ "175167bcb5d8f071 a3faff70544ab2ba"
+ "52947d35d6e545e9 bda57b3972ecad10"
+ "f0e85aec389f4276 2e58978918d4c285"
+ "c2088ca8ac48095c 976065aa47766756"
+ "7a507bab08315b2e 36327e8103a6a70d"
+ "7f9f5318684697b2 bf95d65fa5458e6e"
+ "f40a974cb940e8fd 63baf0ce96773279"),
+ SHEX("BBAA9988776655443322110F"), /* nonce */
+ SHEX("3aa4f4e4b4ff142c 9357291589fa25d8")); /* tag */
+
+ /* 16 complete blocks + left-over bytes, not verified with other
+ implementations or any authoritative test vector. */
+ test_aead(&nettle_ocb_aes128, NULL,
+ SHEX("000102030405060708090A0B0C0D0E0F"), /* key */
+ SHEX(""), /* auth data */
+ SHEX("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+ "deadbeaf"),
+ SHEX("4412923493c57d5d e0d700f753cce0d1"
+ "d2d95060122e9f15 a5ddbfc5787e50b5"
+ "11dfb888da244711 f051dbce82b0b9a7"
+ "cb14869b164e55eb 578e41fa435ff220"
+ "25ed114f6ec18cd6 7b743ab299e596f6"
+ "6100fba539db164d 765eaff0bf489ace"
+ "90ff6af96d1c395b 8dd586b154a0ecea"
+ "504395c5592cf2f0 03a3878585a0bfd3"
+ "b4039d15bc47a6d6 4a51f7302a976bb0"
+ "175167bcb5d8f071 a3faff70544ab2ba"
+ "52947d35d6e545e9 bda57b3972ecad10"
+ "f0e85aec389f4276 2e58978918d4c285"
+ "c2088ca8ac48095c 976065aa47766756"
+ "7a507bab08315b2e 36327e8103a6a70d"
+ "7f9f5318684697b2 bf95d65fa5458e6e"
+ "f40a974cb940e8fd 63baf0ce96773279"
+ "1dd97611"),
+ SHEX("BBAA9988776655443322110F"), /* nonce */
+ SHEX("8a24edb596b59425 43ec197d5369979b")); /* tag */
+
+}