From 9cf0e2d2675268a403194d85a78a44e8cbdf562b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= Date: Tue, 7 Feb 2023 20:04:03 +0100 Subject: Implement OCB mode, RFC 7253. --- ChangeLog | 36 +++++ Makefile.in | 3 +- block-internal.h | 8 + nettle-internal.c | 60 +++++++ nettle-internal.h | 15 ++ ocb-aes128.c | 118 ++++++++++++++ ocb.c | 432 ++++++++++++++++++++++++++++++++++++++++++++++++++ ocb.h | 188 ++++++++++++++++++++++ testsuite/Makefile.in | 2 +- testsuite/ocb-test.c | 235 +++++++++++++++++++++++++++ 10 files changed, 1095 insertions(+), 2 deletions(-) create mode 100644 ocb-aes128.c create mode 100644 ocb.c create mode 100644 ocb.h create mode 100644 testsuite/ocb-test.c diff --git a/ChangeLog b/ChangeLog index 618c57dc..94f78204 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2023-02-07 Niels Möller + + 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 * 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 @@ -50,6 +50,14 @@ block16_zero (union nettle_block16 *r) *r = zero_block; } +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 #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 + +#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 */ + +} -- cgit v1.2.1