blob: 5450a211c3b024c592a0dd7ed66f8a1a424a8018 [file] [log] [blame]
/**
* @file axHostCryptoOpenSSL.c
* @author NXP Semiconductors
* @version 1.0
* @par License
*
* Copyright 2016 NXP
* SPDX-License-Identifier: Apache-2.0
*
* @par Description
* Host Crypto OpenSSL wrapper implementation for the A7-series
*
* @par HISTORY
*
*/
#include "axHostCrypto.h"
#include "ax_util.h"
#include "sm_types.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "nxLog_hostLib.h"
#include "nxEnsure.h"
#ifdef OPENSSL
#include <openssl/sha.h>
#include <openssl/aes.h>
#include <openssl/cmac.h>
#include <openssl/rand.h>
#include <openssl/des.h>
#include <openssl/evp.h>
#ifdef TGT_A70CU
S32 HOST_SHA256_GetPartialHash(const U8 *msg, U32 msgLen, U8 *pHashState, U32 *pNumProcessedMsgBytes)
{
SHA256_CTX ctx256;
const U32 sha256BlockSize = 64;
int ret = HOST_CRYPTO_ERROR;
ENSURE_OR_GO_EXIT(pHashState != NULL);
ENSURE_OR_GO_EXIT(pNumProcessedMsgBytes != NULL);
ret = SHA256_Init(&ctx256);
if (ret == HOST_CRYPTO_OK)
{
U32 numExtraBytes = msgLen % sha256BlockSize;
U32 numProcessedBytes = msgLen - numExtraBytes;
#ifdef PARTIAL_HASH_DEFAULT_NO_STATE
if (numProcessedBytes > 0)
{
#endif
ret = SHA256_Update(&ctx256, msg, numProcessedBytes);
if (ret == HOST_CRYPTO_OK)
{
SHA_LONG h;
int i;
for (i = 0; i < 8; i++)
{
h = ctx256.h[i];
*pHashState++ = (h >> 24) & 0xff;
*pHashState++ = (h >> 16) & 0xff;
*pHashState++ = (h >> 8) & 0xff;
*pHashState++ = h & 0xff;
}
}
#ifdef PARTIAL_HASH_DEFAULT_NO_STATE
}
#endif
*pNumProcessedMsgBytes = numProcessedBytes;
}
exit:
return ret;
}
#endif // TGT_A70CU
S32 HOST_SHA1_Get(const U8 *msg, U32 msgLen, U8 *pHash)
{
SHA_CTX ctx;
int ret;
ret = SHA1_Init(&ctx);
if (ret == HOST_CRYPTO_OK)
{
ret = SHA1_Update(&ctx, msg, msgLen);
if (ret == HOST_CRYPTO_OK)
{
ret = SHA1_Final(pHash, &ctx);
}
}
return ret;
}
S32 HOST_SHA256_Get(const U8 *msg, U32 msgLen, U8 *pHash)
{
SHA256_CTX ctx256;
int ret;
ret = SHA256_Init(&ctx256);
if (ret == HOST_CRYPTO_OK)
{
ret = SHA256_Update(&ctx256, msg, msgLen);
if (ret == HOST_CRYPTO_OK)
{
ret = SHA256_Final(pHash, &ctx256);
}
}
return ret;
}
S32 HOST_AES_ECB_ENCRYPT(const U8 *plainText, U8 *cipherText, const U8 *key, U32 keyLen)
{
AES_KEY keyLocal;
int keyLenBits = keyLen * 8;
int nRet = 0;
// This works assuming the plaintext has the same size as the key
// NOTE: AES_set_encrypt_key returns 0 upon success
nRet = AES_set_encrypt_key(key, keyLenBits, &keyLocal);
if (nRet != 0)
{
return HOST_CRYPTO_ERROR;
}
// AES_ecb_encrypt has return type void
AES_ecb_encrypt(plainText, cipherText, &keyLocal, AES_ENCRYPT);
return HOST_CRYPTO_OK;
}
S32 HOST_AES_ECB_DECRYPT(U8 *plainText, const U8 *cipherText, const U8 *key, U32 keyLen)
{
AES_KEY keyLocal;
int keyLenBits = keyLen * 8;
int nRet = 0;
// This works assuming the plaintext has the same size as the key
// NOTE: AES_set_encrypt_key returns 0 upon success
nRet = AES_set_decrypt_key(key, keyLenBits, &keyLocal);
if (nRet != 0)
{
return HOST_CRYPTO_ERROR;
}
// AES_ecb_encrypt has return type void
AES_ecb_encrypt(cipherText, plainText, &keyLocal, AES_DECRYPT);
return HOST_CRYPTO_OK;
}
S32 HOST_CMAC_Get(const U8 *pKey, U8 keySizeInBytes, const U8 *pMsg, U32 msgLen, U8* pMac)
{
int ret;
size_t size;
axHcCmacCtx_t *ctx;
ret = HOST_CMAC_Init(&ctx, pKey, keySizeInBytes);
if (ret == HOST_CRYPTO_OK)
{
ret = CMAC_Update(ctx, pMsg, msgLen);
if (ret == HOST_CRYPTO_OK)
{
ret = CMAC_Final(ctx, pMac, &size);
}
}
if (ret != ERR_MEMORY)
{
CMAC_CTX_free(ctx);
}
return ret;
}
S32 HOST_CMAC_Init(axHcCmacCtx_t **ctx, const U8 *pKey, U8 keySizeInBytes)
{
int ret = ERR_GENERAL_ERROR;
ENSURE_OR_GO_EXIT(ctx != NULL);
*ctx = CMAC_CTX_new();
if (*ctx == NULL)
{
return ERR_MEMORY;
}
// CMAC_Init() returns
// 1 = success
// 0 = failure
ret = CMAC_Init(*ctx, pKey, keySizeInBytes, EVP_aes_128_cbc(), NULL);
exit:
return ret;
}
S32 HOST_CMAC_Update(axHcCmacCtx_t *ctx, const U8 *pMsg, U32 msgLen)
{
int ret;
// CMAC_Update() returns
// 1 = success
// 0 = failure
ret = CMAC_Update(ctx, pMsg, msgLen);
return ret;
}
S32 HOST_CMAC_Finish(axHcCmacCtx_t *ctx, U8 *pMac)
{
int ret;
size_t size;
// CMAC_Final() returns
// 1 = success
// 0 = failure
ret = CMAC_Final(ctx, pMac, &size);
CMAC_CTX_free(ctx);
return ret;
}
S32 HOST_AES_CBC_Process(const U8 *pKey, U32 keyLen, const U8 *pIv,
U8 dir, const U8 *pIn, U32 inLen, U8 *pOut)
{
int outLen = 0;
int nRet = ERR_API_ERROR;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX aesCtx; // OpenSSL 1.0
#else
EVP_CIPHER_CTX *aesCtx = NULL; // OpenSSL 1.1
#endif
ENSURE_OR_GO_EXIT(pIn != NULL);
ENSURE_OR_GO_EXIT(pOut != NULL);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_init(&aesCtx);
#else
aesCtx = EVP_CIPHER_CTX_new();
if (aesCtx == NULL) {
return HOST_CRYPTO_ERROR;
}
#endif
if (keyLen != AES_BLOCK_SIZE)
{
// printf("Unsupported key length for HOST_AES_CBC_Process\r\n");
return ERR_API_ERROR;
}
if ((inLen % AES_BLOCK_SIZE) != 0)
{
// printf("Input data are not block aligned for HOST_AES_CBC_Process\r\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
if (dir == HOST_ENCRYPT)
{
// EVP_EncryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_EncryptInit_ex(&aesCtx, EVP_aes_128_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(&aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(&aesCtx, EVP_aes_128_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(&aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#else
if (dir == HOST_ENCRYPT)
{
// EVP_EncryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_EncryptInit_ex(aesCtx, EVP_aes_128_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(aesCtx, EVP_aes_128_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#endif
else
{
// printf("Unsupported direction for HOST_AES_CBC_Process\r\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
nRet = EVP_CIPHER_CTX_cleanup(&aesCtx);
#else
EVP_CIPHER_CTX_free(aesCtx);
nRet = 1;
#endif
exit:
return nRet;
}
S32 HOST_AES_ECB_Process(const U8 *pKey, U32 keyLen, const U8 *pIv,
U8 dir, const U8 *pIn, U32 inLen, U8 *pOut)
{
int outLen = 0;
int nRet = ERR_API_ERROR;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX aesCtx; // OpenSSL 1.0
#else
EVP_CIPHER_CTX *aesCtx = NULL; // OpenSSL 1.1
#endif
ENSURE_OR_GO_EXIT(pIn != NULL);
ENSURE_OR_GO_EXIT(pOut != NULL);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_init(&aesCtx);
#else
aesCtx = EVP_CIPHER_CTX_new();
if (aesCtx == NULL) {
return HOST_CRYPTO_ERROR;
}
#endif
if (keyLen != AES_BLOCK_SIZE)
{
// printf("Unsupported key length for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
if ((inLen % AES_BLOCK_SIZE) != 0)
{
// printf("Input data are not block aligned for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
if (dir == HOST_ENCRYPT)
{
// EVP_EncryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_EncryptInit_ex(&aesCtx, EVP_aes_128_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(&aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(&aesCtx, EVP_aes_128_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(&aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#else
if (dir == HOST_ENCRYPT)
{
// EVP_EncryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_EncryptInit_ex(aesCtx, EVP_aes_128_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(aesCtx, EVP_aes_128_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(aesCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#endif
else
{
// printf("Unsupported direction for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
nRet = EVP_CIPHER_CTX_cleanup(&aesCtx);
#else
EVP_CIPHER_CTX_free(aesCtx);
nRet = 1;
#endif
exit:
return nRet;
}
S32 HOST_GetRandom(U32 inLen, U8 *pRandom)
{
int nRet;
nRet = RAND_bytes(pRandom, inLen);
return nRet;
}
S32 HOST_3DES_CBC_Process(const U8 *pKey, U32 keyLen, const U8 *pIv,
U8 dir, const U8 *pIn, U32 inLen, U8 *pOut)
{
//DES_ecb3_encrypt();
int outLen = 0;
int nRet = ERR_API_ERROR;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX desCtx;
#else
EVP_CIPHER_CTX *desCtx = NULL;
#endif
ENSURE_OR_GO_EXIT(pIn != NULL);
ENSURE_OR_GO_EXIT(pOut != NULL);
if (keyLen == 8) {
DES_key_schedule ks1;
nRet = DES_set_key((DES_cblock *)pKey, &ks1); // C_Block -> DES_cblock
if (nRet != 0) {
return ERR_API_ERROR;
}
DES_cbc_encrypt(pIn, pOut, inLen, &ks1, (void *)pIv, DES_ENCRYPT);
return HOST_CRYPTO_OK;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_init(&desCtx);
#else
desCtx = EVP_CIPHER_CTX_new();
if (desCtx == NULL) {
return HOST_CRYPTO_ERROR;
}
#endif
if (keyLen != 16)
{
// printf("Unsupported key length for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
if ((inLen % 8) != 0)
{
// printf("Input data are not block aligned for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
if (dir == HOST_ENCRYPT)
{
if (!EVP_EncryptInit_ex(&desCtx, EVP_des_ede_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(&desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(&desCtx, EVP_des_ede_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(&desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#else
if (dir == HOST_ENCRYPT)
{
if (!EVP_EncryptInit_ex(desCtx, EVP_des_ede_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(desCtx, EVP_des_ede_cbc(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#endif
else
{
// printf("Unsupported direction for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
nRet = EVP_CIPHER_CTX_cleanup(&desCtx);
#else
EVP_CIPHER_CTX_free(desCtx);
nRet = 1;
#endif
exit:
return nRet;
}
S32 HOST_3DES_ECB_Process(const U8 *pKey, U32 keyLen, const U8 *pIv,
U8 dir, const U8 *pIn, U32 inLen, U8 *pOut)
{
//DES_ecb3_encrypt();
int outLen = 0;
int nRet = ERR_API_ERROR;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX desCtx;
#else
EVP_CIPHER_CTX *desCtx = NULL;
#endif
ENSURE_OR_GO_EXIT(pIn != NULL);
ENSURE_OR_GO_EXIT(pOut != NULL);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
EVP_CIPHER_CTX_init(&desCtx);
#else
desCtx = EVP_CIPHER_CTX_new();
if (desCtx == NULL) {
return HOST_CRYPTO_ERROR;
}
#endif
if (keyLen != 16)
{
// printf("Unsupported key length for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
if ((inLen % 8) != 0)
{
// printf("Input data are not block aligned for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
if (dir == HOST_ENCRYPT)
{
if (!EVP_EncryptInit_ex(&desCtx, EVP_des_ede_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(&desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(&desCtx, EVP_des_ede_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(&desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#else
if (dir == HOST_ENCRYPT)
{
if (!EVP_EncryptInit_ex(desCtx, EVP_des_ede_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_EncryptUpdate(desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
else if (dir == HOST_DECRYPT)
{
// EVP_DecryptInit_ex returns 0 on failure and 1 upon success
if (!EVP_DecryptInit_ex(desCtx, EVP_des_ede_ecb(), NULL, pKey, pIv))
{
return HOST_CRYPTO_ERROR;
}
if (!EVP_DecryptUpdate(desCtx, pOut, &outLen, pIn, inLen))
{
return HOST_CRYPTO_ERROR;
}
}
#endif
else
{
// printf("Unsupported direction for HOST_AES_CBC_Process\n");
return ERR_API_ERROR;
}
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
nRet = EVP_CIPHER_CTX_cleanup(&desCtx);
#else
EVP_CIPHER_CTX_free(desCtx);
nRet = 1;
#endif
exit:
return nRet;
}
S32 HOST_CMAC_Get_Des(const U8 *pKey, U8 keySizeInBytes, const U8 *pMsg, U32 msgLen, U8* pMac)
{
int ret;
size_t size;
axHcCmacCtx_t *ctx;
ret = HOST_CMAC_Init_Des(&ctx, pKey, keySizeInBytes);
if (ret == HOST_CRYPTO_OK)
{
ret = CMAC_Update(ctx, pMsg, msgLen);
if (ret == HOST_CRYPTO_OK)
{
ret = CMAC_Final(ctx, pMac, &size);
}
}
if (ret != ERR_MEMORY)
{
CMAC_CTX_free(ctx);
}
return ret;
}
S32 HOST_CMAC_Init_Des(axHcCmacCtx_t **ctx, const U8 *pKey, U8 keySizeInBytes)
{
int ret = ERR_GENERAL_ERROR;
ENSURE_OR_GO_EXIT(ctx != NULL);
*ctx = CMAC_CTX_new();
if (*ctx == NULL) {
return ERR_MEMORY;
}
// CMAC_Init() returns
// 1 = success
// 0 = failure
ret = CMAC_Init(*ctx, pKey, keySizeInBytes, EVP_des_ede3_cbc(), NULL);
exit:
return ret;
}
/**
* Key wrapping according to RFC3394 (https://tools.ietf.org/html/rfc3394)
* \note Only tested with an 128 bits wrapKey. When wrapping a 128 bit key (16 byte), the resulting
* wrapped key is 24 byte long.
* @param[in] wrapKey Secret key used to wrap \p in. Also called KEK (key encryption key)
* @param[in] wrapKeyLen Length in byte of wrapKey
* @param[in,out] out IN: buffer of at least \p outLen byte; OUT: wrapped key
* @param[in,out] outLen IN: size of buffer \p out provided; OUT: actual length of wrapped key
* @param[in] in Key to be wrapped
* @param[in] inLen Length in byte of key to be wrapped
*/
S32 HOST_AesWrapKeyRFC3394(const U8 *wrapKey, U16 wrapKeyLen, U8 *out, U16 *outLen, const U8 *in, U16 inLen)
{
unsigned char *iv = NULL;
int ret = 0;
AES_KEY wctx;
int keybits = wrapKeyLen * 8;
S32 rv = ERR_GENERAL_ERROR;
ENSURE_OR_GO_EXIT(outLen != NULL);
if (*outLen < (inLen + 8))
{
return ERR_API_ERROR;
}
if (AES_set_encrypt_key(wrapKey, keybits, &wctx))
{
return ERR_CRYPTO_ENGINE_FAILED;
}
ret = AES_wrap_key(&wctx, iv, out, in, inLen);
if (ret <= 0)
{
return ERR_API_ERROR;
}
*outLen = (U16)ret;
rv = 1;
exit:
return rv; // OpenSSL success code
}
S32 HOST_AesWrapKeyRFC3394WithIV(const U8 *wrapKey, U16 wrapKeyLen, U8* iv, U8 *out, U16 *outLen, const U8 *in, U16 inLen)
{
int ret = 0;
AES_KEY wctx;
int keybits = wrapKeyLen * 8;
S32 rv = ERR_GENERAL_ERROR;
ENSURE_OR_GO_EXIT(outLen != NULL);
if (*outLen < (inLen + 8))
{
return ERR_API_ERROR;
}
if (AES_set_encrypt_key(wrapKey, keybits, &wctx))
{
return ERR_CRYPTO_ENGINE_FAILED;
}
ret = AES_wrap_key(&wctx, iv, out, in, inLen);
if (ret <= 0)
{
return ERR_API_ERROR;
}
*outLen = (U16)ret;
rv = 1;
exit:
return rv; // OpenSSL success code
}
#endif // OPENSSL