projects/linux/crypto/aes_gcm_openssl.c (208 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <openssl/err.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include "aes_gcm_openssl.h" int aes_gcm_openssl_set_key (const struct aes_gcm_engine *engine, const uint8_t *key, size_t length) { const struct aes_gcm_engine_openssl *openssl = (const struct aes_gcm_engine_openssl*) engine; int status; if ((openssl == NULL) || (key == NULL)) { return AES_GCM_ENGINE_INVALID_ARGUMENT; } switch (length) { case (128 / 8): case (192 / 8): return AES_GCM_ENGINE_UNSUPPORTED_KEY_LENGTH; case (256 / 8): break; default: return AES_GCM_ENGINE_INVALID_KEY_LENGTH; } ERR_clear_error (); status = EVP_CIPHER_CTX_cleanup (openssl->state->context); if (status != 1) { status = ERR_get_error (); return -status; } EVP_CIPHER_CTX_init (openssl->state->context); status = EVP_CipherInit_ex (openssl->state->context, EVP_aes_256_gcm (), NULL, key, NULL, -1); if (status != 1) { status = ERR_get_error (); return -status; } return 0; } /** * Initialize the IV for an AES operation. * * @param openssl The AES instance to configure. * @param iv The IV being used. * @param length The length of the IV. * @param encrypt Flag indicating if the operation will be an encrypt or decrypt operation. * * @return 0 if the IV was successfully initialized or an error code. */ static int aes_gcm_openssl_init_iv (const struct aes_gcm_engine_openssl *openssl, const uint8_t *iv, size_t length, int encrypt) { int status; status = EVP_CIPHER_CTX_ctrl (openssl->state->context, EVP_CTRL_GCM_SET_IVLEN, length, NULL); if (status != 1) { status = ERR_get_error (); return -status; } status = EVP_CipherInit_ex (openssl->state->context, NULL, NULL, NULL, iv, encrypt); if (status != 1) { status = ERR_get_error (); return -status; } return 0; } int aes_gcm_openssl_encrypt_with_add_data (const struct aes_gcm_engine *engine, const uint8_t *plaintext, size_t length, const uint8_t *iv, size_t iv_length, const uint8_t *additional_data, size_t additional_data_length, uint8_t *ciphertext, size_t out_length, uint8_t *tag, size_t tag_length) { const struct aes_gcm_engine_openssl *openssl = (const struct aes_gcm_engine_openssl*) engine; int status; int enc_length; if ((openssl == NULL) || (plaintext == NULL) || (length == 0) || (iv == NULL) || (iv_length == 0) || (ciphertext == NULL) || ((additional_data_length > 0) && (additional_data == NULL))) { return AES_GCM_ENGINE_INVALID_ARGUMENT; } if (out_length < length) { return AES_GCM_ENGINE_OUT_BUFFER_TOO_SMALL; } if (tag && (tag_length < 16)) { return AES_GCM_ENGINE_OUT_BUFFER_TOO_SMALL; } if (EVP_CIPHER_CTX_key_length (openssl->state->context) <= 0) { return AES_GCM_ENGINE_NO_KEY; } ERR_clear_error (); status = aes_gcm_openssl_init_iv (openssl, iv, iv_length, 1); if (status != 0) { return status; } if (additional_data) { status = EVP_EncryptUpdate (openssl->state->context, NULL, &enc_length, additional_data, additional_data_length); if (status != 1) { status = ERR_get_error (); return -status; } } status = EVP_EncryptUpdate (openssl->state->context, ciphertext, &enc_length, plaintext, length); if (status != 1) { status = ERR_get_error (); return -status; } status = EVP_EncryptFinal_ex (openssl->state->context, ciphertext + enc_length, &enc_length); if (status != 1) { status = ERR_get_error (); return -status; } if (tag) { status = EVP_CIPHER_CTX_ctrl (openssl->state->context, EVP_CTRL_GCM_GET_TAG, 16, tag); if (status != 1) { status = ERR_get_error (); return -status; } } return 0; } int aes_gcm_openssl_encrypt_data (const struct aes_gcm_engine *engine, const uint8_t *plaintext, size_t length, const uint8_t *iv, size_t iv_length, uint8_t *ciphertext, size_t out_length, uint8_t *tag, size_t tag_length) { return aes_gcm_openssl_encrypt_with_add_data (engine, plaintext, length, iv, iv_length, NULL, 0, ciphertext, out_length, tag, tag_length); } int aes_gcm_openssl_decrypt_with_add_data (const struct aes_gcm_engine *engine, const uint8_t *ciphertext, size_t length, const uint8_t *tag, const uint8_t *iv, size_t iv_length, const uint8_t *additional_data, size_t additional_data_length, uint8_t *plaintext, size_t out_length) { const struct aes_gcm_engine_openssl *openssl = (const struct aes_gcm_engine_openssl*) engine; int status; int dec_length; if ((openssl == NULL) || (ciphertext == NULL) || (length == 0) || (iv == NULL) || (iv_length == 0) || (plaintext == NULL) || ((additional_data_length > 0) && (additional_data == NULL))) { return AES_GCM_ENGINE_INVALID_ARGUMENT; } if (out_length < length) { return AES_GCM_ENGINE_OUT_BUFFER_TOO_SMALL; } if (EVP_CIPHER_CTX_key_length (openssl->state->context) <= 0) { return AES_GCM_ENGINE_NO_KEY; } ERR_clear_error (); status = aes_gcm_openssl_init_iv (openssl, iv, iv_length, 0); if (status != 0) { return status; } if (additional_data) { status = EVP_DecryptUpdate (openssl->state->context, NULL, &dec_length, additional_data, additional_data_length); if (status != 1) { status = ERR_get_error (); return -status; } } status = EVP_DecryptUpdate (openssl->state->context, plaintext, &dec_length, ciphertext, length); if (status != 1) { status = ERR_get_error (); return -status; } if (tag) { status = EVP_CIPHER_CTX_ctrl (openssl->state->context, EVP_CTRL_GCM_SET_TAG, 16, (void*) tag); if (status != 1) { status = ERR_get_error (); return -status; } } status = EVP_DecryptFinal_ex (openssl->state->context, plaintext + dec_length, &dec_length); if (tag) { return (status == 1) ? 0 : AES_GCM_ENGINE_GCM_AUTH_FAILED; } else { return 0; } } int aes_gcm_openssl_decrypt_data (const struct aes_gcm_engine *engine, const uint8_t *ciphertext, size_t length, const uint8_t *tag, const uint8_t *iv, size_t iv_length, uint8_t *plaintext, size_t out_length) { return aes_gcm_openssl_decrypt_with_add_data (engine, ciphertext, length, tag, iv, iv_length, NULL, 0, plaintext, out_length); } /** * Initialize an instance for running AES-GCM operations using OpenSSL. * * @param engine The AES engine to initialize. * * @return 0 if the AES engine was successfully initialized or an error code. */ int aes_gcm_openssl_init (struct aes_gcm_engine_openssl *engine, struct aes_gcm_engine_openssl_state *state) { if (engine == NULL) { return AES_GCM_ENGINE_INVALID_ARGUMENT; } memset (engine, 0, sizeof (struct aes_gcm_engine_openssl)); engine->base.set_key = aes_gcm_openssl_set_key; engine->base.encrypt_data = aes_gcm_openssl_encrypt_data; engine->base.encrypt_with_add_data = aes_gcm_openssl_encrypt_with_add_data; engine->base.decrypt_data = aes_gcm_openssl_decrypt_data; engine->base.decrypt_with_add_data = aes_gcm_openssl_decrypt_with_add_data; engine->state = state; return aes_gcm_openssl_init_state (engine); } /** * Initialize only the variable state of an OpenSSL AES-GCM 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-GCM engine that contains the state to initialize. * * @return 0 if the state was successfully initialized or an error code. */ int aes_gcm_openssl_init_state (const struct aes_gcm_engine_openssl *engine) { if ((engine == NULL) || (engine->state == NULL)) { return AES_GCM_ENGINE_INVALID_ARGUMENT; } memset (engine->state, 0, sizeof (*engine->state)); engine->state->context = EVP_CIPHER_CTX_new (); if (engine->state->context == NULL) { return AES_GCM_ENGINE_NO_MEMORY; } EVP_CIPHER_CTX_reset (engine->state->context); return 0; } /** * Release an OpenSSL AES engine. * * @param engine The AES engine to release. */ void aes_gcm_openssl_release (const struct aes_gcm_engine_openssl *engine) { if (engine) { EVP_CIPHER_CTX_free (engine->state->context); } }