From 680fc613acf9d873a613e8b022b57440cc2ac19c Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 29 May 2026 14:54:55 -0400 Subject: [PATCH 1/3] vendors libaes-siv v1.0.1 (9681279cfaa6e6399bb7ca3afbbc27fc2e19df4b) --- .gitignore | 3 + Makefile.am | 18 +- aes_siv/COPYING | 202 ++++++++++++++++ aes_siv/aes_siv.c | 594 ++++++++++++++++++++++++++++++++++++++++++++++ aes_siv/aes_siv.h | 57 +++++ aes_siv/tests.c | 584 +++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 6 - liburcrypt.pc.in | 1 - 8 files changed, 1453 insertions(+), 12 deletions(-) create mode 100644 aes_siv/COPYING create mode 100644 aes_siv/aes_siv.c create mode 100644 aes_siv/aes_siv.h create mode 100644 aes_siv/tests.c diff --git a/.gitignore b/.gitignore index 52e6bed..a8c7cbb 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ build-aux/m4/lt~obsolete.m4 # (which is called by configure script)) Makefile +*.o +*.lo +*.la diff --git a/Makefile.am b/Makefile.am index 58cdad5..d64bb40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,8 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 AM_CFLAGS = -Wall -g -O3 lib_LTLIBRARIES = liburcrypt.la -noinst_LTLIBRARIES = libed25519.la \ +noinst_LTLIBRARIES = libaes_siv.la \ + libed25519.la \ libge_additions.la \ libargon2.la \ libblake3.la \ @@ -13,6 +14,7 @@ noinst_LTLIBRARIES = libed25519.la \ include_HEADERS = urcrypt/urcrypt.h noinst_HEADERS = urcrypt/util.h \ + aes_siv/aes_siv.h \ ed25519/src/ed25519.h \ ed25519/src/ge.h \ ge-additions/ge-additions.h \ @@ -28,7 +30,8 @@ noinst_HEADERS = urcrypt/util.h \ pkgconfig_DATA = liburcrypt-$(URCRYPT_API_VERSION).pc DISTCLEANFILES = $(pkgconfig_DATA) -liburcrypt_la_CPPFLAGS = -I$(srcdir)/ed25519/src \ +liburcrypt_la_CPPFLAGS = -I$(srcdir)/aes_siv \ + -I$(srcdir)/ed25519/src \ -I$(srcdir)/ge-additions \ -I$(srcdir)/argon2/include \ -I$(srcdir)/argon2/src/blake2 \ @@ -38,7 +41,7 @@ liburcrypt_la_CPPFLAGS = -I$(srcdir)/ed25519/src \ -I$(srcdir)/scrypt liburcrypt_la_LIBADD = $(LIBCRYPTO_LIBS) \ $(LIBSECP256K1_LIBS) \ - $(LIBAES_SIV_LIBS) \ + libaes_siv.la \ libed25519.la \ libge_additions.la \ libargon2.la \ @@ -47,8 +50,7 @@ liburcrypt_la_LIBADD = $(LIBCRYPTO_LIBS) \ libkeccak_tiny.la \ libscrypt.la liburcrypt_la_CFLAGS = $(LIBCRYPTO_CFLAGS) \ - $(LIBSECP256K1_CFLAGS) \ - $(LIBAES_SIV_CFLAGS) + $(LIBSECP256K1_CFLAGS) # urcrypt_ is used for public symbols, urcrypt__ for internal. liburcrypt_la_LDFLAGS = -export-symbols-regex '^urcrypt_[^_]' \ -version-info $(URCRYPT_LT_VERSION) @@ -68,6 +70,12 @@ liburcrypt_la_SOURCES = urcrypt/aes_cbc.c \ urcrypt/util.c \ urcrypt/util.h +# aes-siv (RFC 5297), vendored from https://github.com/dfoxfranke/libaes_siv +libaes_siv_la_CPPFLAGS = -I$(srcdir)/aes_siv +libaes_siv_la_CFLAGS = $(LIBCRYPTO_CFLAGS) +libaes_siv_la_SOURCES = aes_siv/aes_siv.c \ + aes_siv/aes_siv.h + # ed25519 libed25519_la_CFLAGS = -Wno-unused-result libed25519_la_SOURCES = ed25519/src/fixedint.h \ diff --git a/aes_siv/COPYING b/aes_siv/COPYING new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/aes_siv/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/aes_siv/aes_siv.c b/aes_siv/aes_siv.c new file mode 100644 index 0000000..00a510d --- /dev/null +++ b/aes_siv/aes_siv.c @@ -0,0 +1,594 @@ +/* Copyright (c) 2017-2019 Akamai Technologies, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define _POSIX_C_SOURCE 200112L +#define _ISOC99_SOURCE 1 + +#include "config.h" +#include "aes_siv.h" + +#include +#include +#include +#include +#ifdef ENABLE_DEBUG_OUTPUT +#include +#endif +#ifdef _MSC_VER +/* For _byteswap_uint64 */ +#include +#endif +#include + +#include +#include +#include + +#ifdef ENABLE_CTGRIND +#include +#endif + +#if CHAR_BIT != 8 +#error "libaes_siv requires an 8-bit char type" +#endif + +#if -1 != ~0 +#error "libaes_siv requires a two's-complement architecture" +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 +#undef inline +#elif defined(__GNUC__) || defined(__clang__) +#define inline __inline__ +#elif defined(_MSC_VER) +#define inline __inline +#else +#define inline +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define LIKELY(cond) __builtin_expect(cond, 1) +#define UNLIKELY(cond) __builtin_expect(cond, 0) +#else +#define LIKELY(cond) cond +#define UNLIKELY(cond) cond +#endif + +#ifndef ENABLE_CTGRIND +static inline void ct_poison(const void *data, size_t len) { + (void)data; + (void)len; +} +static inline void ct_unpoison(const void *data, size_t len) { + (void)data; + (void)len; +} +#endif + +static void debug(const char *label, const unsigned char *hex, size_t len) { +/* ENABLE_CTGRIND has to override ENABLE_DEBUG_OUTPUT since sensitive data + gets printed. +*/ +#if defined(ENABLE_DEBUG_OUTPUT) && !defined(ENABLE_CTGRIND) + size_t i; + printf("%16s: ", label); + for (i = 0; i < len; i++) { + if (i > 0 && i % 16 == 0) { + printf("\n "); + } + printf("%.2x", (int)hex[i]); + if (i > 0 && i % 4 == 3) { + printf(" "); + } + } + printf("\n"); +#else + (void)label; + (void)hex; + (void)len; +#endif +} + +typedef union block_un { + uint64_t word[2]; + unsigned char byte[16]; +} block; + +const union { + uint64_t word; + char byte[8]; +} endian = {0x0102030405060708}; + +#define I_AM_BIG_ENDIAN (endian.byte[0] == 1 && \ + endian.byte[1] == 2 && \ + endian.byte[2] == 3 && \ + endian.byte[3] == 4 && \ + endian.byte[4] == 5 && \ + endian.byte[5] == 6 && \ + endian.byte[6] == 7 && \ + endian.byte[7] == 8) + +#define I_AM_LITTLE_ENDIAN (endian.byte[0] == 8 && \ + endian.byte[1] == 7 && \ + endian.byte[2] == 6 && \ + endian.byte[3] == 5 && \ + endian.byte[4] == 4 && \ + endian.byte[5] == 3 && \ + endian.byte[6] == 2 && \ + endian.byte[7] == 1) + +#if defined(__GNUC__) || defined(__clang__) +static inline uint64_t bswap64(uint64_t x) { return __builtin_bswap64(x); } +#elif defined(_MSC_VER) +static inline uint64_t bswap64(uint64_t x) { return _byteswap_uint64(x); } +#else + +static inline uint32_t rotl(uint32_t x) { return (x << 8) | (x >> 24); } +static inline uint32_t rotr(uint32_t x) { return (x >> 8) | (x << 24); } + +static inline uint64_t bswap64(uint64_t x) { + uint32_t high = (uint32_t)(x >> 32); + uint32_t low = (uint32_t)x; + + high = (rotl(high) & 0x00ff00ff) | (rotr(high) & 0xff00ff00); + low = (rotl(low) & 0x00ff00ff) | (rotr(low) & 0xff00ff00); + return ((uint64_t)low) << 32 | (uint64_t)high; +} +#endif + +static inline uint64_t getword(block const *block, size_t i) { +#ifndef ENABLE_DEBUG_WEIRD_ENDIAN + if (I_AM_BIG_ENDIAN) { + return block->word[i]; + } else if (I_AM_LITTLE_ENDIAN) { + return bswap64(block->word[i]); + } else { +#endif + i <<= 3; + return ((uint64_t)block->byte[i + 7]) | + ((uint64_t)block->byte[i + 6] << 8) | + ((uint64_t)block->byte[i + 5] << 16) | + ((uint64_t)block->byte[i + 4] << 24) | + ((uint64_t)block->byte[i + 3] << 32) | + ((uint64_t)block->byte[i + 2] << 40) | + ((uint64_t)block->byte[i + 1] << 48) | + ((uint64_t)block->byte[i] << 56); +#ifndef ENABLE_DEBUG_WEIRD_ENDIAN + } +#endif +} + +static inline void putword(block *block, size_t i, uint64_t x) { +#ifndef ENABLE_DEBUG_WEIRD_ENDIAN + if (I_AM_BIG_ENDIAN) { + block->word[i] = x; + } else if (I_AM_LITTLE_ENDIAN) { + block->word[i] = bswap64(x); + } else { +#endif + i <<= 3; + block->byte[i] = (unsigned char)(x >> 56); + block->byte[i + 1] = (unsigned char)((x >> 48) & 0xff); + block->byte[i + 2] = (unsigned char)((x >> 40) & 0xff); + block->byte[i + 3] = (unsigned char)((x >> 32) & 0xff); + block->byte[i + 4] = (unsigned char)((x >> 24) & 0xff); + block->byte[i + 5] = (unsigned char)((x >> 16) & 0xff); + block->byte[i + 6] = (unsigned char)((x >> 8) & 0xff); + block->byte[i + 7] = (unsigned char)(x & 0xff); +#ifndef ENABLE_DEBUG_WEIRD_ENDIAN + } +#endif +} + +static inline void xorblock(block *x, block const *y) { + x->word[0] ^= y->word[0]; + x->word[1] ^= y->word[1]; +} + +/* Doubles `block`, which is 16 bytes representing an element + of GF(2**128) modulo the irreducible polynomial + x**128 + x**7 + x**2 + x + 1. */ +static inline void dbl(block *block) { + uint64_t high = getword(block, 0); + uint64_t low = getword(block, 1); + uint64_t high_carry = high & (((uint64_t)1) << 63); + uint64_t low_carry = low & (((uint64_t)1) << 63); + /* Assumes two's-complement arithmetic */ + int64_t low_mask = -((int64_t)(high_carry >> 63)) & 0x87; + uint64_t high_mask = low_carry >> 63; + high = (high << 1) | high_mask; + low = (low << 1) ^ (uint64_t)low_mask; + putword(block, 0, high); + putword(block, 1, low); +} + +struct AES_SIV_CTX_st { + /* d stores intermediate results of S2V; it corresponds to D from the + pseudocode in section 2.4 of RFC 5297. */ + block d; + EVP_CIPHER_CTX *cipher_ctx; + /* SIV_AES_Init() sets up cmac_ctx_init. cmac_ctx is a scratchpad used + by SIV_AES_AssociateData() and SIV_AES_(En|De)cryptFinal. */ + CMAC_CTX *cmac_ctx_init, *cmac_ctx; +}; + +void AES_SIV_CTX_cleanup(AES_SIV_CTX *ctx) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_CIPHER_CTX_reset(ctx->cipher_ctx); +#else + EVP_CIPHER_CTX_cleanup(ctx->cipher_ctx); +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER <= 0x10100060L + /* Workaround for an OpenSSL bug that causes a double free + if you call CMAC_CTX_cleanup() before CMAC_CTX_free(). + https://github.com/openssl/openssl/pull/2798 + */ + CMAC_CTX_free(ctx->cmac_ctx_init); + ctx->cmac_ctx_init = CMAC_CTX_new(); + CMAC_CTX_free(ctx->cmac_ctx); + ctx->cmac_ctx = CMAC_CTX_new(); +#else + CMAC_CTX_cleanup(ctx->cmac_ctx_init); + CMAC_CTX_cleanup(ctx->cmac_ctx); +#endif + OPENSSL_cleanse(&ctx->d, sizeof ctx->d); +} + +void AES_SIV_CTX_free(AES_SIV_CTX *ctx) { + if (ctx) { + EVP_CIPHER_CTX_free(ctx->cipher_ctx); + /* Prior to OpenSSL 1.0.2b, CMAC_CTX_free() crashes on NULL */ + if (LIKELY(ctx->cmac_ctx_init != NULL)) { + CMAC_CTX_free(ctx->cmac_ctx_init); + } + if (LIKELY(ctx->cmac_ctx != NULL)) { + CMAC_CTX_free(ctx->cmac_ctx); + } + OPENSSL_cleanse(&ctx->d, sizeof ctx->d); + OPENSSL_free(ctx); + } +} + +AES_SIV_CTX *AES_SIV_CTX_new(void) { + AES_SIV_CTX *ctx = OPENSSL_malloc(sizeof(struct AES_SIV_CTX_st)); + if (UNLIKELY(ctx == NULL)) { + return NULL; + } + + ctx->cipher_ctx = EVP_CIPHER_CTX_new(); + ctx->cmac_ctx_init = CMAC_CTX_new(); + ctx->cmac_ctx = CMAC_CTX_new(); + + if (UNLIKELY(ctx->cipher_ctx == NULL || + ctx->cmac_ctx_init == NULL || + ctx->cmac_ctx == NULL)) { + AES_SIV_CTX_free(ctx); + return NULL; + } + + return ctx; +} + +int AES_SIV_CTX_copy(AES_SIV_CTX *dst, AES_SIV_CTX const *src) { + memcpy(&dst->d, &src->d, sizeof src->d); + if(UNLIKELY(EVP_CIPHER_CTX_copy(dst->cipher_ctx, src->cipher_ctx) + != 1)) { + return 0; + } + if (UNLIKELY(CMAC_CTX_copy(dst->cmac_ctx_init, src->cmac_ctx_init) + != 1)) { + return 0; + } + /* Not necessary to copy cmac_ctx since it's just temporary + * storage */ + return 1; +} + +int AES_SIV_Init(AES_SIV_CTX *ctx, unsigned char const *key, size_t key_len) { + static const unsigned char zero[] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + size_t out_len; + int ret = 0; + + ct_poison(key, key_len); + + switch (key_len) { + case 32: + if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 16, + EVP_aes_128_cbc(), NULL) != 1)) { + goto done; + } + if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, + EVP_aes_128_ctr(), + NULL, key + 16, NULL) != 1)) { + goto done; + } + break; + case 48: + if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 24, + EVP_aes_192_cbc(), NULL) != 1)) { + goto done; + } + if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, + EVP_aes_192_ctr(), + NULL, key + 24, NULL) != 1)) { + goto done; + } + break; + case 64: + if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 32, + EVP_aes_256_cbc(), NULL) != 1)) { + goto done; + } + if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, + EVP_aes_256_ctr(), + NULL, key + 32, NULL) != 1)) { + goto done; + } + break; + default: + goto done; + } + + if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { + goto done; + } + if (UNLIKELY(CMAC_Update(ctx->cmac_ctx, zero, sizeof zero) != 1)) { + goto done; + } + out_len = sizeof ctx->d; + if (UNLIKELY(CMAC_Final(ctx->cmac_ctx, ctx->d.byte, &out_len) != 1)) { + goto done; + } + debug("CMAC(zero)", ctx->d.byte, out_len); + ret = 1; + + done: + ct_unpoison(key, key_len); + return ret; +} + +int AES_SIV_AssociateData(AES_SIV_CTX *ctx, unsigned char const *data, + size_t len) { + block cmac_out; + size_t out_len = sizeof cmac_out; + int ret = 0; + + ct_poison(data, len); + + dbl(&ctx->d); + debug("double()", ctx->d.byte, 16); + + if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { + goto done; + } + if (UNLIKELY(CMAC_Update(ctx->cmac_ctx, data, len) != 1)) { + goto done; + } + if (UNLIKELY(CMAC_Final(ctx->cmac_ctx, cmac_out.byte, &out_len) != 1)) { + goto done; + } + assert(out_len == 16); + debug("CMAC(ad)", cmac_out.byte, 16); + + xorblock(&ctx->d, &cmac_out); + debug("xor", ctx->d.byte, 16); + ret = 1; + +done: + ct_unpoison(data, len); + return ret; +} + +static inline int do_s2v_p(AES_SIV_CTX *ctx, block *out, + unsigned char const* in, size_t len) { + block t; + size_t out_len = sizeof out->byte; + + if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { + return 0; + } + + if(len >= 16) { + if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, in, len - 16) != 1)) { + return 0; + } + debug("xorend part 1", in, len - 16); + memcpy(&t, in + (len-16), 16); + xorblock(&t, &ctx->d); + debug("xorend part 2", t.byte, 16); + if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, t.byte, 16) != 1)) { + return 0; + } + } else { + size_t i; + memcpy(&t, in, len); + t.byte[len] = 0x80; + for(i = len + 1; i < 16; i++) { + t.byte[i] = 0; + } + debug("pad", t.byte, 16); + dbl(&ctx->d); + xorblock(&t, &ctx->d); + debug("xor", t.byte, 16); + if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, t.byte, 16) != 1)) { + return 0; + } + } + if(UNLIKELY(CMAC_Final(ctx->cmac_ctx, out->byte, &out_len) != 1)) { + return 0; + } + assert(out_len == 16); + debug("CMAC(final)", out->byte, 16); + return 1; +} + +static inline int do_encrypt(EVP_CIPHER_CTX *ctx, unsigned char *out, + unsigned char const *in, size_t len, block *icv) { +#ifdef ENABLE_DEBUG_TINY_CHUNK_SIZE + const int chunk_size = 7; +#else + const int chunk_size = 1 << 30; +#endif + size_t len_remaining = len; + int out_len; + int ret; + + if(UNLIKELY(EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, icv->byte) + != 1)) { + return 0; + } + + while(UNLIKELY(len_remaining > (size_t)chunk_size)) { + out_len = chunk_size; + if(UNLIKELY(EVP_EncryptUpdate(ctx, out, &out_len, in, out_len) + != 1)) { + return 0; + } + assert(out_len == chunk_size); + out += out_len; + in += out_len; + len_remaining -= (size_t)out_len; + } + + out_len = (int)len_remaining; + ret = EVP_EncryptUpdate(ctx, out, &out_len, in, out_len); + assert(!ret || out_len == (int)len_remaining); + return ret; +} + +int AES_SIV_EncryptFinal(AES_SIV_CTX *ctx, unsigned char *v_out, + unsigned char *c_out, unsigned char const *plaintext, + size_t len) { + block q; + int ret = 0; + + ct_poison(plaintext, len); + + if(UNLIKELY(do_s2v_p(ctx, &q, plaintext, len) != 1)) { + goto done; + } + + ct_unpoison(&q, sizeof q); + memcpy(v_out, &q, 16); + q.byte[8] &= 0x7f; + q.byte[12] &= 0x7f; + + if(UNLIKELY(do_encrypt(ctx->cipher_ctx, c_out, plaintext, len, &q) + != 1)) { + goto done; + } + + ret = 1; + debug("ciphertext", c_out, len); + +done: + ct_unpoison(plaintext, len); + ct_unpoison(c_out, len); + ct_unpoison(v_out, 16); + return ret; +} + +int AES_SIV_DecryptFinal(AES_SIV_CTX *ctx, unsigned char *out, + unsigned char const *v, unsigned char const *c, + size_t len) { + block t, q; + size_t i; + uint64_t result; + int ret = 0; + + ct_poison(c, len); + + memcpy(&q, v, 16); + q.byte[8] &= 0x7f; + q.byte[12] &= 0x7f; + + if(UNLIKELY(do_encrypt(ctx->cipher_ctx, out, c, len, &q) != 1)) { + goto done; + } + debug("plaintext", out, len); + + if(UNLIKELY(do_s2v_p(ctx, &t, out, len) != 1)) { + goto done; + } + + for (i = 0; i < 16; i++) { + t.byte[i] ^= v[i]; + } + + result = t.word[0] | t.word[1]; + ct_unpoison(&result, sizeof result); + ret = !result; + + if(ret) { + ct_unpoison(out, len); + } else { + OPENSSL_cleanse(out, len); + } + +done: + ct_unpoison(c, len); + return ret; +} + +int AES_SIV_Encrypt(AES_SIV_CTX *ctx, unsigned char *out, size_t *out_len, + unsigned char const *key, size_t key_len, + unsigned char const *nonce, size_t nonce_len, + unsigned char const *plaintext, size_t plaintext_len, + unsigned char const *ad, size_t ad_len) { + if (UNLIKELY(*out_len < plaintext_len + 16)) { + return 0; + } + *out_len = plaintext_len + 16; + + if (UNLIKELY(AES_SIV_Init(ctx, key, key_len) != 1)) { + return 0; + } + if (UNLIKELY(AES_SIV_AssociateData(ctx, ad, ad_len) != 1)) { + return 0; + } + if (nonce != NULL && + UNLIKELY(AES_SIV_AssociateData(ctx, nonce, nonce_len) != 1)) { + return 0; + } + if (UNLIKELY(AES_SIV_EncryptFinal(ctx, out, out + 16, plaintext, + plaintext_len) != 1)) { + return 0; + } + + debug("IV || C", out, *out_len); + return 1; +} + +int AES_SIV_Decrypt(AES_SIV_CTX *ctx, unsigned char *out, size_t *out_len, + unsigned char const *key, size_t key_len, + unsigned char const *nonce, size_t nonce_len, + unsigned char const *ciphertext, size_t ciphertext_len, + unsigned char const *ad, size_t ad_len) { + if (UNLIKELY(ciphertext_len < 16)) { + return 0; + } + if (UNLIKELY(*out_len < ciphertext_len - 16)) { + return 0; + } + *out_len = ciphertext_len - 16; + + if (UNLIKELY(AES_SIV_Init(ctx, key, key_len) != 1)) { + return 0; + } + if (UNLIKELY(AES_SIV_AssociateData(ctx, ad, ad_len) != 1)) { + return 0; + } + if (nonce != NULL && + UNLIKELY(AES_SIV_AssociateData(ctx, nonce, nonce_len) != 1)) { + return 0; + } + if (UNLIKELY(AES_SIV_DecryptFinal(ctx, out, ciphertext, ciphertext + 16, + ciphertext_len - 16) != 1)) { + return 0; + } + debug("plaintext", out, *out_len); + return 1; +} diff --git a/aes_siv/aes_siv.h b/aes_siv/aes_siv.h new file mode 100644 index 0000000..f6b2caa --- /dev/null +++ b/aes_siv/aes_siv.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2017-2019 Akamai Technologies, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef AES_SIV_H_ +#define AES_SIV_H_ + +#include + +#define LIBAES_SIV_VERSION_MAJOR 1 +#define LIBAES_SIV_VERSION_MINOR 0 +#define LIBAES_SIV_VERSION_PATCH 1 + +#define LIBAES_SIV_VERSION ((LIBAES_SIV_VERSION_MAJOR << 16) + \ + (LIBAES_SIV_VERSION_MINOR << 8) + \ + LIBAES_SIV_VERSION_PATCH) + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AES_SIV_CTX_st AES_SIV_CTX; + +AES_SIV_CTX *AES_SIV_CTX_new(void); +int AES_SIV_CTX_copy(AES_SIV_CTX *dst, AES_SIV_CTX const *src); +void AES_SIV_CTX_cleanup(AES_SIV_CTX *ctx); +void AES_SIV_CTX_free(AES_SIV_CTX *ctx); + +int AES_SIV_Init(AES_SIV_CTX *ctx, unsigned char const *key, size_t key_len); +int AES_SIV_AssociateData(AES_SIV_CTX *ctx, unsigned char const *data, + size_t len); +int AES_SIV_EncryptFinal(AES_SIV_CTX *ctx, unsigned char *v_out, + unsigned char *c_out, unsigned char const *plaintext, + size_t len); +int AES_SIV_DecryptFinal(AES_SIV_CTX *ctx, unsigned char *out, + unsigned char const *v, unsigned char const *c, + size_t len); + +int AES_SIV_Encrypt(AES_SIV_CTX *ctx, unsigned char *out, size_t *out_len, + unsigned char const *key, size_t key_len, + unsigned char const *nonce, size_t nonce_len, + unsigned char const *plaintext, size_t plaintext_len, + unsigned char const *ad, size_t ad_len); + +int AES_SIV_Decrypt(AES_SIV_CTX *ctx, unsigned char *out, size_t *out_len, + unsigned char const *key, size_t key_len, + unsigned char const *nonce, size_t nonce_len, + unsigned char const *ciphertext, size_t ciphertext_len, + unsigned char const *ad, size_t ad_len); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/aes_siv/tests.c b/aes_siv/tests.c new file mode 100644 index 0000000..996cc58 --- /dev/null +++ b/aes_siv/tests.c @@ -0,0 +1,584 @@ +/* Copyright (c) 2017-2019 Akamai Technologies, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define _POSIX_C_SOURCE 200112L +#define _ISOC99_SOURCE 1 + +#include "config.h" +#include "aes_siv.h" + +#undef NDEBUG +#include +#include +#include +#include + +#include +#include +#include + +static void debug(const char *label, const unsigned char *hex, size_t len) { + size_t i; + printf("%16s: ", label); + for (i = 0; i < len; i++) { + if (i > 0 && i % 16 == 0) + printf("\n "); + printf("%.2x", (int)hex[i]); + if (i > 0 && i % 4 == 3) + printf(" "); + } + printf("\n"); +} + +static int fail_allocation_counter = -1; + +static void* mock_malloc(size_t num) { + if(fail_allocation_counter < 0) { + return malloc(num); + } + if(fail_allocation_counter-- == 0) { + return NULL; + } + return malloc(num); +} + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +static void* mock_malloc_ex(size_t num, const char *file, int line) { + (void)file; + (void)line; + return mock_malloc(num); +} + +static void *mock_realloc_ex(void *mem, size_t num, const char *file, int line) { + (void)file; + (void)line; + return realloc(mem, num); +} + +static void mock_free_ex(void *mem, const char* file, int line) { + (void)file; + (void)line; + free(mem); +} +#endif + +/* This needs to be the first test case because CRYPTO_set_mem_functions() + will fail once any allocations have happened. +*/ +static void test_malloc_failure(void) { + int ret, i=0; + AES_SIV_CTX *ctx; + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ret = CRYPTO_set_mem_functions(mock_malloc, realloc, free); +#else + ret = CRYPTO_set_mem_functions(mock_malloc_ex, mock_realloc_ex, mock_free_ex); +#endif + assert(ret == 1); + + printf("Test allocation failure cases:\n" ); + + do { + fail_allocation_counter = i++; + } while((ctx = AES_SIV_CTX_new()) == NULL); + assert(i > 1); + printf("AES_SIV_CTX_new() succeeds after %d successful allocations.\n", i-1); + AES_SIV_CTX_free(ctx); + fail_allocation_counter = -1; +} + +static void test_cleanup_before_free(void) { + printf("Test cleanup before free: "); + AES_SIV_CTX *ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + AES_SIV_CTX_cleanup(ctx); + AES_SIV_CTX_free(ctx); + printf("OK\n"); +} + +static void test_vector_1(void) { + const unsigned char key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + + const unsigned char ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + const unsigned char plaintext[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee}; + + const unsigned char ciphertext[] = { + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, + 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, + 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + }; + + unsigned char ciphertext_out[256]; + unsigned char plaintext_out[256]; + + size_t plaintext_len = sizeof plaintext_out; + size_t ciphertext_len = sizeof ciphertext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("Test vector 1:\n"); + debug("key", key, sizeof key); + debug("AD", ad, sizeof ad); + debug("plaintext", plaintext, sizeof plaintext); + debug("exp. ciphertext", ciphertext, sizeof ciphertext); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + printf("Encryption:\n"); + ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, + sizeof key, NULL, 0, plaintext, sizeof plaintext, + ad, sizeof ad); + assert(ret == 1); + assert(ciphertext_len == sizeof ciphertext); + assert(!memcmp(ciphertext, ciphertext_out, ciphertext_len)); + + printf("Decryption:\n"); + ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, + sizeof key, NULL, 0, ciphertext_out, + ciphertext_len, ad, sizeof ad); + assert(ret == 1); + assert(plaintext_len == sizeof plaintext); + assert(!memcmp(plaintext, plaintext_out, plaintext_len)); + AES_SIV_CTX_free(ctx); +} + +static void test_vector_2(void) { + const unsigned char key[] = { + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + + const unsigned char ad1[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 + }; + + const unsigned char ad2[] = { + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, + 0x90, 0xa0 + }; + + const unsigned char nonce[] = { + 0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b, + 0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0 + }; + + const unsigned char plaintext[] = { + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, + 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61, + 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74, + 0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, + 0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53 + }; + + const unsigned char ciphertext[] = { + 0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb, + 0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f, + 0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43, + 0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17, + 0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63, + 0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29, + 0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c, + 0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d + }; + + unsigned char ciphertext_out[256]; + unsigned char plaintext_out[256]; + + AES_SIV_CTX *ctx; + int ret; + + printf("Test vector 2:\n"); + debug("key", key, sizeof key); + debug("AD1", ad1, sizeof ad1); + debug("AD2", ad2, sizeof ad2); + debug("nonce", nonce, sizeof nonce); + debug("plaintext", plaintext, sizeof plaintext); + debug("exp. ciphertext", ciphertext, sizeof ciphertext); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + printf("Encryption:\n"); + ret = AES_SIV_Init(ctx, key, sizeof key); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, ad1, sizeof ad1); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, ad2, sizeof ad2); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, nonce, sizeof nonce); + assert(ret == 1); + ret = AES_SIV_EncryptFinal(ctx, ciphertext_out, ciphertext_out + 16, + plaintext, sizeof plaintext); + assert(ret == 1); + debug("IV || C", ciphertext_out, sizeof plaintext + 16); + assert(!memcmp(ciphertext_out, ciphertext, sizeof ciphertext)); + + printf("Decryption:\n"); + ret = AES_SIV_Init(ctx, key, sizeof key); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, ad1, sizeof ad1); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, ad2, sizeof ad2); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx, nonce, sizeof nonce); + assert(ret == 1); + ret = AES_SIV_DecryptFinal(ctx, plaintext_out, ciphertext_out, + ciphertext_out + 16, sizeof plaintext); + assert(ret == 1); + debug("plaintext", plaintext_out, sizeof plaintext); + assert(!memcmp(plaintext_out, plaintext, sizeof plaintext)); + AES_SIV_CTX_free(ctx); +} + +static void test_384bit(void) { + const unsigned char key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + }; + + const unsigned char ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + const unsigned char plaintext[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + }; + + const unsigned char ciphertext[] = { + 0x89, 0xe8, 0x69, 0xb9, 0x32, 0x56, 0x78, 0x51, + 0x54, 0xf0, 0x96, 0x39, 0x62, 0xfe, 0x07, 0x40, + 0xef, 0xf3, 0x56, 0xe4, 0x2d, 0xec, 0x1f, 0x4f, + 0xeb, 0xde, 0xd3, 0x66, 0x42, 0xf2 + }; + + unsigned char ciphertext_out[256]; + unsigned char plaintext_out[256]; + + size_t plaintext_len = sizeof plaintext_out; + size_t ciphertext_len = sizeof ciphertext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("384-bit key test:\n"); + debug("key", key, sizeof key); + debug("AD", ad, sizeof ad); + debug("plaintext", plaintext, sizeof plaintext); + debug("exp. ciphertext", ciphertext, sizeof ciphertext); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + printf("Encryption:\n"); + ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, + sizeof key, NULL, 0, plaintext, sizeof plaintext, + ad, sizeof ad); + assert(ret == 1); + assert(ciphertext_len == sizeof ciphertext); + assert(!memcmp(ciphertext, ciphertext_out, ciphertext_len)); + + printf("Decryption:\n"); + ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, + sizeof key, NULL, 0, ciphertext_out, + ciphertext_len, ad, sizeof ad); + assert(ret == 1); + assert(plaintext_len == sizeof plaintext); + assert(!memcmp(plaintext, plaintext_out, plaintext_len)); + AES_SIV_CTX_free(ctx); +} + +static void test_512bit(void) { + const unsigned char key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0 + }; + + const unsigned char ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + const unsigned char plaintext[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + }; + + const unsigned char ciphertext[] = { + 0x72, 0x4d, 0xfb, 0x2e, 0xaf, 0x94, 0xdb, 0xb1, + 0x9b, 0x0b, 0xa3, 0xa2, 0x99, 0xa0, 0x80, 0x1e, + 0xf3, 0xb0, 0x5a, 0x55, 0x49, 0x8e, 0xc2, 0x55, + 0x26, 0x90, 0xb8, 0x98, 0x10, 0xe4 + }; + + unsigned char ciphertext_out[256]; + unsigned char plaintext_out[256]; + + size_t plaintext_len = sizeof plaintext_out; + size_t ciphertext_len = sizeof ciphertext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("512-bit key test:\n"); + debug("key", key, sizeof key); + debug("AD", ad, sizeof ad); + debug("plaintext", plaintext, sizeof plaintext); + debug("exp. ciphertext", ciphertext, sizeof ciphertext); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + printf("Encryption:\n"); + ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, + sizeof key, NULL, 0, plaintext, sizeof plaintext, + ad, sizeof ad); + assert(ret == 1); + assert(ciphertext_len == sizeof ciphertext); + assert(!memcmp(ciphertext, ciphertext_out, ciphertext_len)); + + printf("Decryption:\n"); + ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, + sizeof key, NULL, 0, ciphertext_out, + ciphertext_len, ad, sizeof ad); + assert(ret == 1); + assert(plaintext_len == sizeof plaintext); + assert(!memcmp(plaintext, plaintext_out, plaintext_len)); + AES_SIV_CTX_free(ctx); +} + +static void test_highlevel_with_nonce(void) { + const unsigned char key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + + const unsigned char ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + const unsigned char nonce[] = { + 0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b, + 0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0 + }; + + const unsigned char plaintext[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + }; + + unsigned char ciphertext_out[256]; + unsigned char plaintext_out[256]; + + size_t plaintext_len = sizeof plaintext_out; + size_t ciphertext_len = sizeof ciphertext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("Test high-level interface with non-NULL nonce:\n"); + debug("key", key, sizeof key); + debug("AD", ad, sizeof ad); + debug("nonce", nonce, sizeof nonce); + debug("plaintext", plaintext, sizeof plaintext); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + printf("Encryption:\n"); + ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, + sizeof key, nonce, sizeof nonce, plaintext, + sizeof plaintext, ad, sizeof ad); + assert(ret == 1); + + printf("Decryption:\n"); + ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, + sizeof key, nonce, sizeof nonce, ciphertext_out, + ciphertext_len, ad, sizeof ad); + assert(ret == 1); + assert(plaintext_len == sizeof plaintext); + assert(!memcmp(plaintext, plaintext_out, plaintext_len)); + AES_SIV_CTX_free(ctx); +} + +static void test_copy(void) { + const unsigned char key[] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + + const unsigned char ad[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 + }; + + const unsigned char plaintext1[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee + }; + + const unsigned char plaintext2[] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, + 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xef + }; + + const unsigned char ciphertext[] = { + 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f, + 0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93, + 0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04, + 0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c + }; + + unsigned char ciphertext1_out[256], ciphertext2_out[256]; + + AES_SIV_CTX *ctx1, *ctx2, *ctx3; + int ret; + + ctx1 = AES_SIV_CTX_new(); + assert(ctx1 != NULL); + ctx2 = AES_SIV_CTX_new(); + assert(ctx2 != NULL); + ctx3 = AES_SIV_CTX_new(); + assert(ctx3 != NULL); + + ret = AES_SIV_Init(ctx1, key, sizeof key); + assert(ret == 1); + ret = AES_SIV_CTX_copy(ctx2, ctx1); + assert(ret == 1); + + ret = AES_SIV_AssociateData(ctx1, ad, sizeof ad); + assert(ret == 1); + ret = AES_SIV_AssociateData(ctx2, ad, sizeof ad); + assert(ret == 1); + + ret = AES_SIV_CTX_copy(ctx3, ctx1); + assert(ret == 1); + + ret = AES_SIV_EncryptFinal(ctx1, ciphertext1_out, ciphertext1_out + 16, + plaintext1, sizeof plaintext1); + assert(ret == 1); + assert(!memcmp(ciphertext, ciphertext1_out, sizeof ciphertext)); + + ret = AES_SIV_EncryptFinal(ctx2, ciphertext2_out, ciphertext2_out + 16, + plaintext1, sizeof plaintext1); + assert(ret == 1); + assert(!memcmp(ciphertext, ciphertext2_out, sizeof ciphertext)); + + ret = AES_SIV_EncryptFinal(ctx3, ciphertext2_out, ciphertext2_out + 16, + plaintext2, sizeof plaintext2); + assert(ret == 1); + assert(memcmp(ciphertext, ciphertext2_out, sizeof ciphertext)); + + AES_SIV_CTX_free(ctx1); + AES_SIV_CTX_free(ctx2); + AES_SIV_CTX_free(ctx3); +} + +static void test_bad_key(void) { + static const unsigned char key[40]; + static const unsigned char ad[16]; + static const unsigned char plaintext[16]; + + unsigned char ciphertext_out[256]; + size_t ciphertext_len = sizeof ciphertext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("Test bad key size: "); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, + sizeof key, NULL, 0, plaintext, sizeof plaintext, + ad, sizeof ad); + assert(ret == 0); + + ret = AES_SIV_Init(ctx, key, sizeof key); + assert(ret == 0); + + AES_SIV_CTX_free(ctx); + printf("OK\n"); +} + +static void test_decrypt_failure(void) { + static const unsigned char key[32]; + static const unsigned char ad[16]; + static const unsigned char ciphertext[32]; + + unsigned char plaintext_out[256]; + size_t plaintext_len = sizeof plaintext_out; + + AES_SIV_CTX *ctx; + int ret; + + printf("Test decryption failure:\n"); + + ctx = AES_SIV_CTX_new(); + assert(ctx != NULL); + + ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, + sizeof key, NULL, 0, ciphertext, + sizeof ciphertext, ad, sizeof ad); + assert(ret == 0); + + AES_SIV_CTX_free(ctx); +} + +int main(void) { + test_malloc_failure(); + test_cleanup_before_free(); + test_vector_1(); + test_vector_2(); + test_384bit(); + test_512bit(); + test_highlevel_with_nonce(); + test_copy(); + test_bad_key(); + test_decrypt_failure(); + return 0; +} diff --git a/configure.ac b/configure.ac index ea57a7c..5520c90 100644 --- a/configure.ac +++ b/configure.ac @@ -88,12 +88,6 @@ AS_IF([test "$enable_shared" == "yes"], LIBS=$save_LIBS CFLAGS=$save_CFLAGS]) -# Checks for non pkg-config libraries -AC_CHECK_LIB([aes_siv], [AES_SIV_CTX_new], - [AC_SUBST([LIBAES_SIV_LIBS], "-laes_siv")], - [AC_MSG_ERROR([libaes_siv is required.])], - [-lcrypto]) - # Checks for header files. AC_CHECK_HEADERS([limits.h stddef.h stdint.h stdlib.h string.h]) diff --git a/liburcrypt.pc.in b/liburcrypt.pc.in index 9581b4f..73b82a9 100644 --- a/liburcrypt.pc.in +++ b/liburcrypt.pc.in @@ -10,4 +10,3 @@ Version: @PACKAGE_VERSION@ Cflags: -I${includedir} Requires.private: libcrypto libsecp256k1 Libs: -L${libdir} -lurcrypt -Libs.private: -laes_siv From 75706c9475215e794cb9f6d2fd24f9eb491f14d9 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 29 May 2026 10:49:34 -0400 Subject: [PATCH 2/3] replaces openssl with nettle (including in vendored libaes-siv) --- Makefile.am | 8 +- README.md | 20 ++-- aes_siv/aes_siv.c | 251 +++++++++++++++------------------------------- aes_siv/tests.c | 63 ------------ configure.ac | 23 +---- liburcrypt.pc.in | 2 +- urcrypt/aes_cbc.c | 91 +++++++---------- urcrypt/aes_ecb.c | 86 ++++++---------- urcrypt/ripemd.c | 20 ++-- urcrypt/sha.c | 18 +++- 10 files changed, 187 insertions(+), 395 deletions(-) diff --git a/Makefile.am b/Makefile.am index d64bb40..1966cc9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,7 +39,7 @@ liburcrypt_la_CPPFLAGS = -I$(srcdir)/aes_siv \ -I$(srcdir)/monocypher \ -I$(srcdir)/keccak-tiny \ -I$(srcdir)/scrypt -liburcrypt_la_LIBADD = $(LIBCRYPTO_LIBS) \ +liburcrypt_la_LIBADD = $(NETTLE_LIBS) \ $(LIBSECP256K1_LIBS) \ libaes_siv.la \ libed25519.la \ @@ -49,7 +49,7 @@ liburcrypt_la_LIBADD = $(LIBCRYPTO_LIBS) \ libmonocypher.la \ libkeccak_tiny.la \ libscrypt.la -liburcrypt_la_CFLAGS = $(LIBCRYPTO_CFLAGS) \ +liburcrypt_la_CFLAGS = $(NETTLE_CFLAGS) \ $(LIBSECP256K1_CFLAGS) # urcrypt_ is used for public symbols, urcrypt__ for internal. liburcrypt_la_LDFLAGS = -export-symbols-regex '^urcrypt_[^_]' \ @@ -70,9 +70,9 @@ liburcrypt_la_SOURCES = urcrypt/aes_cbc.c \ urcrypt/util.c \ urcrypt/util.h -# aes-siv (RFC 5297), vendored from https://github.com/dfoxfranke/libaes_siv +# aes-siv (RFC 5297), retargeted from libaes_siv onto nettle libaes_siv_la_CPPFLAGS = -I$(srcdir)/aes_siv -libaes_siv_la_CFLAGS = $(LIBCRYPTO_CFLAGS) +libaes_siv_la_CFLAGS = $(NETTLE_CFLAGS) libaes_siv_la_SOURCES = aes_siv/aes_siv.c \ aes_siv/aes_siv.h diff --git a/README.md b/README.md index 1b9008c..99240c0 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,19 @@ urcrypt: * Some property of the routine is cryptographically useful (SHA, RIPE, etc) * The routine typically lives in a crypto library, for whatever reason. -A word on OpenSSL ------------------ -Urcrypt depends on OpenSSL's libcrypto, which has global state. In order -to avoid dealing with this state, urcrypt refuses to build with an internal -libcrypto. Either build statically (pass `--disable-shared` to `./configure`) -or provide a shared libcrypto for urcrypt to link against. It is the library -user's responsibility to initialize openssl, set custom memory functions, etc. +A word on dependencies +---------------------- +Urcrypt depends on [GNU Nettle](https://www.lysator.liu.se/~nisse/nettle/) +(libnettle) for its SHA, RIPEMD, and AES (ECB, CBC, and SIV) primitives. +Unlike OpenSSL's libcrypto, Nettle keeps no global state, so there is no need +to initialize the library, register custom memory functions, or arrange for a +shared object — urcrypt may be built statically or shared without restriction. + +AES-SIV (RFC 5297) is provided by a vendored copy of +[libaes_siv](https://github.com/dfoxfranke/libaes_siv) under `aes_siv/`, with +its OpenSSL primitives retargeted onto Nettle's `cmac128`, `ctr`, and `aes`. +It preserves the full RFC 5297 interface (256/384/512-bit keys and a vector of +associated-data blocks) and passes the upstream RFC 5297 test vectors. Installation ------------ diff --git a/aes_siv/aes_siv.c b/aes_siv/aes_siv.c index 00a510d..dac362a 100644 --- a/aes_siv/aes_siv.c +++ b/aes_siv/aes_siv.c @@ -1,5 +1,9 @@ /* Copyright (c) 2017-2019 Akamai Technologies, Inc. * SPDX-License-Identifier: Apache-2.0 + * + * Retargeted from OpenSSL's libcrypto onto GNU Nettle for urcrypt: the + * RFC 5297 S2V/CTR construction is unchanged; only the AES, CMAC and CTR + * primitives now come from Nettle (aes, cmac128, ctr) instead of EVP/CMAC. */ #define _POSIX_C_SOURCE 200112L @@ -21,9 +25,9 @@ #endif #include -#include -#include -#include +#include +#include +#include #ifdef ENABLE_CTGRIND #include @@ -66,6 +70,15 @@ static inline void ct_unpoison(const void *data, size_t len) { } #endif +/* Securely zero memory without being optimized away. Nettle provides no + equivalent of OPENSSL_cleanse(). */ +static void cleanse(void *p, size_t len) { + volatile unsigned char *v = (volatile unsigned char *)p; + while (len--) { + *v++ = 0; + } +} + static void debug(const char *label, const unsigned char *hex, size_t len) { /* ENABLE_CTGRIND has to override ENABLE_DEBUG_OUTPUT since sensitive data gets printed. @@ -203,146 +216,85 @@ static inline void dbl(block *block) { putword(block, 1, low); } +/* AES key schedule for whichever variant is in use. The same union type backs + both the CMAC half and the CTR half of the SIV key. */ +union aes_ctx { + struct aes128_ctx a128; + struct aes192_ctx a192; + struct aes256_ctx a256; +}; + struct AES_SIV_CTX_st { /* d stores intermediate results of S2V; it corresponds to D from the pseudocode in section 2.4 of RFC 5297. */ block d; - EVP_CIPHER_CTX *cipher_ctx; - /* SIV_AES_Init() sets up cmac_ctx_init. cmac_ctx is a scratchpad used - by SIV_AES_AssociateData() and SIV_AES_(En|De)cryptFinal. */ - CMAC_CTX *cmac_ctx_init, *cmac_ctx; + /* cmac_cipher and ctr_cipher hold the AES key schedules for the S2V + (CMAC) and CTR halves of the SIV key; encrypt is the matching Nettle + block-cipher function. cmac_key holds the CMAC subkeys derived from + cmac_cipher, and cmac_ctx is a scratchpad used by + AES_SIV_AssociateData() and AES_SIV_(En|De)cryptFinal. */ + union aes_ctx cmac_cipher, ctr_cipher; + nettle_cipher_func *encrypt; + struct cmac128_key cmac_key; + struct cmac128_ctx cmac_ctx; }; void AES_SIV_CTX_cleanup(AES_SIV_CTX *ctx) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - EVP_CIPHER_CTX_reset(ctx->cipher_ctx); -#else - EVP_CIPHER_CTX_cleanup(ctx->cipher_ctx); -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && OPENSSL_VERSION_NUMBER <= 0x10100060L - /* Workaround for an OpenSSL bug that causes a double free - if you call CMAC_CTX_cleanup() before CMAC_CTX_free(). - https://github.com/openssl/openssl/pull/2798 - */ - CMAC_CTX_free(ctx->cmac_ctx_init); - ctx->cmac_ctx_init = CMAC_CTX_new(); - CMAC_CTX_free(ctx->cmac_ctx); - ctx->cmac_ctx = CMAC_CTX_new(); -#else - CMAC_CTX_cleanup(ctx->cmac_ctx_init); - CMAC_CTX_cleanup(ctx->cmac_ctx); -#endif - OPENSSL_cleanse(&ctx->d, sizeof ctx->d); + cleanse(ctx, sizeof *ctx); } void AES_SIV_CTX_free(AES_SIV_CTX *ctx) { if (ctx) { - EVP_CIPHER_CTX_free(ctx->cipher_ctx); - /* Prior to OpenSSL 1.0.2b, CMAC_CTX_free() crashes on NULL */ - if (LIKELY(ctx->cmac_ctx_init != NULL)) { - CMAC_CTX_free(ctx->cmac_ctx_init); - } - if (LIKELY(ctx->cmac_ctx != NULL)) { - CMAC_CTX_free(ctx->cmac_ctx); - } - OPENSSL_cleanse(&ctx->d, sizeof ctx->d); - OPENSSL_free(ctx); + cleanse(ctx, sizeof *ctx); + free(ctx); } } AES_SIV_CTX *AES_SIV_CTX_new(void) { - AES_SIV_CTX *ctx = OPENSSL_malloc(sizeof(struct AES_SIV_CTX_st)); - if (UNLIKELY(ctx == NULL)) { - return NULL; - } - - ctx->cipher_ctx = EVP_CIPHER_CTX_new(); - ctx->cmac_ctx_init = CMAC_CTX_new(); - ctx->cmac_ctx = CMAC_CTX_new(); - - if (UNLIKELY(ctx->cipher_ctx == NULL || - ctx->cmac_ctx_init == NULL || - ctx->cmac_ctx == NULL)) { - AES_SIV_CTX_free(ctx); - return NULL; - } - - return ctx; + return malloc(sizeof(struct AES_SIV_CTX_st)); } int AES_SIV_CTX_copy(AES_SIV_CTX *dst, AES_SIV_CTX const *src) { - memcpy(&dst->d, &src->d, sizeof src->d); - if(UNLIKELY(EVP_CIPHER_CTX_copy(dst->cipher_ctx, src->cipher_ctx) - != 1)) { - return 0; - } - if (UNLIKELY(CMAC_CTX_copy(dst->cmac_ctx_init, src->cmac_ctx_init) - != 1)) { - return 0; - } - /* Not necessary to copy cmac_ctx since it's just temporary - * storage */ + /* The context is self-contained (no external pointers), so a flat copy + reproduces the key schedules, subkeys and S2V state. */ + memcpy(dst, src, sizeof *dst); return 1; } int AES_SIV_Init(AES_SIV_CTX *ctx, unsigned char const *key, size_t key_len) { static const unsigned char zero[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - size_t out_len; int ret = 0; ct_poison(key, key_len); switch (key_len) { case 32: - if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 16, - EVP_aes_128_cbc(), NULL) != 1)) { - goto done; - } - if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, - EVP_aes_128_ctr(), - NULL, key + 16, NULL) != 1)) { - goto done; - } + aes128_set_encrypt_key(&ctx->cmac_cipher.a128, key); + aes128_set_encrypt_key(&ctx->ctr_cipher.a128, key + 16); + ctx->encrypt = (nettle_cipher_func *)aes128_encrypt; break; case 48: - if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 24, - EVP_aes_192_cbc(), NULL) != 1)) { - goto done; - } - if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, - EVP_aes_192_ctr(), - NULL, key + 24, NULL) != 1)) { - goto done; - } + aes192_set_encrypt_key(&ctx->cmac_cipher.a192, key); + aes192_set_encrypt_key(&ctx->ctr_cipher.a192, key + 24); + ctx->encrypt = (nettle_cipher_func *)aes192_encrypt; break; case 64: - if (UNLIKELY(CMAC_Init(ctx->cmac_ctx_init, key, 32, - EVP_aes_256_cbc(), NULL) != 1)) { - goto done; - } - if (UNLIKELY(EVP_EncryptInit_ex(ctx->cipher_ctx, - EVP_aes_256_ctr(), - NULL, key + 32, NULL) != 1)) { - goto done; - } + aes256_set_encrypt_key(&ctx->cmac_cipher.a256, key); + aes256_set_encrypt_key(&ctx->ctr_cipher.a256, key + 32); + ctx->encrypt = (nettle_cipher_func *)aes256_encrypt; break; default: goto done; } - if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { - goto done; - } - if (UNLIKELY(CMAC_Update(ctx->cmac_ctx, zero, sizeof zero) != 1)) { - goto done; - } - out_len = sizeof ctx->d; - if (UNLIKELY(CMAC_Final(ctx->cmac_ctx, ctx->d.byte, &out_len) != 1)) { - goto done; - } - debug("CMAC(zero)", ctx->d.byte, out_len); + cmac128_set_key(&ctx->cmac_key, &ctx->cmac_cipher, ctx->encrypt); + cmac128_init(&ctx->cmac_ctx); + cmac128_update(&ctx->cmac_ctx, &ctx->cmac_cipher, ctx->encrypt, + sizeof zero, zero); + cmac128_digest(&ctx->cmac_ctx, &ctx->cmac_key, &ctx->cmac_cipher, + ctx->encrypt, ctx->d.byte); + debug("CMAC(zero)", ctx->d.byte, 16); ret = 1; done: @@ -353,55 +305,41 @@ int AES_SIV_Init(AES_SIV_CTX *ctx, unsigned char const *key, size_t key_len) { int AES_SIV_AssociateData(AES_SIV_CTX *ctx, unsigned char const *data, size_t len) { block cmac_out; - size_t out_len = sizeof cmac_out; - int ret = 0; ct_poison(data, len); dbl(&ctx->d); debug("double()", ctx->d.byte, 16); - if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { - goto done; - } - if (UNLIKELY(CMAC_Update(ctx->cmac_ctx, data, len) != 1)) { - goto done; - } - if (UNLIKELY(CMAC_Final(ctx->cmac_ctx, cmac_out.byte, &out_len) != 1)) { - goto done; - } - assert(out_len == 16); + cmac128_init(&ctx->cmac_ctx); + cmac128_update(&ctx->cmac_ctx, &ctx->cmac_cipher, ctx->encrypt, len, + data); + cmac128_digest(&ctx->cmac_ctx, &ctx->cmac_key, &ctx->cmac_cipher, + ctx->encrypt, cmac_out.byte); debug("CMAC(ad)", cmac_out.byte, 16); xorblock(&ctx->d, &cmac_out); debug("xor", ctx->d.byte, 16); - ret = 1; -done: ct_unpoison(data, len); - return ret; + return 1; } static inline int do_s2v_p(AES_SIV_CTX *ctx, block *out, unsigned char const* in, size_t len) { block t; - size_t out_len = sizeof out->byte; - if (UNLIKELY(CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) != 1)) { - return 0; - } + cmac128_init(&ctx->cmac_ctx); if(len >= 16) { - if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, in, len - 16) != 1)) { - return 0; - } + cmac128_update(&ctx->cmac_ctx, &ctx->cmac_cipher, ctx->encrypt, + len - 16, in); debug("xorend part 1", in, len - 16); memcpy(&t, in + (len-16), 16); xorblock(&t, &ctx->d); debug("xorend part 2", t.byte, 16); - if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, t.byte, 16) != 1)) { - return 0; - } + cmac128_update(&ctx->cmac_ctx, &ctx->cmac_cipher, ctx->encrypt, + 16, t.byte); } else { size_t i; memcpy(&t, in, len); @@ -413,50 +351,22 @@ static inline int do_s2v_p(AES_SIV_CTX *ctx, block *out, dbl(&ctx->d); xorblock(&t, &ctx->d); debug("xor", t.byte, 16); - if(UNLIKELY(CMAC_Update(ctx->cmac_ctx, t.byte, 16) != 1)) { - return 0; - } - } - if(UNLIKELY(CMAC_Final(ctx->cmac_ctx, out->byte, &out_len) != 1)) { - return 0; + cmac128_update(&ctx->cmac_ctx, &ctx->cmac_cipher, ctx->encrypt, + 16, t.byte); } - assert(out_len == 16); + cmac128_digest(&ctx->cmac_ctx, &ctx->cmac_key, &ctx->cmac_cipher, + ctx->encrypt, out->byte); debug("CMAC(final)", out->byte, 16); return 1; } -static inline int do_encrypt(EVP_CIPHER_CTX *ctx, unsigned char *out, +static inline int do_encrypt(AES_SIV_CTX *ctx, unsigned char *out, unsigned char const *in, size_t len, block *icv) { -#ifdef ENABLE_DEBUG_TINY_CHUNK_SIZE - const int chunk_size = 7; -#else - const int chunk_size = 1 << 30; -#endif - size_t len_remaining = len; - int out_len; - int ret; - - if(UNLIKELY(EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, icv->byte) - != 1)) { - return 0; - } - - while(UNLIKELY(len_remaining > (size_t)chunk_size)) { - out_len = chunk_size; - if(UNLIKELY(EVP_EncryptUpdate(ctx, out, &out_len, in, out_len) - != 1)) { - return 0; - } - assert(out_len == chunk_size); - out += out_len; - in += out_len; - len_remaining -= (size_t)out_len; - } - - out_len = (int)len_remaining; - ret = EVP_EncryptUpdate(ctx, out, &out_len, in, out_len); - assert(!ret || out_len == (int)len_remaining); - return ret; + /* Nettle's ctr_crypt() takes a size_t length and mutates the counter in + place, so the OpenSSL int-length chunking loop is unnecessary. icv is + the caller's scratch copy of the synthetic IV. */ + ctr_crypt(&ctx->ctr_cipher, ctx->encrypt, 16, icv->byte, len, out, in); + return 1; } int AES_SIV_EncryptFinal(AES_SIV_CTX *ctx, unsigned char *v_out, @@ -476,8 +386,7 @@ int AES_SIV_EncryptFinal(AES_SIV_CTX *ctx, unsigned char *v_out, q.byte[8] &= 0x7f; q.byte[12] &= 0x7f; - if(UNLIKELY(do_encrypt(ctx->cipher_ctx, c_out, plaintext, len, &q) - != 1)) { + if(UNLIKELY(do_encrypt(ctx, c_out, plaintext, len, &q) != 1)) { goto done; } @@ -505,7 +414,7 @@ int AES_SIV_DecryptFinal(AES_SIV_CTX *ctx, unsigned char *out, q.byte[8] &= 0x7f; q.byte[12] &= 0x7f; - if(UNLIKELY(do_encrypt(ctx->cipher_ctx, out, c, len, &q) != 1)) { + if(UNLIKELY(do_encrypt(ctx, out, c, len, &q) != 1)) { goto done; } debug("plaintext", out, len); @@ -525,7 +434,7 @@ int AES_SIV_DecryptFinal(AES_SIV_CTX *ctx, unsigned char *out, if(ret) { ct_unpoison(out, len); } else { - OPENSSL_cleanse(out, len); + cleanse(out, len); } done: diff --git a/aes_siv/tests.c b/aes_siv/tests.c index 996cc58..fdc5426 100644 --- a/aes_siv/tests.c +++ b/aes_siv/tests.c @@ -5,7 +5,6 @@ #define _POSIX_C_SOURCE 200112L #define _ISOC99_SOURCE 1 -#include "config.h" #include "aes_siv.h" #undef NDEBUG @@ -14,10 +13,6 @@ #include #include -#include -#include -#include - static void debug(const char *label, const unsigned char *hex, size_t len) { size_t i; printf("%16s: ", label); @@ -31,63 +26,6 @@ static void debug(const char *label, const unsigned char *hex, size_t len) { printf("\n"); } -static int fail_allocation_counter = -1; - -static void* mock_malloc(size_t num) { - if(fail_allocation_counter < 0) { - return malloc(num); - } - if(fail_allocation_counter-- == 0) { - return NULL; - } - return malloc(num); -} - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -static void* mock_malloc_ex(size_t num, const char *file, int line) { - (void)file; - (void)line; - return mock_malloc(num); -} - -static void *mock_realloc_ex(void *mem, size_t num, const char *file, int line) { - (void)file; - (void)line; - return realloc(mem, num); -} - -static void mock_free_ex(void *mem, const char* file, int line) { - (void)file; - (void)line; - free(mem); -} -#endif - -/* This needs to be the first test case because CRYPTO_set_mem_functions() - will fail once any allocations have happened. -*/ -static void test_malloc_failure(void) { - int ret, i=0; - AES_SIV_CTX *ctx; - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - ret = CRYPTO_set_mem_functions(mock_malloc, realloc, free); -#else - ret = CRYPTO_set_mem_functions(mock_malloc_ex, mock_realloc_ex, mock_free_ex); -#endif - assert(ret == 1); - - printf("Test allocation failure cases:\n" ); - - do { - fail_allocation_counter = i++; - } while((ctx = AES_SIV_CTX_new()) == NULL); - assert(i > 1); - printf("AES_SIV_CTX_new() succeeds after %d successful allocations.\n", i-1); - AES_SIV_CTX_free(ctx); - fail_allocation_counter = -1; -} - static void test_cleanup_before_free(void) { printf("Test cleanup before free: "); AES_SIV_CTX *ctx = AES_SIV_CTX_new(); @@ -570,7 +508,6 @@ static void test_decrypt_failure(void) { } int main(void) { - test_malloc_failure(); test_cleanup_before_free(); test_vector_1(); test_vector_2(); diff --git a/configure.ac b/configure.ac index 5520c90..2799a55 100644 --- a/configure.ac +++ b/configure.ac @@ -66,27 +66,8 @@ AC_CHECK_HEADER([secp256k1_recovery.h], [], AC_CHECK_HEADER([secp256k1_schnorrsig.h], [], [AC_MSG_ERROR([libsecp256k1 must have Schnorr signatures enabled.])]) CPPFLAGS=$save_CPPFLAGS -PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto]) - -AS_IF([test "$enable_shared" == "yes"], - [# ensure crypto will be shared for shared object (see README.md) - save_LIBS=$LIBS - save_CFLAGS=$CFLAGS - LIBS="$LIBCRYPTO_LIBS $LIBS" - CFLAGS="$LIBCRYPTO_CFLAGS $CFLAGS" - AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], - [[unsigned char sha[32]; - SHA256("hello", 5, sha);]])], - [AC_PROG_GREP - AC_CHECK_TOOL([NM], [nm]) - AC_MSG_CHECKING([for shared libcrypto]) - AS_IF( - [$NM conftest$EXEEXT | $GREP 'U .*SHA256' 2>&1 >/dev/null], - [AC_MSG_RESULT([yes])], - [AC_MSG_ERROR([cannot find shared object for libcrypto.])])], - [AC_MSG_ERROR([unable to link libcrypto.])]) - LIBS=$save_LIBS - CFLAGS=$save_CFLAGS]) +# nettle >= 4.0 is required for the 2-argument *_digest() interface. +PKG_CHECK_MODULES([NETTLE], [nettle >= 4.0]) # Checks for header files. AC_CHECK_HEADERS([limits.h stddef.h stdint.h stdlib.h string.h]) diff --git a/liburcrypt.pc.in b/liburcrypt.pc.in index 73b82a9..1a9a61d 100644 --- a/liburcrypt.pc.in +++ b/liburcrypt.pc.in @@ -8,5 +8,5 @@ Description: cryptography library for urbit URL: https://github.com/urbit/urcrypt Version: @PACKAGE_VERSION@ Cflags: -I${includedir} -Requires.private: libcrypto libsecp256k1 +Requires.private: nettle libsecp256k1 Libs: -L${libdir} -lurcrypt diff --git a/urcrypt/aes_cbc.c b/urcrypt/aes_cbc.c index f163bcc..23b1b23 100644 --- a/urcrypt/aes_cbc.c +++ b/urcrypt/aes_cbc.c @@ -1,7 +1,8 @@ #include "urcrypt.h" #include "util.h" #include -#include +#include +#include static int urcrypt__cbc_pad(uint8_t **message_ptr, @@ -41,7 +42,8 @@ urcrypt__cbc_pad(uint8_t **message_ptr, static int urcrypt__cbc_help(uint8_t **message_ptr, size_t *length_ptr, - const AES_KEY *key, + const void *ctx, + nettle_cipher_func *f, uint8_t ivec[16], const int enc, urcrypt_realloc_t realloc_ptr) @@ -54,7 +56,12 @@ urcrypt__cbc_help(uint8_t **message_ptr, size_t length = *length_ptr; urcrypt__reverse(16, ivec); urcrypt__reverse(length, out); - AES_cbc_encrypt(out, out, length, key, ivec, enc); + if ( enc ) { + cbc_encrypt(ctx, f, 16, ivec, length, out, out); + } + else { + cbc_decrypt(ctx, f, 16, ivec, length, out, out); + } urcrypt__reverse(length, out); return 0; } @@ -67,17 +74,12 @@ urcrypt_aes_cbca_en(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes128_ctx ctx; urcrypt__reverse(16, key); - - if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } + aes128_set_encrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes128_encrypt, ivec, 1, realloc_ptr); } int @@ -87,17 +89,12 @@ urcrypt_aes_cbca_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes128_ctx ctx; urcrypt__reverse(16, key); - - if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } + aes128_set_decrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes128_decrypt, ivec, 0, realloc_ptr); } int @@ -107,17 +104,12 @@ urcrypt_aes_cbcb_en(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes192_ctx ctx; urcrypt__reverse(24, key); - - if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } + aes192_set_encrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes192_encrypt, ivec, 1, realloc_ptr); } int @@ -127,17 +119,12 @@ urcrypt_aes_cbcb_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes192_ctx ctx; urcrypt__reverse(24, key); - - if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } + aes192_set_decrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes192_decrypt, ivec, 0, realloc_ptr); } int @@ -147,17 +134,12 @@ urcrypt_aes_cbcc_en(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes256_ctx ctx; urcrypt__reverse(32, key); - - if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_ENCRYPT, realloc_ptr); - } + aes256_set_encrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes256_encrypt, ivec, 1, realloc_ptr); } int @@ -167,15 +149,10 @@ urcrypt_aes_cbcc_de(uint8_t **message_ptr, uint8_t ivec[16], urcrypt_realloc_t realloc_ptr) { - AES_KEY aes_key; + struct aes256_ctx ctx; urcrypt__reverse(32, key); - - if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - return urcrypt__cbc_help(message_ptr, length_ptr, - &aes_key, ivec, AES_DECRYPT, realloc_ptr); - } + aes256_set_decrypt_key(&ctx, key); + return urcrypt__cbc_help(message_ptr, length_ptr, &ctx, + (nettle_cipher_func *)aes256_decrypt, ivec, 0, realloc_ptr); } diff --git a/urcrypt/aes_ecb.c b/urcrypt/aes_ecb.c index 9b39100..2ddd4f2 100644 --- a/urcrypt/aes_ecb.c +++ b/urcrypt/aes_ecb.c @@ -1,111 +1,87 @@ #include "urcrypt.h" #include "util.h" -#include +#include int urcrypt_aes_ecba_en(uint8_t key[16], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes128_ctx ctx; urcrypt__reverse(16, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_encrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes128_set_encrypt_key(&ctx, key); + aes128_encrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } int urcrypt_aes_ecba_de(uint8_t key[16], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes128_ctx ctx; urcrypt__reverse(16, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_decrypt_key(key, 128, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes128_set_decrypt_key(&ctx, key); + aes128_decrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } int urcrypt_aes_ecbb_en(uint8_t key[24], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes192_ctx ctx; urcrypt__reverse(24, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_encrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes192_set_encrypt_key(&ctx, key); + aes192_encrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } int urcrypt_aes_ecbb_de(uint8_t key[24], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes192_ctx ctx; urcrypt__reverse(24, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_decrypt_key(key, 192, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes192_set_decrypt_key(&ctx, key); + aes192_decrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } int urcrypt_aes_ecbc_en(uint8_t key[32], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes256_ctx ctx; urcrypt__reverse(32, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_encrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_ENCRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes256_set_encrypt_key(&ctx, key); + aes256_encrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } int urcrypt_aes_ecbc_de(uint8_t key[32], uint8_t block[16], uint8_t out[16]) { - AES_KEY aes_key; + struct aes256_ctx ctx; urcrypt__reverse(32, key); urcrypt__reverse(16, block); - if ( 0 != AES_set_decrypt_key(key, 256, &aes_key) ) { - return -1; - } - else { - AES_ecb_encrypt(block, out, &aes_key, AES_DECRYPT); - urcrypt__reverse(16, out); - return 0; - } + aes256_set_decrypt_key(&ctx, key); + aes256_decrypt(&ctx, 16, out, block); + urcrypt__reverse(16, out); + return 0; } diff --git a/urcrypt/ripemd.c b/urcrypt/ripemd.c index 7871840..8e14cfa 100644 --- a/urcrypt/ripemd.c +++ b/urcrypt/ripemd.c @@ -1,20 +1,16 @@ #include "urcrypt.h" #include "util.h" -#include +#include int urcrypt_ripemd160(uint8_t *message, size_t length, uint8_t out[20]) { - unsigned long n = length; - - if ( length != n ) { - return -1; - } - else { - urcrypt__reverse(length, message); - RIPEMD160(message, n, out); - urcrypt__reverse(20, out); - return 0; - } + struct ripemd160_ctx ctx; + urcrypt__reverse(length, message); + ripemd160_init(&ctx); + ripemd160_update(&ctx, length, message); + ripemd160_digest(&ctx, out); + urcrypt__reverse(20, out); + return 0; } diff --git a/urcrypt/sha.c b/urcrypt/sha.c index 9cd318c..9ace7f2 100644 --- a/urcrypt/sha.c +++ b/urcrypt/sha.c @@ -1,25 +1,35 @@ #include "urcrypt.h" #include "util.h" -#include +#include +#include void urcrypt_sha1(uint8_t *message, size_t length, uint8_t out[20]) { + struct sha1_ctx ctx; urcrypt__reverse(length, message); - SHA1(message, length, out); + sha1_init(&ctx); + sha1_update(&ctx, length, message); + sha1_digest(&ctx, out); urcrypt__reverse(20, out); } void urcrypt_shay(const uint8_t *message, size_t length, uint8_t out[32]) { - SHA256(message, length, out); + struct sha256_ctx ctx; + sha256_init(&ctx); + sha256_update(&ctx, length, message); + sha256_digest(&ctx, out); } void urcrypt_shal(const uint8_t *message, size_t length, uint8_t out[64]) { - SHA512(message, length, out); + struct sha512_ctx ctx; + sha512_init(&ctx); + sha512_update(&ctx, length, message); + sha512_digest(&ctx, out); } void From 802b32bf09922be0b3211b2c73d25a0f9b23480a Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 29 May 2026 16:40:05 -0400 Subject: [PATCH 3/3] refactors aes-siv, removing dynamic ctx allocation --- aes_siv/aes_siv.c | 39 ---------------------- aes_siv/aes_siv.h | 42 +++++++++++++++++++++-- aes_siv/tests.c | 76 +++++++++++++----------------------------- urcrypt/aes_siv.c | 85 ++++++++++++++++++++++------------------------- 4 files changed, 103 insertions(+), 139 deletions(-) diff --git a/aes_siv/aes_siv.c b/aes_siv/aes_siv.c index dac362a..fe6738e 100644 --- a/aes_siv/aes_siv.c +++ b/aes_siv/aes_siv.c @@ -103,11 +103,6 @@ static void debug(const char *label, const unsigned char *hex, size_t len) { #endif } -typedef union block_un { - uint64_t word[2]; - unsigned char byte[16]; -} block; - const union { uint64_t word; char byte[8]; @@ -216,44 +211,10 @@ static inline void dbl(block *block) { putword(block, 1, low); } -/* AES key schedule for whichever variant is in use. The same union type backs - both the CMAC half and the CTR half of the SIV key. */ -union aes_ctx { - struct aes128_ctx a128; - struct aes192_ctx a192; - struct aes256_ctx a256; -}; - -struct AES_SIV_CTX_st { - /* d stores intermediate results of S2V; it corresponds to D from the - pseudocode in section 2.4 of RFC 5297. */ - block d; - /* cmac_cipher and ctr_cipher hold the AES key schedules for the S2V - (CMAC) and CTR halves of the SIV key; encrypt is the matching Nettle - block-cipher function. cmac_key holds the CMAC subkeys derived from - cmac_cipher, and cmac_ctx is a scratchpad used by - AES_SIV_AssociateData() and AES_SIV_(En|De)cryptFinal. */ - union aes_ctx cmac_cipher, ctr_cipher; - nettle_cipher_func *encrypt; - struct cmac128_key cmac_key; - struct cmac128_ctx cmac_ctx; -}; - void AES_SIV_CTX_cleanup(AES_SIV_CTX *ctx) { cleanse(ctx, sizeof *ctx); } -void AES_SIV_CTX_free(AES_SIV_CTX *ctx) { - if (ctx) { - cleanse(ctx, sizeof *ctx); - free(ctx); - } -} - -AES_SIV_CTX *AES_SIV_CTX_new(void) { - return malloc(sizeof(struct AES_SIV_CTX_st)); -} - int AES_SIV_CTX_copy(AES_SIV_CTX *dst, AES_SIV_CTX const *src) { /* The context is self-contained (no external pointers), so a flat copy reproduces the key schedules, subkeys and S2V state. */ diff --git a/aes_siv/aes_siv.h b/aes_siv/aes_siv.h index f6b2caa..380b2a6 100644 --- a/aes_siv/aes_siv.h +++ b/aes_siv/aes_siv.h @@ -1,11 +1,20 @@ /* Copyright (c) 2017-2019 Akamai Technologies, Inc. * SPDX-License-Identifier: Apache-2.0 + * + * Retargeted onto GNU Nettle for urcrypt. The AES_SIV_CTX is caller-allocated + * (it has no internal pointers, so it can live on the stack), and the library + * never calls malloc; there is no AES_SIV_CTX_new()/AES_SIV_CTX_free(). Wipe a + * finished context with AES_SIV_CTX_cleanup(). */ #ifndef AES_SIV_H_ #define AES_SIV_H_ #include +#include + +#include +#include #define LIBAES_SIV_VERSION_MAJOR 1 #define LIBAES_SIV_VERSION_MINOR 0 @@ -20,12 +29,41 @@ extern "C" { #endif +/* 16-byte block, accessible as two 64-bit words or as bytes. */ +typedef union block_un { + uint64_t word[2]; + unsigned char byte[16]; +} block; + +/* AES key schedule for whichever variant is in use. The same union type backs + both the CMAC half and the CTR half of the SIV key. */ +union aes_ctx { + struct aes128_ctx a128; + struct aes192_ctx a192; + struct aes256_ctx a256; +}; + +/* The context is caller-allocated and fully self-contained (no external + pointers), so it can live on the stack. */ +struct AES_SIV_CTX_st { + /* d stores intermediate results of S2V; it corresponds to D from the + pseudocode in section 2.4 of RFC 5297. */ + block d; + /* cmac_cipher and ctr_cipher hold the AES key schedules for the S2V + (CMAC) and CTR halves of the SIV key; encrypt is the matching Nettle + block-cipher function. cmac_key holds the CMAC subkeys derived from + cmac_cipher, and cmac_ctx is a scratchpad used by + AES_SIV_AssociateData() and AES_SIV_(En|De)cryptFinal. */ + union aes_ctx cmac_cipher, ctr_cipher; + nettle_cipher_func *encrypt; + struct cmac128_key cmac_key; + struct cmac128_ctx cmac_ctx; +}; + typedef struct AES_SIV_CTX_st AES_SIV_CTX; -AES_SIV_CTX *AES_SIV_CTX_new(void); int AES_SIV_CTX_copy(AES_SIV_CTX *dst, AES_SIV_CTX const *src); void AES_SIV_CTX_cleanup(AES_SIV_CTX *ctx); -void AES_SIV_CTX_free(AES_SIV_CTX *ctx); int AES_SIV_Init(AES_SIV_CTX *ctx, unsigned char const *key, size_t key_len); int AES_SIV_AssociateData(AES_SIV_CTX *ctx, unsigned char const *data, diff --git a/aes_siv/tests.c b/aes_siv/tests.c index fdc5426..701452a 100644 --- a/aes_siv/tests.c +++ b/aes_siv/tests.c @@ -26,14 +26,12 @@ static void debug(const char *label, const unsigned char *hex, size_t len) { printf("\n"); } -static void test_cleanup_before_free(void) { - printf("Test cleanup before free: "); - AES_SIV_CTX *ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); +static void test_cleanup(void) { + AES_SIV_CTX ctx[1]; + printf("Test cleanup: "); AES_SIV_CTX_cleanup(ctx); - AES_SIV_CTX_free(ctx); printf("OK\n"); -} +} static void test_vector_1(void) { const unsigned char key[] = { @@ -66,7 +64,7 @@ static void test_vector_1(void) { size_t plaintext_len = sizeof plaintext_out; size_t ciphertext_len = sizeof ciphertext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("Test vector 1:\n"); @@ -75,9 +73,6 @@ static void test_vector_1(void) { debug("plaintext", plaintext, sizeof plaintext); debug("exp. ciphertext", ciphertext, sizeof ciphertext); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - printf("Encryption:\n"); ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, sizeof key, NULL, 0, plaintext, sizeof plaintext, @@ -93,7 +88,7 @@ static void test_vector_1(void) { assert(ret == 1); assert(plaintext_len == sizeof plaintext); assert(!memcmp(plaintext, plaintext_out, plaintext_len)); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } static void test_vector_2(void) { @@ -145,7 +140,7 @@ static void test_vector_2(void) { unsigned char ciphertext_out[256]; unsigned char plaintext_out[256]; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("Test vector 2:\n"); @@ -156,9 +151,6 @@ static void test_vector_2(void) { debug("plaintext", plaintext, sizeof plaintext); debug("exp. ciphertext", ciphertext, sizeof ciphertext); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - printf("Encryption:\n"); ret = AES_SIV_Init(ctx, key, sizeof key); assert(ret == 1); @@ -188,7 +180,7 @@ static void test_vector_2(void) { assert(ret == 1); debug("plaintext", plaintext_out, sizeof plaintext); assert(!memcmp(plaintext_out, plaintext, sizeof plaintext)); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } static void test_384bit(void) { @@ -225,7 +217,7 @@ static void test_384bit(void) { size_t plaintext_len = sizeof plaintext_out; size_t ciphertext_len = sizeof ciphertext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("384-bit key test:\n"); @@ -234,9 +226,6 @@ static void test_384bit(void) { debug("plaintext", plaintext, sizeof plaintext); debug("exp. ciphertext", ciphertext, sizeof ciphertext); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - printf("Encryption:\n"); ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, sizeof key, NULL, 0, plaintext, sizeof plaintext, @@ -252,7 +241,7 @@ static void test_384bit(void) { assert(ret == 1); assert(plaintext_len == sizeof plaintext); assert(!memcmp(plaintext, plaintext_out, plaintext_len)); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } static void test_512bit(void) { @@ -291,7 +280,7 @@ static void test_512bit(void) { size_t plaintext_len = sizeof plaintext_out; size_t ciphertext_len = sizeof ciphertext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("512-bit key test:\n"); @@ -300,9 +289,6 @@ static void test_512bit(void) { debug("plaintext", plaintext, sizeof plaintext); debug("exp. ciphertext", ciphertext, sizeof ciphertext); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - printf("Encryption:\n"); ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, sizeof key, NULL, 0, plaintext, sizeof plaintext, @@ -318,7 +304,7 @@ static void test_512bit(void) { assert(ret == 1); assert(plaintext_len == sizeof plaintext); assert(!memcmp(plaintext, plaintext_out, plaintext_len)); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } static void test_highlevel_with_nonce(void) { @@ -351,7 +337,7 @@ static void test_highlevel_with_nonce(void) { size_t plaintext_len = sizeof plaintext_out; size_t ciphertext_len = sizeof ciphertext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("Test high-level interface with non-NULL nonce:\n"); @@ -360,9 +346,6 @@ static void test_highlevel_with_nonce(void) { debug("nonce", nonce, sizeof nonce); debug("plaintext", plaintext, sizeof plaintext); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - printf("Encryption:\n"); ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, sizeof key, nonce, sizeof nonce, plaintext, @@ -376,7 +359,7 @@ static void test_highlevel_with_nonce(void) { assert(ret == 1); assert(plaintext_len == sizeof plaintext); assert(!memcmp(plaintext, plaintext_out, plaintext_len)); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } static void test_copy(void) { @@ -412,16 +395,9 @@ static void test_copy(void) { unsigned char ciphertext1_out[256], ciphertext2_out[256]; - AES_SIV_CTX *ctx1, *ctx2, *ctx3; + AES_SIV_CTX ctx1[1], ctx2[1], ctx3[1]; int ret; - ctx1 = AES_SIV_CTX_new(); - assert(ctx1 != NULL); - ctx2 = AES_SIV_CTX_new(); - assert(ctx2 != NULL); - ctx3 = AES_SIV_CTX_new(); - assert(ctx3 != NULL); - ret = AES_SIV_Init(ctx1, key, sizeof key); assert(ret == 1); ret = AES_SIV_CTX_copy(ctx2, ctx1); @@ -450,9 +426,9 @@ static void test_copy(void) { assert(ret == 1); assert(memcmp(ciphertext, ciphertext2_out, sizeof ciphertext)); - AES_SIV_CTX_free(ctx1); - AES_SIV_CTX_free(ctx2); - AES_SIV_CTX_free(ctx3); + AES_SIV_CTX_cleanup(ctx1); + AES_SIV_CTX_cleanup(ctx2); + AES_SIV_CTX_cleanup(ctx3); } static void test_bad_key(void) { @@ -463,14 +439,11 @@ static void test_bad_key(void) { unsigned char ciphertext_out[256]; size_t ciphertext_len = sizeof ciphertext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("Test bad key size: "); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - ret = AES_SIV_Encrypt(ctx, ciphertext_out, &ciphertext_len, key, sizeof key, NULL, 0, plaintext, sizeof plaintext, ad, sizeof ad); @@ -479,7 +452,7 @@ static void test_bad_key(void) { ret = AES_SIV_Init(ctx, key, sizeof key); assert(ret == 0); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); printf("OK\n"); } @@ -491,24 +464,21 @@ static void test_decrypt_failure(void) { unsigned char plaintext_out[256]; size_t plaintext_len = sizeof plaintext_out; - AES_SIV_CTX *ctx; + AES_SIV_CTX ctx[1]; int ret; printf("Test decryption failure:\n"); - ctx = AES_SIV_CTX_new(); - assert(ctx != NULL); - ret = AES_SIV_Decrypt(ctx, plaintext_out, &plaintext_len, key, sizeof key, NULL, 0, ciphertext, sizeof ciphertext, ad, sizeof ad); assert(ret == 0); - AES_SIV_CTX_free(ctx); + AES_SIV_CTX_cleanup(ctx); } int main(void) { - test_cleanup_before_free(); + test_cleanup(); test_vector_1(); test_vector_2(); test_384bit(); diff --git a/urcrypt/aes_siv.c b/urcrypt/aes_siv.c index 3e3528b..8e7b2f1 100644 --- a/urcrypt/aes_siv.c +++ b/urcrypt/aes_siv.c @@ -2,38 +2,34 @@ #include "util.h" #include -static AES_SIV_CTX* -urcrypt__aes_siv_init(uint8_t *key, +/* the caller stack-allocates the context (see aes_siv.h); urcrypt never calls + * malloc, and we wipe the finished context with AES_SIV_CTX_cleanup(). + */ +static int +urcrypt__aes_siv_init(AES_SIV_CTX *ctx, + uint8_t *key, size_t key_length, urcrypt_aes_siv_data *data, size_t data_length) { - AES_SIV_CTX *ctx = AES_SIV_CTX_new(); - if ( NULL == ctx ) { - return NULL; + urcrypt__reverse(key_length, key); + if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { + return -1; } else { - urcrypt__reverse(key_length, key); - if ( 0 == AES_SIV_Init(ctx, key, key_length) ) { - AES_SIV_CTX_free(ctx); - return NULL; - } - else { - size_t i, len; - uint8_t *dat; - - for ( i = 0; i < data_length; ++i ) { - len = data[i].length; - dat = data[i].bytes; - urcrypt__reverse(len, dat); - if ( 0 == AES_SIV_AssociateData(ctx, dat, len) ) { - AES_SIV_CTX_free(ctx); - return NULL; - } + size_t i, len; + uint8_t *dat; + + for ( i = 0; i < data_length; ++i ) { + len = data[i].length; + dat = data[i].bytes; + urcrypt__reverse(len, dat); + if ( 0 == AES_SIV_AssociateData(ctx, dat, len) ) { + return -1; } - - return ctx; } + + return 0; } } @@ -47,26 +43,26 @@ urcrypt__aes_siv_en(uint8_t *key, uint8_t iv[16], uint8_t *out) { - AES_SIV_CTX *ctx = urcrypt__aes_siv_init(key, key_length, data, data_length); + int ret; + AES_SIV_CTX ctx; - if ( NULL == ctx ) { - return -1; + if ( 0 != urcrypt__aes_siv_init(&ctx, key, key_length, data, data_length) ) { + ret = -1; } else { - int ret; urcrypt__reverse(message_length, message); - ret = AES_SIV_EncryptFinal(ctx, iv, out, message, message_length); - AES_SIV_CTX_free(ctx); - - if ( 0 == ret ) { - return -2; + if ( 0 == AES_SIV_EncryptFinal(&ctx, iv, out, message, message_length) ) { + ret = -2; } else { urcrypt__reverse(16, iv); urcrypt__reverse(message_length, out); - return 0; + ret = 0; } } + + AES_SIV_CTX_cleanup(&ctx); + return ret; } static int @@ -79,27 +75,26 @@ urcrypt__aes_siv_de(uint8_t *key, uint8_t iv[16], uint8_t *out) { - AES_SIV_CTX *ctx = urcrypt__aes_siv_init(key, key_length, data, data_length); + int ret; + AES_SIV_CTX ctx; - if ( NULL == ctx ) { - return -1; + if ( 0 != urcrypt__aes_siv_init(&ctx, key, key_length, data, data_length) ) { + ret = -1; } else { - int ret; - urcrypt__reverse(message_length, message); urcrypt__reverse(16, iv); - ret = AES_SIV_DecryptFinal(ctx, out, iv, message, message_length); - AES_SIV_CTX_free(ctx); - - if ( 0 == ret ) { - return -2; + if ( 0 == AES_SIV_DecryptFinal(&ctx, out, iv, message, message_length) ) { + ret = -2; } else { urcrypt__reverse(message_length, out); - return 0; + ret = 0; } } + + AES_SIV_CTX_cleanup(&ctx); + return ret; } int