projects/linux/crypto/aes_xts_openssl.c (168 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/err.h>
#include "aes_xts_openssl.h"
#include "common/buffer_util.h"
// #include "openssl/aes.h"
int aes_xts_openssl_set_key (const struct aes_xts_engine *engine, const uint8_t *key, size_t length)
{
const struct aes_xts_engine_openssl *openssl = (const struct aes_xts_engine_openssl*) engine;
const EVP_CIPHER *cipher;
int status;
if ((openssl == NULL) || (key == NULL)) {
return AES_XTS_ENGINE_INVALID_ARGUMENT;
}
switch (length) {
case (128 / 8) * 2:
cipher = EVP_aes_128_xts ();
break;
case (256 / 8) * 2:
cipher = EVP_aes_256_xts ();
break;
default:
return AES_XTS_ENGINE_INVALID_KEY_LENGTH;
}
status = buffer_compare (key, &key[length / 2], length / 2);
if (status == 0) {
/* The two AES keys must be different. */
return AES_XTS_ENGINE_MATCHING_KEYS;
}
ERR_clear_error ();
status = EVP_CIPHER_CTX_cleanup (openssl->state->encrypt);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
EVP_CIPHER_CTX_init (openssl->state->encrypt);
status = EVP_EncryptInit_ex (openssl->state->encrypt, cipher, NULL, key, NULL);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
status = EVP_CIPHER_CTX_cleanup (openssl->state->decrypt);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
EVP_CIPHER_CTX_init (openssl->state->decrypt);
status = EVP_DecryptInit_ex (openssl->state->decrypt, cipher, NULL, key, NULL);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
return 0;
}
int aes_xts_openssl_encrypt_data (const struct aes_xts_engine *engine, const uint8_t *plaintext,
size_t length, const uint8_t data_unit_id[16], uint8_t *ciphertext, size_t out_length)
{
const struct aes_xts_engine_openssl *openssl = (const struct aes_xts_engine_openssl*) engine;
int status;
int enc_length;
if ((openssl == NULL) || (plaintext == NULL) || (length == 0) || (data_unit_id == NULL) ||
(ciphertext == NULL)) {
return AES_XTS_ENGINE_INVALID_ARGUMENT;
}
if (out_length < length) {
return AES_XTS_ENGINE_OUT_BUFFER_TOO_SMALL;
}
if ((length < 16) || (length > ((1 << 20) * 16))) {
return AES_XTS_ENGINE_INVALID_DATA_LENGTH;
}
if (EVP_CIPHER_CTX_key_length (openssl->state->encrypt) <= 0) {
return AES_XTS_ENGINE_NO_KEY;
}
ERR_clear_error ();
status = EVP_EncryptInit_ex (openssl->state->encrypt, NULL, NULL, NULL, data_unit_id);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
status = EVP_EncryptUpdate (openssl->state->encrypt, ciphertext, &enc_length, plaintext,
length);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
status = EVP_EncryptFinal_ex (openssl->state->encrypt, ciphertext + enc_length, &enc_length);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
return 0;
}
int aes_xts_openssl_decrypt_data (const struct aes_xts_engine *engine, const uint8_t *ciphertext,
size_t length, const uint8_t data_unit_id[16], uint8_t *plaintext, size_t out_length)
{
const struct aes_xts_engine_openssl *openssl = (const struct aes_xts_engine_openssl*) engine;
int status;
int dec_length;
if ((openssl == NULL) || (ciphertext == NULL) || (length == 0) || (data_unit_id == NULL) ||
(plaintext == NULL)) {
return AES_XTS_ENGINE_INVALID_ARGUMENT;
}
if (out_length < length) {
return AES_XTS_ENGINE_OUT_BUFFER_TOO_SMALL;
}
if ((length < 16) || (length > ((1 << 20) * 16))) {
return AES_XTS_ENGINE_INVALID_DATA_LENGTH;
}
if (EVP_CIPHER_CTX_key_length (openssl->state->decrypt) <= 0) {
return AES_XTS_ENGINE_NO_KEY;
}
ERR_clear_error ();
status = EVP_DecryptInit_ex (openssl->state->decrypt, NULL, NULL, NULL, data_unit_id);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
status = EVP_DecryptUpdate (openssl->state->decrypt, plaintext, &dec_length, ciphertext,
length);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
status = EVP_DecryptFinal_ex (openssl->state->decrypt, plaintext + dec_length, &dec_length);
if (status != 1) {
status = ERR_get_error ();
return -status;
}
return 0;
}
/**
* Initialize an instance for running AES-XTS operations using OpenSSL.
*
* @param engine The AES-XTS engine to initialize.
* @param state Variable context for AES operations. This must be uninitialized.
*
* @return 0 if the AES-XTS engine was successfully initialized or an error code.
*/
int aes_xts_openssl_init (struct aes_xts_engine_openssl *engine,
struct aes_xts_engine_openssl_state *state)
{
if ((engine == NULL) || (state == NULL)) {
return AES_XTS_ENGINE_INVALID_ARGUMENT;
}
memset (engine, 0, sizeof (*engine));
engine->base.set_key = aes_xts_openssl_set_key;
engine->base.encrypt_data = aes_xts_openssl_encrypt_data;
engine->base.decrypt_data = aes_xts_openssl_decrypt_data;
engine->state = state;
return aes_xts_openssl_init_state (engine);
}
/**
* Initialize only the variable state of an OpenSSL AES-XTS engine. The rest of the instance is
* assumed to already have been initialized.
*
* This would generally be used with a statically initialized instance.
*
* @param engine The AES-XTS engine that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int aes_xts_openssl_init_state (const struct aes_xts_engine_openssl *engine)
{
if ((engine == NULL) || (engine->state == NULL)) {
return AES_XTS_ENGINE_INVALID_ARGUMENT;
}
memset (engine->state, 0, sizeof (*engine->state));
engine->state->encrypt = EVP_CIPHER_CTX_new ();
if (engine->state->encrypt == NULL) {
return AES_XTS_ENGINE_NO_MEMORY;
}
engine->state->decrypt = EVP_CIPHER_CTX_new ();
if (engine->state->decrypt == NULL) {
EVP_CIPHER_CTX_free (engine->state->encrypt);
return AES_XTS_ENGINE_NO_MEMORY;
}
EVP_CIPHER_CTX_reset (engine->state->encrypt);
EVP_CIPHER_CTX_reset (engine->state->decrypt);
return 0;
}
/**
* Release the resources used by an OpenSSL AES-XTS engine.
*
* @param engine The AES-XTS engine to release.
*/
void aes_xts_openssl_release (const struct aes_xts_engine_openssl *engine)
{
if (engine) {
EVP_CIPHER_CTX_free (engine->state->encrypt);
EVP_CIPHER_CTX_free (engine->state->decrypt);
}
}