168 lines
3.6 KiB
C
168 lines
3.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2019, Linaro Limited. All rights reserved.
|
||
|
* Author: Sumit Garg <sumit.garg@linaro.org>
|
||
|
*
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*/
|
||
|
|
||
|
#include <firmware_encrypted.h>
|
||
|
#include <openssl/evp.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include "debug.h"
|
||
|
#include "encrypt.h"
|
||
|
|
||
|
#define BUFFER_SIZE 256
|
||
|
#define IV_SIZE 12
|
||
|
#define IV_STRING_SIZE 24
|
||
|
#define TAG_SIZE 16
|
||
|
#define KEY_SIZE 32
|
||
|
#define KEY_STRING_SIZE 64
|
||
|
|
||
|
static int gcm_encrypt(unsigned short fw_enc_status, char *key_string,
|
||
|
char *nonce_string, const char *ip_name,
|
||
|
const char *op_name)
|
||
|
{
|
||
|
FILE *ip_file;
|
||
|
FILE *op_file;
|
||
|
EVP_CIPHER_CTX *ctx;
|
||
|
unsigned char data[BUFFER_SIZE], enc_data[BUFFER_SIZE];
|
||
|
unsigned char key[KEY_SIZE], iv[IV_SIZE], tag[TAG_SIZE];
|
||
|
int bytes, enc_len = 0, i, j, ret = 0;
|
||
|
struct fw_enc_hdr header;
|
||
|
|
||
|
memset(&header, 0, sizeof(struct fw_enc_hdr));
|
||
|
|
||
|
if (strlen(key_string) != KEY_STRING_SIZE) {
|
||
|
ERROR("Unsupported key size: %lu\n", strlen(key_string));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (i = 0, j = 0; i < KEY_SIZE; i++, j += 2) {
|
||
|
if (sscanf(&key_string[j], "%02hhx", &key[i]) != 1) {
|
||
|
ERROR("Incorrect key format\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strlen(nonce_string) != IV_STRING_SIZE) {
|
||
|
ERROR("Unsupported IV size: %lu\n", strlen(nonce_string));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (i = 0, j = 0; i < IV_SIZE; i++, j += 2) {
|
||
|
if (sscanf(&nonce_string[j], "%02hhx", &iv[i]) != 1) {
|
||
|
ERROR("Incorrect IV format\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ip_file = fopen(ip_name, "rb");
|
||
|
if (ip_file == NULL) {
|
||
|
ERROR("Cannot read %s\n", ip_name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
op_file = fopen(op_name, "wb");
|
||
|
if (op_file == NULL) {
|
||
|
ERROR("Cannot write %s\n", op_name);
|
||
|
fclose(ip_file);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
ret = fseek(op_file, sizeof(struct fw_enc_hdr), SEEK_SET);
|
||
|
if (ret) {
|
||
|
ERROR("fseek failed\n");
|
||
|
goto out_file;
|
||
|
}
|
||
|
|
||
|
ctx = EVP_CIPHER_CTX_new();
|
||
|
if (ctx == NULL) {
|
||
|
ERROR("EVP_CIPHER_CTX_new failed\n");
|
||
|
ret = -1;
|
||
|
goto out_file;
|
||
|
}
|
||
|
|
||
|
ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
|
||
|
if (ret != 1) {
|
||
|
ERROR("EVP_EncryptInit_ex failed\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv);
|
||
|
if (ret != 1) {
|
||
|
ERROR("EVP_EncryptInit_ex failed\n");
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
while ((bytes = fread(data, 1, BUFFER_SIZE, ip_file)) != 0) {
|
||
|
ret = EVP_EncryptUpdate(ctx, enc_data, &enc_len, data, bytes);
|
||
|
if (ret != 1) {
|
||
|
ERROR("EVP_EncryptUpdate failed\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
fwrite(enc_data, 1, enc_len, op_file);
|
||
|
}
|
||
|
|
||
|
ret = EVP_EncryptFinal_ex(ctx, enc_data, &enc_len);
|
||
|
if (ret != 1) {
|
||
|
ERROR("EVP_EncryptFinal_ex failed\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_SIZE, tag);
|
||
|
if (ret != 1) {
|
||
|
ERROR("EVP_CIPHER_CTX_ctrl failed\n");
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
header.magic = ENC_HEADER_MAGIC;
|
||
|
header.flags |= fw_enc_status & FW_ENC_STATUS_FLAG_MASK;
|
||
|
header.dec_algo = KEY_ALG_GCM;
|
||
|
header.iv_len = IV_SIZE;
|
||
|
header.tag_len = TAG_SIZE;
|
||
|
memcpy(header.iv, iv, IV_SIZE);
|
||
|
memcpy(header.tag, tag, TAG_SIZE);
|
||
|
|
||
|
ret = fseek(op_file, 0, SEEK_SET);
|
||
|
if (ret) {
|
||
|
ERROR("fseek failed\n");
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
fwrite(&header, 1, sizeof(struct fw_enc_hdr), op_file);
|
||
|
|
||
|
out:
|
||
|
EVP_CIPHER_CTX_free(ctx);
|
||
|
|
||
|
out_file:
|
||
|
fclose(ip_file);
|
||
|
fclose(op_file);
|
||
|
|
||
|
/*
|
||
|
* EVP_* APIs returns 1 as success but enctool considers
|
||
|
* 0 as success.
|
||
|
*/
|
||
|
if (ret == 1)
|
||
|
ret = 0;
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int encrypt_file(unsigned short fw_enc_status, int enc_alg, char *key_string,
|
||
|
char *nonce_string, const char *ip_name, const char *op_name)
|
||
|
{
|
||
|
switch (enc_alg) {
|
||
|
case KEY_ALG_GCM:
|
||
|
return gcm_encrypt(fw_enc_status, key_string, nonce_string,
|
||
|
ip_name, op_name);
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
}
|