That file was getting too huge and we only need to de-static a single function to do it. Change-Id: Ie2c0bc90a7e538a74318c364a136c337ce8d9bbb Reviewed-on: https://boringssl-review.googlesource.com/14884 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>kris/onging/CECPQ3_patch15
@@ -22,6 +22,8 @@ add_library( | |||
e_rc4.c | |||
e_des.c | |||
e_aes.c | |||
e_aesgcmsiv.c | |||
e_aesctrhmac.c | |||
e_chacha20poly1305.c | |||
tls_cbc.c | |||
@@ -56,7 +56,6 @@ | |||
#include <openssl/mem.h> | |||
#include <openssl/nid.h> | |||
#include <openssl/rand.h> | |||
#include <openssl/sha.h> | |||
#include "internal.h" | |||
#include "../internal.h" | |||
@@ -399,11 +398,11 @@ static int aes_ofb_cipher(EVP_CIPHER_CTX *ctx, uint8_t *out, const uint8_t *in, | |||
static char aesni_capable(void); | |||
static ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
block128_f *out_block, const uint8_t *key, | |||
size_t key_len) { | |||
ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
block128_f *out_block, const uint8_t *key, | |||
size_t key_bytes) { | |||
if (aesni_capable()) { | |||
aesni_set_encrypt_key(key, key_len * 8, aes_key); | |||
aesni_set_encrypt_key(key, key_bytes * 8, aes_key); | |||
if (gcm_ctx != NULL) { | |||
CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)aesni_encrypt); | |||
} | |||
@@ -414,7 +413,7 @@ static ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
} | |||
if (hwaes_capable()) { | |||
aes_hw_set_encrypt_key(key, key_len * 8, aes_key); | |||
aes_hw_set_encrypt_key(key, key_bytes * 8, aes_key); | |||
if (gcm_ctx != NULL) { | |||
CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)aes_hw_encrypt); | |||
} | |||
@@ -425,7 +424,7 @@ static ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
} | |||
if (bsaes_capable()) { | |||
AES_set_encrypt_key(key, key_len * 8, aes_key); | |||
AES_set_encrypt_key(key, key_bytes * 8, aes_key); | |||
if (gcm_ctx != NULL) { | |||
CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)AES_encrypt); | |||
} | |||
@@ -436,7 +435,7 @@ static ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
} | |||
if (vpaes_capable()) { | |||
vpaes_set_encrypt_key(key, key_len * 8, aes_key); | |||
vpaes_set_encrypt_key(key, key_bytes * 8, aes_key); | |||
if (out_block) { | |||
*out_block = (block128_f) vpaes_encrypt; | |||
} | |||
@@ -446,7 +445,7 @@ static ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
return NULL; | |||
} | |||
AES_set_encrypt_key(key, key_len * 8, aes_key); | |||
AES_set_encrypt_key(key, key_bytes * 8, aes_key); | |||
if (gcm_ctx != NULL) { | |||
CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)AES_encrypt); | |||
} | |||
@@ -1179,587 +1178,6 @@ const EVP_AEAD *EVP_aead_aes_128_gcm(void) { return &aead_aes_128_gcm; } | |||
const EVP_AEAD *EVP_aead_aes_256_gcm(void) { return &aead_aes_256_gcm; } | |||
#define EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN SHA256_DIGEST_LENGTH | |||
#define EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN 12 | |||
struct aead_aes_ctr_hmac_sha256_ctx { | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} ks; | |||
ctr128_f ctr; | |||
block128_f block; | |||
SHA256_CTX inner_init_state; | |||
SHA256_CTX outer_init_state; | |||
uint8_t tag_len; | |||
}; | |||
static void hmac_init(SHA256_CTX *out_inner, SHA256_CTX *out_outer, | |||
const uint8_t hmac_key[32]) { | |||
static const size_t hmac_key_len = 32; | |||
uint8_t block[SHA256_CBLOCK]; | |||
OPENSSL_memcpy(block, hmac_key, hmac_key_len); | |||
OPENSSL_memset(block + hmac_key_len, 0x36, sizeof(block) - hmac_key_len); | |||
unsigned i; | |||
for (i = 0; i < hmac_key_len; i++) { | |||
block[i] ^= 0x36; | |||
} | |||
SHA256_Init(out_inner); | |||
SHA256_Update(out_inner, block, sizeof(block)); | |||
OPENSSL_memset(block + hmac_key_len, 0x5c, sizeof(block) - hmac_key_len); | |||
for (i = 0; i < hmac_key_len; i++) { | |||
block[i] ^= (0x36 ^ 0x5c); | |||
} | |||
SHA256_Init(out_outer); | |||
SHA256_Update(out_outer, block, sizeof(block)); | |||
} | |||
static int aead_aes_ctr_hmac_sha256_init(EVP_AEAD_CTX *ctx, const uint8_t *key, | |||
size_t key_len, size_t tag_len) { | |||
struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx; | |||
static const size_t hmac_key_len = 32; | |||
if (key_len < hmac_key_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
const size_t aes_key_len = key_len - hmac_key_len; | |||
if (aes_key_len != 16 && aes_key_len != 32) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) { | |||
tag_len = EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN; | |||
} | |||
if (tag_len > EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE); | |||
return 0; | |||
} | |||
aes_ctx = OPENSSL_malloc(sizeof(struct aead_aes_ctr_hmac_sha256_ctx)); | |||
if (aes_ctx == NULL) { | |||
OPENSSL_PUT_ERROR(CIPHER, ERR_R_MALLOC_FAILURE); | |||
return 0; | |||
} | |||
aes_ctx->ctr = | |||
aes_ctr_set_key(&aes_ctx->ks.ks, NULL, &aes_ctx->block, key, aes_key_len); | |||
aes_ctx->tag_len = tag_len; | |||
hmac_init(&aes_ctx->inner_init_state, &aes_ctx->outer_init_state, | |||
key + aes_key_len); | |||
ctx->aead_state = aes_ctx; | |||
return 1; | |||
} | |||
static void aead_aes_ctr_hmac_sha256_cleanup(EVP_AEAD_CTX *ctx) { | |||
struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
OPENSSL_cleanse(aes_ctx, sizeof(struct aead_aes_ctr_hmac_sha256_ctx)); | |||
OPENSSL_free(aes_ctx); | |||
} | |||
static void hmac_update_uint64(SHA256_CTX *sha256, uint64_t value) { | |||
unsigned i; | |||
uint8_t bytes[8]; | |||
for (i = 0; i < sizeof(bytes); i++) { | |||
bytes[i] = value & 0xff; | |||
value >>= 8; | |||
} | |||
SHA256_Update(sha256, bytes, sizeof(bytes)); | |||
} | |||
static void hmac_calculate(uint8_t out[SHA256_DIGEST_LENGTH], | |||
const SHA256_CTX *inner_init_state, | |||
const SHA256_CTX *outer_init_state, | |||
const uint8_t *ad, size_t ad_len, | |||
const uint8_t *nonce, const uint8_t *ciphertext, | |||
size_t ciphertext_len) { | |||
SHA256_CTX sha256; | |||
OPENSSL_memcpy(&sha256, inner_init_state, sizeof(sha256)); | |||
hmac_update_uint64(&sha256, ad_len); | |||
hmac_update_uint64(&sha256, ciphertext_len); | |||
SHA256_Update(&sha256, nonce, EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN); | |||
SHA256_Update(&sha256, ad, ad_len); | |||
/* Pad with zeros to the end of the SHA-256 block. */ | |||
const unsigned num_padding = | |||
(SHA256_CBLOCK - ((sizeof(uint64_t)*2 + | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN + ad_len) % | |||
SHA256_CBLOCK)) % | |||
SHA256_CBLOCK; | |||
uint8_t padding[SHA256_CBLOCK]; | |||
OPENSSL_memset(padding, 0, num_padding); | |||
SHA256_Update(&sha256, padding, num_padding); | |||
SHA256_Update(&sha256, ciphertext, ciphertext_len); | |||
uint8_t inner_digest[SHA256_DIGEST_LENGTH]; | |||
SHA256_Final(inner_digest, &sha256); | |||
OPENSSL_memcpy(&sha256, outer_init_state, sizeof(sha256)); | |||
SHA256_Update(&sha256, inner_digest, sizeof(inner_digest)); | |||
SHA256_Final(out, &sha256); | |||
} | |||
static void aead_aes_ctr_hmac_sha256_crypt( | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx, uint8_t *out, | |||
const uint8_t *in, size_t len, const uint8_t *nonce) { | |||
/* Since the AEAD operation is one-shot, keeping a buffer of unused keystream | |||
* bytes is pointless. However, |CRYPTO_ctr128_encrypt| requires it. */ | |||
uint8_t partial_block_buffer[AES_BLOCK_SIZE]; | |||
unsigned partial_block_offset = 0; | |||
OPENSSL_memset(partial_block_buffer, 0, sizeof(partial_block_buffer)); | |||
uint8_t counter[AES_BLOCK_SIZE]; | |||
OPENSSL_memcpy(counter, nonce, EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN); | |||
OPENSSL_memset(counter + EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN, 0, 4); | |||
if (aes_ctx->ctr) { | |||
CRYPTO_ctr128_encrypt_ctr32(in, out, len, &aes_ctx->ks.ks, counter, | |||
partial_block_buffer, &partial_block_offset, | |||
aes_ctx->ctr); | |||
} else { | |||
CRYPTO_ctr128_encrypt(in, out, len, &aes_ctx->ks.ks, counter, | |||
partial_block_buffer, &partial_block_offset, | |||
aes_ctx->block); | |||
} | |||
} | |||
static int aead_aes_ctr_hmac_sha256_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
const uint64_t in_len_64 = in_len; | |||
if (in_len + aes_ctx->tag_len < in_len || | |||
/* This input is so large it would overflow the 32-bit block counter. */ | |||
in_len_64 >= (UINT64_C(1) << 32) * AES_BLOCK_SIZE) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
if (max_out_len < in_len + aes_ctx->tag_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
aead_aes_ctr_hmac_sha256_crypt(aes_ctx, out, in, in_len, nonce); | |||
uint8_t hmac_result[SHA256_DIGEST_LENGTH]; | |||
hmac_calculate(hmac_result, &aes_ctx->inner_init_state, | |||
&aes_ctx->outer_init_state, ad, ad_len, nonce, out, in_len); | |||
OPENSSL_memcpy(out + in_len, hmac_result, aes_ctx->tag_len); | |||
*out_len = in_len + aes_ctx->tag_len; | |||
return 1; | |||
} | |||
static int aead_aes_ctr_hmac_sha256_open(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
size_t plaintext_len; | |||
if (in_len < aes_ctx->tag_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
plaintext_len = in_len - aes_ctx->tag_len; | |||
if (max_out_len < plaintext_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
uint8_t hmac_result[SHA256_DIGEST_LENGTH]; | |||
hmac_calculate(hmac_result, &aes_ctx->inner_init_state, | |||
&aes_ctx->outer_init_state, ad, ad_len, nonce, in, | |||
plaintext_len); | |||
if (CRYPTO_memcmp(hmac_result, in + plaintext_len, aes_ctx->tag_len) != 0) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
aead_aes_ctr_hmac_sha256_crypt(aes_ctx, out, in, plaintext_len, nonce); | |||
*out_len = plaintext_len; | |||
return 1; | |||
} | |||
static const EVP_AEAD aead_aes_128_ctr_hmac_sha256 = { | |||
16 /* AES key */ + 32 /* HMAC key */, | |||
12, /* nonce length */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* max tag length */ | |||
aead_aes_ctr_hmac_sha256_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_ctr_hmac_sha256_cleanup, | |||
aead_aes_ctr_hmac_sha256_seal, | |||
aead_aes_ctr_hmac_sha256_open, | |||
NULL /* get_iv */, | |||
}; | |||
static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = { | |||
32 /* AES key */ + 32 /* HMAC key */, | |||
12, /* nonce length */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* max tag length */ | |||
aead_aes_ctr_hmac_sha256_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_ctr_hmac_sha256_cleanup, | |||
aead_aes_ctr_hmac_sha256_seal, | |||
aead_aes_ctr_hmac_sha256_open, | |||
NULL /* get_iv */, | |||
}; | |||
const EVP_AEAD *EVP_aead_aes_128_ctr_hmac_sha256(void) { | |||
return &aead_aes_128_ctr_hmac_sha256; | |||
} | |||
const EVP_AEAD *EVP_aead_aes_256_ctr_hmac_sha256(void) { | |||
return &aead_aes_256_ctr_hmac_sha256; | |||
} | |||
#if !defined(OPENSSL_SMALL) | |||
#define EVP_AEAD_AES_GCM_SIV_NONCE_LEN 12 | |||
#define EVP_AEAD_AES_GCM_SIV_TAG_LEN 16 | |||
struct aead_aes_gcm_siv_ctx { | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} ks; | |||
block128_f kgk_block; | |||
unsigned is_256:1; | |||
}; | |||
static int aead_aes_gcm_siv_init(EVP_AEAD_CTX *ctx, const uint8_t *key, | |||
size_t key_len, size_t tag_len) { | |||
const size_t key_bits = key_len * 8; | |||
if (key_bits != 128 && key_bits != 256) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) { | |||
tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
} | |||
if (tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE); | |||
return 0; | |||
} | |||
struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = | |||
OPENSSL_malloc(sizeof(struct aead_aes_gcm_siv_ctx)); | |||
if (gcm_siv_ctx == NULL) { | |||
return 0; | |||
} | |||
OPENSSL_memset(gcm_siv_ctx, 0, sizeof(struct aead_aes_gcm_siv_ctx)); | |||
if (aesni_capable()) { | |||
aesni_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks); | |||
gcm_siv_ctx->kgk_block = (block128_f)aesni_encrypt; | |||
} else if (hwaes_capable()) { | |||
aes_hw_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks); | |||
gcm_siv_ctx->kgk_block = (block128_f)aes_hw_encrypt; | |||
} else if (vpaes_capable()) { | |||
vpaes_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks); | |||
gcm_siv_ctx->kgk_block = (block128_f)vpaes_encrypt; | |||
} else { | |||
AES_set_encrypt_key(key, key_len * 8, &gcm_siv_ctx->ks.ks); | |||
gcm_siv_ctx->kgk_block = (block128_f)AES_encrypt; | |||
} | |||
gcm_siv_ctx->is_256 = (key_len == 32); | |||
ctx->aead_state = gcm_siv_ctx; | |||
return 1; | |||
} | |||
static void aead_aes_gcm_siv_cleanup(EVP_AEAD_CTX *ctx) { | |||
struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
OPENSSL_cleanse(gcm_siv_ctx, sizeof(struct aead_aes_gcm_siv_ctx)); | |||
OPENSSL_free(gcm_siv_ctx); | |||
} | |||
/* gcm_siv_crypt encrypts (or decrypts—it's the same thing) |in_len| bytes from | |||
* |in| to |out|, using the block function |enc_block| with |key| in counter | |||
* mode, starting at |initial_counter|. This differs from the traditional | |||
* counter mode code in that the counter is handled little-endian, only the | |||
* first four bytes are used and the GCM-SIV tweak to the final byte is | |||
* applied. The |in| and |out| pointers may be equal but otherwise must not | |||
* alias. */ | |||
static void gcm_siv_crypt(uint8_t *out, const uint8_t *in, size_t in_len, | |||
const uint8_t initial_counter[AES_BLOCK_SIZE], | |||
block128_f enc_block, const AES_KEY *key) { | |||
union { | |||
uint32_t w[4]; | |||
uint8_t c[16]; | |||
} counter; | |||
OPENSSL_memcpy(counter.c, initial_counter, AES_BLOCK_SIZE); | |||
counter.c[15] |= 0x80; | |||
for (size_t done = 0; done < in_len;) { | |||
uint8_t keystream[AES_BLOCK_SIZE]; | |||
enc_block(counter.c, keystream, key); | |||
counter.w[0]++; | |||
size_t todo = AES_BLOCK_SIZE; | |||
if (in_len - done < todo) { | |||
todo = in_len - done; | |||
} | |||
for (size_t i = 0; i < todo; i++) { | |||
out[done + i] = keystream[i] ^ in[done + i]; | |||
} | |||
done += todo; | |||
} | |||
} | |||
/* gcm_siv_polyval evaluates POLYVAL at |auth_key| on the given plaintext and | |||
* AD. The result is written to |out_tag|. */ | |||
static void gcm_siv_polyval( | |||
uint8_t out_tag[16], const uint8_t *in, size_t in_len, const uint8_t *ad, | |||
size_t ad_len, const uint8_t auth_key[16], | |||
const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) { | |||
struct polyval_ctx polyval_ctx; | |||
CRYPTO_POLYVAL_init(&polyval_ctx, auth_key); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, ad, ad_len & ~15); | |||
uint8_t scratch[16]; | |||
if (ad_len & 15) { | |||
OPENSSL_memset(scratch, 0, sizeof(scratch)); | |||
OPENSSL_memcpy(scratch, &ad[ad_len & ~15], ad_len & 15); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch)); | |||
} | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, in, in_len & ~15); | |||
if (in_len & 15) { | |||
OPENSSL_memset(scratch, 0, sizeof(scratch)); | |||
OPENSSL_memcpy(scratch, &in[in_len & ~15], in_len & 15); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch)); | |||
} | |||
union { | |||
uint8_t c[16]; | |||
struct { | |||
uint64_t ad; | |||
uint64_t in; | |||
} bitlens; | |||
} length_block; | |||
length_block.bitlens.ad = ad_len * 8; | |||
length_block.bitlens.in = in_len * 8; | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, length_block.c, | |||
sizeof(length_block)); | |||
CRYPTO_POLYVAL_finish(&polyval_ctx, out_tag); | |||
for (size_t i = 0; i < EVP_AEAD_AES_GCM_SIV_NONCE_LEN; i++) { | |||
out_tag[i] ^= nonce[i]; | |||
} | |||
out_tag[15] &= 0x7f; | |||
} | |||
/* gcm_siv_record_keys contains the keys used for a specific GCM-SIV record. */ | |||
struct gcm_siv_record_keys { | |||
uint8_t auth_key[16]; | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} enc_key; | |||
block128_f enc_block; | |||
}; | |||
/* gcm_siv_keys calculates the keys for a specific GCM-SIV record with the | |||
* given nonce and writes them to |*out_keys|. */ | |||
static void gcm_siv_keys( | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx, | |||
struct gcm_siv_record_keys *out_keys, | |||
const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) { | |||
const AES_KEY *const key = &gcm_siv_ctx->ks.ks; | |||
uint8_t key_material[(128 /* POLYVAL key */ + 256 /* max AES key */) / 8]; | |||
const size_t blocks_needed = gcm_siv_ctx->is_256 ? 6 : 4; | |||
uint8_t counter[AES_BLOCK_SIZE]; | |||
OPENSSL_memset(counter, 0, AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN); | |||
OPENSSL_memcpy(counter + AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN, | |||
nonce, EVP_AEAD_AES_GCM_SIV_NONCE_LEN); | |||
for (size_t i = 0; i < blocks_needed; i++) { | |||
counter[0] = i; | |||
uint8_t ciphertext[AES_BLOCK_SIZE]; | |||
gcm_siv_ctx->kgk_block(counter, ciphertext, key); | |||
OPENSSL_memcpy(&key_material[i * 8], ciphertext, 8); | |||
} | |||
OPENSSL_memcpy(out_keys->auth_key, key_material, 16); | |||
aes_ctr_set_key(&out_keys->enc_key.ks, NULL, &out_keys->enc_block, | |||
key_material + 16, gcm_siv_ctx->is_256 ? 32 : 16); | |||
} | |||
static int aead_aes_gcm_siv_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
const uint64_t in_len_64 = in_len; | |||
const uint64_t ad_len_64 = ad_len; | |||
if (in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN < in_len || | |||
in_len_64 > (UINT64_C(1) << 36) || | |||
ad_len_64 >= (UINT64_C(1) << 61)) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
if (max_out_len < in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
struct gcm_siv_record_keys keys; | |||
gcm_siv_keys(gcm_siv_ctx, &keys, nonce); | |||
uint8_t tag[16]; | |||
gcm_siv_polyval(tag, in, in_len, ad, ad_len, keys.auth_key, nonce); | |||
keys.enc_block(tag, tag, &keys.enc_key.ks); | |||
gcm_siv_crypt(out, in, in_len, tag, keys.enc_block, &keys.enc_key.ks); | |||
OPENSSL_memcpy(&out[in_len], tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN); | |||
*out_len = in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
return 1; | |||
} | |||
static int aead_aes_gcm_siv_open(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const uint64_t ad_len_64 = ad_len; | |||
if (ad_len_64 >= (UINT64_C(1) << 61)) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
const uint64_t in_len_64 = in_len; | |||
if (in_len < EVP_AEAD_AES_GCM_SIV_TAG_LEN || | |||
in_len_64 > (UINT64_C(1) << 36) + AES_BLOCK_SIZE) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
const size_t plaintext_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
if (max_out_len < plaintext_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
struct gcm_siv_record_keys keys; | |||
gcm_siv_keys(gcm_siv_ctx, &keys, nonce); | |||
gcm_siv_crypt(out, in, plaintext_len, &in[plaintext_len], keys.enc_block, | |||
&keys.enc_key.ks); | |||
uint8_t expected_tag[EVP_AEAD_AES_GCM_SIV_TAG_LEN]; | |||
gcm_siv_polyval(expected_tag, out, plaintext_len, ad, ad_len, keys.auth_key, | |||
nonce); | |||
keys.enc_block(expected_tag, expected_tag, &keys.enc_key.ks); | |||
if (CRYPTO_memcmp(expected_tag, &in[plaintext_len], sizeof(expected_tag)) != | |||
0) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
*out_len = plaintext_len; | |||
return 1; | |||
} | |||
static const EVP_AEAD aead_aes_128_gcm_siv = { | |||
16, /* key length */ | |||
EVP_AEAD_AES_GCM_SIV_NONCE_LEN, /* nonce length */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */ | |||
aead_aes_gcm_siv_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_gcm_siv_cleanup, | |||
aead_aes_gcm_siv_seal, | |||
aead_aes_gcm_siv_open, | |||
NULL /* get_iv */, | |||
}; | |||
static const EVP_AEAD aead_aes_256_gcm_siv = { | |||
32, /* key length */ | |||
EVP_AEAD_AES_GCM_SIV_NONCE_LEN, /* nonce length */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */ | |||
aead_aes_gcm_siv_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_gcm_siv_cleanup, | |||
aead_aes_gcm_siv_seal, | |||
aead_aes_gcm_siv_open, | |||
NULL /* get_iv */, | |||
}; | |||
const EVP_AEAD *EVP_aead_aes_128_gcm_siv(void) { | |||
return &aead_aes_128_gcm_siv; | |||
} | |||
const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void) { | |||
return &aead_aes_256_gcm_siv; | |||
} | |||
#endif /* !OPENSSL_SMALL */ | |||
int EVP_has_aes_hardware(void) { | |||
#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64) | |||
return aesni_capable() && crypto_gcm_clmul_enabled(); | |||
@@ -0,0 +1,289 @@ | |||
/* Copyright (c) 2017, Google Inc. | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any | |||
* purpose with or without fee is hereby granted, provided that the above | |||
* copyright notice and this permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | |||
#include <openssl/aead.h> | |||
#include <openssl/cipher.h> | |||
#include <openssl/crypto.h> | |||
#include <openssl/err.h> | |||
#include <openssl/sha.h> | |||
#include "internal.h" | |||
#define EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN SHA256_DIGEST_LENGTH | |||
#define EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN 12 | |||
struct aead_aes_ctr_hmac_sha256_ctx { | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} ks; | |||
ctr128_f ctr; | |||
block128_f block; | |||
SHA256_CTX inner_init_state; | |||
SHA256_CTX outer_init_state; | |||
uint8_t tag_len; | |||
}; | |||
static void hmac_init(SHA256_CTX *out_inner, SHA256_CTX *out_outer, | |||
const uint8_t hmac_key[32]) { | |||
static const size_t hmac_key_len = 32; | |||
uint8_t block[SHA256_CBLOCK]; | |||
OPENSSL_memcpy(block, hmac_key, hmac_key_len); | |||
OPENSSL_memset(block + hmac_key_len, 0x36, sizeof(block) - hmac_key_len); | |||
unsigned i; | |||
for (i = 0; i < hmac_key_len; i++) { | |||
block[i] ^= 0x36; | |||
} | |||
SHA256_Init(out_inner); | |||
SHA256_Update(out_inner, block, sizeof(block)); | |||
OPENSSL_memset(block + hmac_key_len, 0x5c, sizeof(block) - hmac_key_len); | |||
for (i = 0; i < hmac_key_len; i++) { | |||
block[i] ^= (0x36 ^ 0x5c); | |||
} | |||
SHA256_Init(out_outer); | |||
SHA256_Update(out_outer, block, sizeof(block)); | |||
} | |||
static int aead_aes_ctr_hmac_sha256_init(EVP_AEAD_CTX *ctx, const uint8_t *key, | |||
size_t key_len, size_t tag_len) { | |||
struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx; | |||
static const size_t hmac_key_len = 32; | |||
if (key_len < hmac_key_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
const size_t aes_key_len = key_len - hmac_key_len; | |||
if (aes_key_len != 16 && aes_key_len != 32) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) { | |||
tag_len = EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN; | |||
} | |||
if (tag_len > EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE); | |||
return 0; | |||
} | |||
aes_ctx = OPENSSL_malloc(sizeof(struct aead_aes_ctr_hmac_sha256_ctx)); | |||
if (aes_ctx == NULL) { | |||
OPENSSL_PUT_ERROR(CIPHER, ERR_R_MALLOC_FAILURE); | |||
return 0; | |||
} | |||
aes_ctx->ctr = | |||
aes_ctr_set_key(&aes_ctx->ks.ks, NULL, &aes_ctx->block, key, aes_key_len); | |||
aes_ctx->tag_len = tag_len; | |||
hmac_init(&aes_ctx->inner_init_state, &aes_ctx->outer_init_state, | |||
key + aes_key_len); | |||
ctx->aead_state = aes_ctx; | |||
return 1; | |||
} | |||
static void aead_aes_ctr_hmac_sha256_cleanup(EVP_AEAD_CTX *ctx) { | |||
struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
OPENSSL_cleanse(aes_ctx, sizeof(struct aead_aes_ctr_hmac_sha256_ctx)); | |||
OPENSSL_free(aes_ctx); | |||
} | |||
static void hmac_update_uint64(SHA256_CTX *sha256, uint64_t value) { | |||
unsigned i; | |||
uint8_t bytes[8]; | |||
for (i = 0; i < sizeof(bytes); i++) { | |||
bytes[i] = value & 0xff; | |||
value >>= 8; | |||
} | |||
SHA256_Update(sha256, bytes, sizeof(bytes)); | |||
} | |||
static void hmac_calculate(uint8_t out[SHA256_DIGEST_LENGTH], | |||
const SHA256_CTX *inner_init_state, | |||
const SHA256_CTX *outer_init_state, | |||
const uint8_t *ad, size_t ad_len, | |||
const uint8_t *nonce, const uint8_t *ciphertext, | |||
size_t ciphertext_len) { | |||
SHA256_CTX sha256; | |||
OPENSSL_memcpy(&sha256, inner_init_state, sizeof(sha256)); | |||
hmac_update_uint64(&sha256, ad_len); | |||
hmac_update_uint64(&sha256, ciphertext_len); | |||
SHA256_Update(&sha256, nonce, EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN); | |||
SHA256_Update(&sha256, ad, ad_len); | |||
/* Pad with zeros to the end of the SHA-256 block. */ | |||
const unsigned num_padding = | |||
(SHA256_CBLOCK - ((sizeof(uint64_t)*2 + | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN + ad_len) % | |||
SHA256_CBLOCK)) % | |||
SHA256_CBLOCK; | |||
uint8_t padding[SHA256_CBLOCK]; | |||
OPENSSL_memset(padding, 0, num_padding); | |||
SHA256_Update(&sha256, padding, num_padding); | |||
SHA256_Update(&sha256, ciphertext, ciphertext_len); | |||
uint8_t inner_digest[SHA256_DIGEST_LENGTH]; | |||
SHA256_Final(inner_digest, &sha256); | |||
OPENSSL_memcpy(&sha256, outer_init_state, sizeof(sha256)); | |||
SHA256_Update(&sha256, inner_digest, sizeof(inner_digest)); | |||
SHA256_Final(out, &sha256); | |||
} | |||
static void aead_aes_ctr_hmac_sha256_crypt( | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx, uint8_t *out, | |||
const uint8_t *in, size_t len, const uint8_t *nonce) { | |||
/* Since the AEAD operation is one-shot, keeping a buffer of unused keystream | |||
* bytes is pointless. However, |CRYPTO_ctr128_encrypt| requires it. */ | |||
uint8_t partial_block_buffer[AES_BLOCK_SIZE]; | |||
unsigned partial_block_offset = 0; | |||
OPENSSL_memset(partial_block_buffer, 0, sizeof(partial_block_buffer)); | |||
uint8_t counter[AES_BLOCK_SIZE]; | |||
OPENSSL_memcpy(counter, nonce, EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN); | |||
OPENSSL_memset(counter + EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN, 0, 4); | |||
if (aes_ctx->ctr) { | |||
CRYPTO_ctr128_encrypt_ctr32(in, out, len, &aes_ctx->ks.ks, counter, | |||
partial_block_buffer, &partial_block_offset, | |||
aes_ctx->ctr); | |||
} else { | |||
CRYPTO_ctr128_encrypt(in, out, len, &aes_ctx->ks.ks, counter, | |||
partial_block_buffer, &partial_block_offset, | |||
aes_ctx->block); | |||
} | |||
} | |||
static int aead_aes_ctr_hmac_sha256_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
const uint64_t in_len_64 = in_len; | |||
if (in_len + aes_ctx->tag_len < in_len || | |||
/* This input is so large it would overflow the 32-bit block counter. */ | |||
in_len_64 >= (UINT64_C(1) << 32) * AES_BLOCK_SIZE) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
if (max_out_len < in_len + aes_ctx->tag_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
aead_aes_ctr_hmac_sha256_crypt(aes_ctx, out, in, in_len, nonce); | |||
uint8_t hmac_result[SHA256_DIGEST_LENGTH]; | |||
hmac_calculate(hmac_result, &aes_ctx->inner_init_state, | |||
&aes_ctx->outer_init_state, ad, ad_len, nonce, out, in_len); | |||
OPENSSL_memcpy(out + in_len, hmac_result, aes_ctx->tag_len); | |||
*out_len = in_len + aes_ctx->tag_len; | |||
return 1; | |||
} | |||
static int aead_aes_ctr_hmac_sha256_open(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_ctr_hmac_sha256_ctx *aes_ctx = ctx->aead_state; | |||
size_t plaintext_len; | |||
if (in_len < aes_ctx->tag_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
plaintext_len = in_len - aes_ctx->tag_len; | |||
if (max_out_len < plaintext_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_CTR_HMAC_SHA256_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
uint8_t hmac_result[SHA256_DIGEST_LENGTH]; | |||
hmac_calculate(hmac_result, &aes_ctx->inner_init_state, | |||
&aes_ctx->outer_init_state, ad, ad_len, nonce, in, | |||
plaintext_len); | |||
if (CRYPTO_memcmp(hmac_result, in + plaintext_len, aes_ctx->tag_len) != 0) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
aead_aes_ctr_hmac_sha256_crypt(aes_ctx, out, in, plaintext_len, nonce); | |||
*out_len = plaintext_len; | |||
return 1; | |||
} | |||
static const EVP_AEAD aead_aes_128_ctr_hmac_sha256 = { | |||
16 /* AES key */ + 32 /* HMAC key */, | |||
12, /* nonce length */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* max tag length */ | |||
aead_aes_ctr_hmac_sha256_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_ctr_hmac_sha256_cleanup, | |||
aead_aes_ctr_hmac_sha256_seal, | |||
aead_aes_ctr_hmac_sha256_open, | |||
NULL /* get_iv */, | |||
}; | |||
static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = { | |||
32 /* AES key */ + 32 /* HMAC key */, | |||
12, /* nonce length */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_CTR_HMAC_SHA256_TAG_LEN, /* max tag length */ | |||
aead_aes_ctr_hmac_sha256_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_ctr_hmac_sha256_cleanup, | |||
aead_aes_ctr_hmac_sha256_seal, | |||
aead_aes_ctr_hmac_sha256_open, | |||
NULL /* get_iv */, | |||
}; | |||
const EVP_AEAD *EVP_aead_aes_128_ctr_hmac_sha256(void) { | |||
return &aead_aes_128_ctr_hmac_sha256; | |||
} | |||
const EVP_AEAD *EVP_aead_aes_256_ctr_hmac_sha256(void) { | |||
return &aead_aes_256_ctr_hmac_sha256; | |||
} |
@@ -0,0 +1,323 @@ | |||
/* Copyright (c) 2017, Google Inc. | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any | |||
* purpose with or without fee is hereby granted, provided that the above | |||
* copyright notice and this permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | |||
#include <openssl/aead.h> | |||
#include <openssl/cipher.h> | |||
#include <openssl/crypto.h> | |||
#include <openssl/err.h> | |||
#include "internal.h" | |||
#if !defined(OPENSSL_SMALL) | |||
#define EVP_AEAD_AES_GCM_SIV_NONCE_LEN 12 | |||
#define EVP_AEAD_AES_GCM_SIV_TAG_LEN 16 | |||
struct aead_aes_gcm_siv_ctx { | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} ks; | |||
block128_f kgk_block; | |||
unsigned is_256:1; | |||
}; | |||
static int aead_aes_gcm_siv_init(EVP_AEAD_CTX *ctx, const uint8_t *key, | |||
size_t key_len, size_t tag_len) { | |||
const size_t key_bits = key_len * 8; | |||
if (key_bits != 128 && key_bits != 256) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH); | |||
return 0; /* EVP_AEAD_CTX_init should catch this. */ | |||
} | |||
if (tag_len == EVP_AEAD_DEFAULT_TAG_LENGTH) { | |||
tag_len = EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
} | |||
if (tag_len != EVP_AEAD_AES_GCM_SIV_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TAG_TOO_LARGE); | |||
return 0; | |||
} | |||
struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = | |||
OPENSSL_malloc(sizeof(struct aead_aes_gcm_siv_ctx)); | |||
if (gcm_siv_ctx == NULL) { | |||
return 0; | |||
} | |||
OPENSSL_memset(gcm_siv_ctx, 0, sizeof(struct aead_aes_gcm_siv_ctx)); | |||
aes_ctr_set_key(&gcm_siv_ctx->ks.ks, NULL, &gcm_siv_ctx->kgk_block, key, | |||
key_len); | |||
gcm_siv_ctx->is_256 = (key_len == 32); | |||
ctx->aead_state = gcm_siv_ctx; | |||
return 1; | |||
} | |||
static void aead_aes_gcm_siv_cleanup(EVP_AEAD_CTX *ctx) { | |||
struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
OPENSSL_cleanse(gcm_siv_ctx, sizeof(struct aead_aes_gcm_siv_ctx)); | |||
OPENSSL_free(gcm_siv_ctx); | |||
} | |||
/* gcm_siv_crypt encrypts (or decrypts—it's the same thing) |in_len| bytes from | |||
* |in| to |out|, using the block function |enc_block| with |key| in counter | |||
* mode, starting at |initial_counter|. This differs from the traditional | |||
* counter mode code in that the counter is handled little-endian, only the | |||
* first four bytes are used and the GCM-SIV tweak to the final byte is | |||
* applied. The |in| and |out| pointers may be equal but otherwise must not | |||
* alias. */ | |||
static void gcm_siv_crypt(uint8_t *out, const uint8_t *in, size_t in_len, | |||
const uint8_t initial_counter[AES_BLOCK_SIZE], | |||
block128_f enc_block, const AES_KEY *key) { | |||
union { | |||
uint32_t w[4]; | |||
uint8_t c[16]; | |||
} counter; | |||
OPENSSL_memcpy(counter.c, initial_counter, AES_BLOCK_SIZE); | |||
counter.c[15] |= 0x80; | |||
for (size_t done = 0; done < in_len;) { | |||
uint8_t keystream[AES_BLOCK_SIZE]; | |||
enc_block(counter.c, keystream, key); | |||
counter.w[0]++; | |||
size_t todo = AES_BLOCK_SIZE; | |||
if (in_len - done < todo) { | |||
todo = in_len - done; | |||
} | |||
for (size_t i = 0; i < todo; i++) { | |||
out[done + i] = keystream[i] ^ in[done + i]; | |||
} | |||
done += todo; | |||
} | |||
} | |||
/* gcm_siv_polyval evaluates POLYVAL at |auth_key| on the given plaintext and | |||
* AD. The result is written to |out_tag|. */ | |||
static void gcm_siv_polyval( | |||
uint8_t out_tag[16], const uint8_t *in, size_t in_len, const uint8_t *ad, | |||
size_t ad_len, const uint8_t auth_key[16], | |||
const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) { | |||
struct polyval_ctx polyval_ctx; | |||
CRYPTO_POLYVAL_init(&polyval_ctx, auth_key); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, ad, ad_len & ~15); | |||
uint8_t scratch[16]; | |||
if (ad_len & 15) { | |||
OPENSSL_memset(scratch, 0, sizeof(scratch)); | |||
OPENSSL_memcpy(scratch, &ad[ad_len & ~15], ad_len & 15); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch)); | |||
} | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, in, in_len & ~15); | |||
if (in_len & 15) { | |||
OPENSSL_memset(scratch, 0, sizeof(scratch)); | |||
OPENSSL_memcpy(scratch, &in[in_len & ~15], in_len & 15); | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, scratch, sizeof(scratch)); | |||
} | |||
union { | |||
uint8_t c[16]; | |||
struct { | |||
uint64_t ad; | |||
uint64_t in; | |||
} bitlens; | |||
} length_block; | |||
length_block.bitlens.ad = ad_len * 8; | |||
length_block.bitlens.in = in_len * 8; | |||
CRYPTO_POLYVAL_update_blocks(&polyval_ctx, length_block.c, | |||
sizeof(length_block)); | |||
CRYPTO_POLYVAL_finish(&polyval_ctx, out_tag); | |||
for (size_t i = 0; i < EVP_AEAD_AES_GCM_SIV_NONCE_LEN; i++) { | |||
out_tag[i] ^= nonce[i]; | |||
} | |||
out_tag[15] &= 0x7f; | |||
} | |||
/* gcm_siv_record_keys contains the keys used for a specific GCM-SIV record. */ | |||
struct gcm_siv_record_keys { | |||
uint8_t auth_key[16]; | |||
union { | |||
double align; | |||
AES_KEY ks; | |||
} enc_key; | |||
block128_f enc_block; | |||
}; | |||
/* gcm_siv_keys calculates the keys for a specific GCM-SIV record with the | |||
* given nonce and writes them to |*out_keys|. */ | |||
static void gcm_siv_keys( | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx, | |||
struct gcm_siv_record_keys *out_keys, | |||
const uint8_t nonce[EVP_AEAD_AES_GCM_SIV_NONCE_LEN]) { | |||
const AES_KEY *const key = &gcm_siv_ctx->ks.ks; | |||
uint8_t key_material[(128 /* POLYVAL key */ + 256 /* max AES key */) / 8]; | |||
const size_t blocks_needed = gcm_siv_ctx->is_256 ? 6 : 4; | |||
uint8_t counter[AES_BLOCK_SIZE]; | |||
OPENSSL_memset(counter, 0, AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN); | |||
OPENSSL_memcpy(counter + AES_BLOCK_SIZE - EVP_AEAD_AES_GCM_SIV_NONCE_LEN, | |||
nonce, EVP_AEAD_AES_GCM_SIV_NONCE_LEN); | |||
for (size_t i = 0; i < blocks_needed; i++) { | |||
counter[0] = i; | |||
uint8_t ciphertext[AES_BLOCK_SIZE]; | |||
gcm_siv_ctx->kgk_block(counter, ciphertext, key); | |||
OPENSSL_memcpy(&key_material[i * 8], ciphertext, 8); | |||
} | |||
OPENSSL_memcpy(out_keys->auth_key, key_material, 16); | |||
aes_ctr_set_key(&out_keys->enc_key.ks, NULL, &out_keys->enc_block, | |||
key_material + 16, gcm_siv_ctx->is_256 ? 32 : 16); | |||
} | |||
static int aead_aes_gcm_siv_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
const uint64_t in_len_64 = in_len; | |||
const uint64_t ad_len_64 = ad_len; | |||
if (in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN < in_len || | |||
in_len_64 > (UINT64_C(1) << 36) || | |||
ad_len_64 >= (UINT64_C(1) << 61)) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
if (max_out_len < in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
struct gcm_siv_record_keys keys; | |||
gcm_siv_keys(gcm_siv_ctx, &keys, nonce); | |||
uint8_t tag[16]; | |||
gcm_siv_polyval(tag, in, in_len, ad, ad_len, keys.auth_key, nonce); | |||
keys.enc_block(tag, tag, &keys.enc_key.ks); | |||
gcm_siv_crypt(out, in, in_len, tag, keys.enc_block, &keys.enc_key.ks); | |||
OPENSSL_memcpy(&out[in_len], tag, EVP_AEAD_AES_GCM_SIV_TAG_LEN); | |||
*out_len = in_len + EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
return 1; | |||
} | |||
static int aead_aes_gcm_siv_open(const EVP_AEAD_CTX *ctx, uint8_t *out, | |||
size_t *out_len, size_t max_out_len, | |||
const uint8_t *nonce, size_t nonce_len, | |||
const uint8_t *in, size_t in_len, | |||
const uint8_t *ad, size_t ad_len) { | |||
const uint64_t ad_len_64 = ad_len; | |||
if (ad_len_64 >= (UINT64_C(1) << 61)) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE); | |||
return 0; | |||
} | |||
const uint64_t in_len_64 = in_len; | |||
if (in_len < EVP_AEAD_AES_GCM_SIV_TAG_LEN || | |||
in_len_64 > (UINT64_C(1) << 36) + AES_BLOCK_SIZE) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
if (nonce_len != EVP_AEAD_AES_GCM_SIV_NONCE_LEN) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE); | |||
return 0; | |||
} | |||
const struct aead_aes_gcm_siv_ctx *gcm_siv_ctx = ctx->aead_state; | |||
const size_t plaintext_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN; | |||
if (max_out_len < plaintext_len) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BUFFER_TOO_SMALL); | |||
return 0; | |||
} | |||
struct gcm_siv_record_keys keys; | |||
gcm_siv_keys(gcm_siv_ctx, &keys, nonce); | |||
gcm_siv_crypt(out, in, plaintext_len, &in[plaintext_len], keys.enc_block, | |||
&keys.enc_key.ks); | |||
uint8_t expected_tag[EVP_AEAD_AES_GCM_SIV_TAG_LEN]; | |||
gcm_siv_polyval(expected_tag, out, plaintext_len, ad, ad_len, keys.auth_key, | |||
nonce); | |||
keys.enc_block(expected_tag, expected_tag, &keys.enc_key.ks); | |||
if (CRYPTO_memcmp(expected_tag, &in[plaintext_len], sizeof(expected_tag)) != | |||
0) { | |||
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT); | |||
return 0; | |||
} | |||
*out_len = plaintext_len; | |||
return 1; | |||
} | |||
static const EVP_AEAD aead_aes_128_gcm_siv = { | |||
16, /* key length */ | |||
EVP_AEAD_AES_GCM_SIV_NONCE_LEN, /* nonce length */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */ | |||
aead_aes_gcm_siv_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_gcm_siv_cleanup, | |||
aead_aes_gcm_siv_seal, | |||
aead_aes_gcm_siv_open, | |||
NULL /* get_iv */, | |||
}; | |||
static const EVP_AEAD aead_aes_256_gcm_siv = { | |||
32, /* key length */ | |||
EVP_AEAD_AES_GCM_SIV_NONCE_LEN, /* nonce length */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* overhead */ | |||
EVP_AEAD_AES_GCM_SIV_TAG_LEN, /* max tag length */ | |||
aead_aes_gcm_siv_init, | |||
NULL /* init_with_direction */, | |||
aead_aes_gcm_siv_cleanup, | |||
aead_aes_gcm_siv_seal, | |||
aead_aes_gcm_siv_open, | |||
NULL /* get_iv */, | |||
}; | |||
const EVP_AEAD *EVP_aead_aes_128_gcm_siv(void) { | |||
return &aead_aes_128_gcm_siv; | |||
} | |||
const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void) { | |||
return &aead_aes_256_gcm_siv; | |||
} | |||
#endif /* !OPENSSL_SMALL */ |
@@ -60,6 +60,9 @@ | |||
#include <openssl/base.h> | |||
#include <openssl/aead.h> | |||
#include <openssl/aes.h> | |||
#include "../modes/internal.h" | |||
#if defined(__cplusplus) | |||
extern "C" { | |||
@@ -154,6 +157,16 @@ int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out, | |||
const uint8_t *mac_secret, | |||
unsigned mac_secret_length); | |||
/* aes_ctr_set_key initialises |*aes_key| using |key_bytes| bytes from |key|, | |||
* where |key_bytes| must either be 16, 24 or 32. If not NULL, |*out_block| is | |||
* set to a function that encrypts single blocks. If not NULL, |*gcm_ctx| is | |||
* initialised to do GHASH with the given key. It returns a function for | |||
* optimised CTR-mode, or NULL if CTR-mode should be built using | |||
* |*out_block|. */ | |||
ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx, | |||
block128_f *out_block, const uint8_t *key, | |||
size_t key_bytes); | |||
#if defined(__cplusplus) | |||
} /* extern C */ | |||
#endif | |||