diff options
author | Todd Short <tshort@akamai.com> | 2017-05-19 10:27:28 -0400 |
---|---|---|
committer | Pauli <paul.dale@oracle.com> | 2018-12-12 08:16:10 +1000 |
commit | b1ceb439f234a998db84f27a3a245dab95d322ab (patch) | |
tree | 98dbc974b4a6119fe88c01ae0a4017b13fa10e93 /crypto/modes | |
parent | 6de98b4fb6265f8a4b2e5b599d6714ff937dca6b (diff) |
Add RFC5297 AES-SIV support
Based originally on github.com/dfoxfranke/libaes_siv
This creates an SIV128 mode that uses EVP interfaces for the CBC, CTR
and CMAC code to reduce complexity at the cost of perfomance. The
expected use is for short inputs, not TLS-sized records.
Add multiple AAD input capacity in the EVP tests.
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
(Merged from https://github.com/openssl/openssl/pull/3540)
Diffstat (limited to 'crypto/modes')
-rw-r--r-- | crypto/modes/build.info | 2 | ||||
-rw-r--r-- | crypto/modes/modes_lcl.h | 25 | ||||
-rw-r--r-- | crypto/modes/siv128.c | 349 |
3 files changed, 375 insertions, 1 deletions
diff --git a/crypto/modes/build.info b/crypto/modes/build.info index 821340eb90..1820ab26ba 100644 --- a/crypto/modes/build.info +++ b/crypto/modes/build.info @@ -1,7 +1,7 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ cbc128.c ctr128.c cts128.c cfb128.c ofb128.c gcm128.c \ - ccm128.c xts128.c wrap128.c ocb128.c \ + ccm128.c xts128.c wrap128.c ocb128.c siv128.c \ {- $target{modes_asm_src} -} INCLUDE[gcm128.o]=.. diff --git a/crypto/modes/modes_lcl.h b/crypto/modes/modes_lcl.h index 0215bbdedd..d042d30016 100644 --- a/crypto/modes/modes_lcl.h +++ b/crypto/modes/modes_lcl.h @@ -188,3 +188,28 @@ struct ocb128_context { } sess; }; #endif /* OPENSSL_NO_OCB */ + +#ifndef OPENSSL_NO_SIV + +#include <openssl/cmac.h> + +#define SIV_LEN 16 + +typedef union siv_block_u { + uint64_t word[SIV_LEN/sizeof(uint64_t)]; + unsigned char byte[SIV_LEN]; +} SIV_BLOCK; + +struct siv128_context { + /* d stores intermediate results of S2V; it corresponds to D from the + pseudocode in section 2.4 of RFC 5297. */ + SIV_BLOCK d; + SIV_BLOCK tag; + EVP_CIPHER_CTX *cipher_ctx; + CMAC_CTX *cmac_ctx_init; + CMAC_CTX *cmac_ctx; + int final_ret; + int crypto_ok; +}; + +#endif /* OPENSSL_NO_SIV */ diff --git a/crypto/modes/siv128.c b/crypto/modes/siv128.c new file mode 100644 index 0000000000..f4d07d514a --- /dev/null +++ b/crypto/modes/siv128.c @@ -0,0 +1,349 @@ +/* + * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <string.h> +#include <stdlib.h> +#include <openssl/crypto.h> +#include <openssl/cmac.h> +#include "modes_lcl.h" + +#ifndef OPENSSL_NO_SIV + +__owur static ossl_inline uint32_t rotl8(uint32_t x) +{ + return (x << 8) | (x >> 24); +} + +__owur static ossl_inline uint32_t rotr8(uint32_t x) +{ + return (x >> 8) | (x << 24); +} + +__owur static ossl_inline uint64_t byteswap8(uint64_t x) +{ + uint32_t high = (uint32_t)(x >> 32); + uint32_t low = (uint32_t)x; + + high = (rotl8(high) & 0x00ff00ff) | (rotr8(high) & 0xff00ff00); + low = (rotl8(low) & 0x00ff00ff) | (rotr8(low) & 0xff00ff00); + return ((uint64_t)low) << 32 | (uint64_t)high; +} + +__owur static ossl_inline uint64_t siv128_getword(SIV_BLOCK const *b, size_t i) +{ + const union { + long one; + char little; + } is_endian = { 1 }; + + if (is_endian.little) + return byteswap8(b->word[i]); + return b->word[i]; +} + +static ossl_inline void siv128_putword(SIV_BLOCK *b, size_t i, uint64_t x) +{ + const union { + long one; + char little; + } is_endian = { 1 }; + + if (is_endian.little) + b->word[i] = byteswap8(x); + else + b->word[i] = x; +} + +static ossl_inline void siv128_xorblock(SIV_BLOCK *x, + SIV_BLOCK const *y) +{ + x->word[0] ^= y->word[0]; + x->word[1] ^= y->word[1]; +} + +/* + * Doubles |b|, which is 16 bytes representing an element + * of GF(2**128) modulo the irreducible polynomial + * x**128 + x**7 + x**2 + x + 1. + * Assumes two's-complement arithmetic + */ +static ossl_inline void siv128_dbl(SIV_BLOCK *b) +{ + uint64_t high = siv128_getword(b, 0); + uint64_t low = siv128_getword(b, 1); + uint64_t high_carry = high & (((uint64_t)1) << 63); + uint64_t low_carry = low & (((uint64_t)1) << 63); + 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; + siv128_putword(b, 0, high); + siv128_putword(b, 1, low); +} + +__owur static ossl_inline int siv128_do_s2v_p(SIV128_CONTEXT *ctx, SIV_BLOCK *out, + unsigned char const* in, size_t len) +{ + SIV_BLOCK t; + size_t out_len = sizeof(out->byte); + + if (!CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init)) + return 0; + + if (len >= SIV_LEN) { + if (!CMAC_Update(ctx->cmac_ctx, in, len - SIV_LEN)) + return 0; + memcpy(&t, in + (len-SIV_LEN), SIV_LEN); + siv128_xorblock(&t, &ctx->d); + if (!CMAC_Update(ctx->cmac_ctx, t.byte, SIV_LEN)) + return 0; + } else { + memset(&t, 0, sizeof(t)); + memcpy(&t, in, len); + t.byte[len] = 0x80; + siv128_dbl(&ctx->d); + siv128_xorblock(&t, &ctx->d); + if (!CMAC_Update(ctx->cmac_ctx, t.byte, SIV_LEN)) + return 0; + } + if (!CMAC_Final(ctx->cmac_ctx, out->byte, &out_len) + || out_len != SIV_LEN) + return 0; + return 1; +} + + +__owur static ossl_inline int siv128_do_encrypt(EVP_CIPHER_CTX *ctx, unsigned char *out, + unsigned char const *in, size_t len, + SIV_BLOCK *icv) +{ + int out_len = (int)len; + + if (!EVP_CipherInit_ex(ctx, NULL, NULL, NULL, icv->byte, 1)) + return 0; + return EVP_EncryptUpdate(ctx, out, &out_len, in, out_len); +} + +/* + * Create a new SIV128_CONTEXT + */ +SIV128_CONTEXT *CRYPTO_siv128_new(const unsigned char *key, int klen, EVP_CIPHER* cbc, EVP_CIPHER* ctr) +{ + SIV128_CONTEXT *ctx; + int ret; + + if ((ctx = OPENSSL_malloc(sizeof(*ctx))) != NULL) { + ret = CRYPTO_siv128_init(ctx, key, klen, cbc, ctr); + if (ret) + return ctx; + OPENSSL_free(ctx); + } + + return NULL; +} + +/* + * Initialise an existing SIV128_CONTEXT + */ +int CRYPTO_siv128_init(SIV128_CONTEXT *ctx, const unsigned char *key, int klen, + const EVP_CIPHER* cbc, const EVP_CIPHER* ctr) +{ + static const unsigned char zero[SIV_LEN] = { 0 }; + size_t out_len = SIV_LEN; + + memset(&ctx->d, 0, sizeof(ctx->d)); + ctx->cipher_ctx = NULL; + ctx->cmac_ctx = NULL; + ctx->cmac_ctx_init = NULL; + + if (key == NULL || cbc == NULL || ctr == NULL + || (ctx->cipher_ctx = EVP_CIPHER_CTX_new()) == NULL + || (ctx->cmac_ctx_init = CMAC_CTX_new()) == NULL + || (ctx->cmac_ctx = CMAC_CTX_new()) == NULL + || !CMAC_Init(ctx->cmac_ctx_init, key, klen, cbc, NULL) + || !EVP_EncryptInit_ex(ctx->cipher_ctx, ctr, NULL, key + klen, NULL) + || !CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) + || !CMAC_Update(ctx->cmac_ctx, zero, sizeof(zero)) + || !CMAC_Final(ctx->cmac_ctx, ctx->d.byte, &out_len)) { + EVP_CIPHER_CTX_free(ctx->cipher_ctx); + CMAC_CTX_free(ctx->cmac_ctx_init); + CMAC_CTX_free(ctx->cmac_ctx); + return 0; + } + + ctx->final_ret = -1; + ctx->crypto_ok = 1; + + return 1; +} + +/* + * Copy an SIV128_CONTEXT object + */ +int CRYPTO_siv128_copy_ctx(SIV128_CONTEXT *dest, SIV128_CONTEXT *src) +{ + memcpy(&dest->d, &src->d, sizeof(src->d)); + if (!EVP_CIPHER_CTX_copy(dest->cipher_ctx, src->cipher_ctx)) + return 0; + if (!CMAC_CTX_copy(dest->cmac_ctx_init, src->cmac_ctx_init)) + return 0; + /* no need to copy cmac_ctx since it's temp storage */ + return 1; +} + +/* + * Provide any AAD. This can be called multiple times. + * Per RFC5297, the last piece of associated data + * is the nonce, but it's not treated special + */ +int CRYPTO_siv128_aad(SIV128_CONTEXT *ctx, const unsigned char *aad, + size_t len) +{ + SIV_BLOCK cmac_out; + size_t out_len = SIV_LEN; + + siv128_dbl(&ctx->d); + + if (!CMAC_CTX_copy(ctx->cmac_ctx, ctx->cmac_ctx_init) + || !CMAC_Update(ctx->cmac_ctx, aad, len) + || !CMAC_Final(ctx->cmac_ctx, cmac_out.byte, &out_len) + || out_len != SIV_LEN) + return 0; + + siv128_xorblock(&ctx->d, &cmac_out); + + return 1; + +} + +/* + * Provide any data to be encrypted. This can be called once. + */ +int CRYPTO_siv128_encrypt(SIV128_CONTEXT *ctx, + const unsigned char *in, unsigned char *out, + size_t len) +{ + SIV_BLOCK q; + + /* can only do one crypto operation */ + if (ctx->crypto_ok == 0) + return 0; + ctx->crypto_ok--; + + if (!siv128_do_s2v_p(ctx, &q, in, len)) + return 0; + + memcpy(ctx->tag.byte, &q, SIV_LEN); + q.byte[8] &= 0x7f; + q.byte[12] &= 0x7f; + + if (!siv128_do_encrypt(ctx->cipher_ctx, out, in, len, &q)) + return 0; + ctx->final_ret = 0; + return len; +} + +/* + * Provide any data to be decrypted. This can be called once. + */ +int CRYPTO_siv128_decrypt(SIV128_CONTEXT *ctx, + const unsigned char *in, unsigned char *out, + size_t len) +{ + unsigned char* p; + SIV_BLOCK t, q; + int i; + + /* can only do one crypto operation */ + if (ctx->crypto_ok == 0) + return 0; + ctx->crypto_ok--; + + memcpy(&q, ctx->tag.byte, SIV_LEN); + q.byte[8] &= 0x7f; + q.byte[12] &= 0x7f; + + if (!siv128_do_encrypt(ctx->cipher_ctx, out, in, len, &q) + || !siv128_do_s2v_p(ctx, &t, out, len)) + return 0; + + p = ctx->tag.byte; + for (i = 0; i < SIV_LEN; i++) + t.byte[i] ^= p[i]; + + if ((t.word[0] | t.word[1]) != 0) { + OPENSSL_cleanse(out, len); + return 0; + } + ctx->final_ret = 0; + return len; +} + +/* + * Return the already calculated final result. + */ +int CRYPTO_siv128_finish(SIV128_CONTEXT *ctx) +{ + return ctx->final_ret; +} + +/* + * Set the tag + */ +int CRYPTO_siv128_set_tag(SIV128_CONTEXT *ctx, const unsigned char *tag, size_t len) +{ + if (len != SIV_LEN) + return 0; + + /* Copy the tag from the supplied buffer */ + memcpy(ctx->tag.byte, tag, len); + return 1; +} + +/* + * Retrieve the calculated tag + */ +int CRYPTO_siv128_get_tag(SIV128_CONTEXT *ctx, unsigned char *tag, size_t len) +{ + if (len != SIV_LEN) + return 0; + + /* Copy the tag into the supplied buffer */ + memcpy(tag, ctx->tag.byte, len); + return 1; +} + +/* + * Release all resources + */ +int CRYPTO_siv128_cleanup(SIV128_CONTEXT *ctx) +{ + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx->cipher_ctx); + ctx->cipher_ctx = NULL; + CMAC_CTX_free(ctx->cmac_ctx_init); + ctx->cmac_ctx_init = NULL; + CMAC_CTX_free(ctx->cmac_ctx); + ctx->cmac_ctx = NULL; + OPENSSL_cleanse(&ctx->d, sizeof(ctx->d)); + OPENSSL_cleanse(&ctx->tag, sizeof(ctx->tag)); + ctx->final_ret = -1; + ctx->crypto_ok = 1; + } + return 1; +} + +int CRYPTO_siv128_speed(SIV128_CONTEXT *ctx, int arg) +{ + ctx->crypto_ok = (arg == 1) ? -1 : 1; + return 1; +} + +#endif /* OPENSSL_NO_SIV */ |