Browse Source
* changes: docs: qemu: Add instructions to boot using FIP image docs: Update docs with firmware encryption feature qemu: Support optional encryption of BL31 and BL32 images qemu: Update flash address map to keep FIP in secure FLASH0 Makefile: Add support to optionally encrypt BL31 and BL32 tools: Add firmware authenticated encryption tool TBB: Add an IO abstraction layer to load encrypted firmwares drivers: crypto: Add authenticated decryption frameworkpull/1938/head
Sandrine Bailleux
5 years ago
committed by
TrustedFirmware Code Review
33 changed files with 1541 additions and 23 deletions
@ -0,0 +1,244 @@ |
|||
/*
|
|||
* Copyright (c) 2020, Linaro Limited. All rights reserved. |
|||
* Author: Sumit Garg <sumit.garg@linaro.org> |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
#include <stdint.h> |
|||
#include <string.h> |
|||
|
|||
#include <platform_def.h> |
|||
|
|||
#include <common/bl_common.h> |
|||
#include <common/debug.h> |
|||
#include <drivers/auth/crypto_mod.h> |
|||
#include <drivers/io/io_driver.h> |
|||
#include <drivers/io/io_encrypted.h> |
|||
#include <drivers/io/io_storage.h> |
|||
#include <lib/utils.h> |
|||
#include <plat/common/platform.h> |
|||
#include <tools_share/firmware_encrypted.h> |
|||
#include <tools_share/uuid.h> |
|||
|
|||
static uintptr_t backend_dev_handle; |
|||
static uintptr_t backend_dev_spec; |
|||
static uintptr_t backend_handle; |
|||
static uintptr_t backend_image_spec; |
|||
|
|||
static io_dev_info_t enc_dev_info; |
|||
|
|||
/* Encrypted firmware driver functions */ |
|||
static int enc_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); |
|||
static int enc_file_open(io_dev_info_t *dev_info, const uintptr_t spec, |
|||
io_entity_t *entity); |
|||
static int enc_file_len(io_entity_t *entity, size_t *length); |
|||
static int enc_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, |
|||
size_t *length_read); |
|||
static int enc_file_close(io_entity_t *entity); |
|||
static int enc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params); |
|||
static int enc_dev_close(io_dev_info_t *dev_info); |
|||
|
|||
static inline int is_valid_header(struct fw_enc_hdr *header) |
|||
{ |
|||
if (header->magic == ENC_HEADER_MAGIC) |
|||
return 1; |
|||
else |
|||
return 0; |
|||
} |
|||
|
|||
static io_type_t device_type_enc(void) |
|||
{ |
|||
return IO_TYPE_ENCRYPTED; |
|||
} |
|||
|
|||
static const io_dev_connector_t enc_dev_connector = { |
|||
.dev_open = enc_dev_open |
|||
}; |
|||
|
|||
static const io_dev_funcs_t enc_dev_funcs = { |
|||
.type = device_type_enc, |
|||
.open = enc_file_open, |
|||
.seek = NULL, |
|||
.size = enc_file_len, |
|||
.read = enc_file_read, |
|||
.write = NULL, |
|||
.close = enc_file_close, |
|||
.dev_init = enc_dev_init, |
|||
.dev_close = enc_dev_close, |
|||
}; |
|||
|
|||
static int enc_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) |
|||
{ |
|||
assert(dev_info != NULL); |
|||
|
|||
enc_dev_info.funcs = &enc_dev_funcs; |
|||
*dev_info = &enc_dev_info; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int enc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params) |
|||
{ |
|||
int result; |
|||
unsigned int image_id = (unsigned int)init_params; |
|||
|
|||
/* Obtain a reference to the image by querying the platform layer */ |
|||
result = plat_get_image_source(image_id, &backend_dev_handle, |
|||
&backend_dev_spec); |
|||
if (result != 0) { |
|||
WARN("Failed to obtain reference to image id=%u (%i)\n", |
|||
image_id, result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static int enc_dev_close(io_dev_info_t *dev_info) |
|||
{ |
|||
backend_dev_handle = (uintptr_t)NULL; |
|||
backend_dev_spec = (uintptr_t)NULL; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
static int enc_file_open(io_dev_info_t *dev_info, const uintptr_t spec, |
|||
io_entity_t *entity) |
|||
{ |
|||
int result; |
|||
|
|||
assert(spec != 0); |
|||
assert(entity != NULL); |
|||
|
|||
backend_image_spec = spec; |
|||
|
|||
result = io_open(backend_dev_handle, backend_image_spec, |
|||
&backend_handle); |
|||
if (result != 0) { |
|||
WARN("Failed to open backend device (%i)\n", result); |
|||
result = -ENOENT; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static int enc_file_len(io_entity_t *entity, size_t *length) |
|||
{ |
|||
int result; |
|||
|
|||
assert(entity != NULL); |
|||
assert(length != NULL); |
|||
|
|||
result = io_size(backend_handle, length); |
|||
if (result != 0) { |
|||
WARN("Failed to read blob length (%i)\n", result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
/*
|
|||
* Encryption header is attached at the beginning of the encrypted file |
|||
* and is not considered a part of the payload. |
|||
*/ |
|||
if (*length < sizeof(struct fw_enc_hdr)) |
|||
return -EIO; |
|||
|
|||
*length -= sizeof(struct fw_enc_hdr); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static int enc_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, |
|||
size_t *length_read) |
|||
{ |
|||
int result; |
|||
struct fw_enc_hdr header; |
|||
enum fw_enc_status_t fw_enc_status; |
|||
size_t bytes_read; |
|||
uint8_t key[ENC_MAX_KEY_SIZE]; |
|||
size_t key_len = sizeof(key); |
|||
unsigned int key_flags = 0; |
|||
const io_uuid_spec_t *uuid_spec = (io_uuid_spec_t *)backend_image_spec; |
|||
|
|||
assert(entity != NULL); |
|||
assert(length_read != NULL); |
|||
|
|||
result = io_read(backend_handle, (uintptr_t)&header, sizeof(header), |
|||
&bytes_read); |
|||
if (result != 0) { |
|||
WARN("Failed to read encryption header (%i)\n", result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
if (!is_valid_header(&header)) { |
|||
WARN("Encryption header check failed.\n"); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
VERBOSE("Encryption header looks OK.\n"); |
|||
fw_enc_status = header.flags & FW_ENC_STATUS_FLAG_MASK; |
|||
|
|||
if ((header.iv_len > ENC_MAX_IV_SIZE) || |
|||
(header.tag_len > ENC_MAX_TAG_SIZE)) { |
|||
WARN("Incorrect IV or tag length\n"); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
result = io_read(backend_handle, buffer, length, &bytes_read); |
|||
if (result != 0) { |
|||
WARN("Failed to read encrypted payload (%i)\n", result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
*length_read = bytes_read; |
|||
|
|||
result = plat_get_enc_key_info(fw_enc_status, key, &key_len, &key_flags, |
|||
(uint8_t *)&uuid_spec->uuid, |
|||
sizeof(uuid_t)); |
|||
if (result != 0) { |
|||
WARN("Failed to obtain encryption key (%i)\n", result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
result = crypto_mod_auth_decrypt(header.dec_algo, |
|||
(void *)buffer, *length_read, key, |
|||
key_len, key_flags, header.iv, |
|||
header.iv_len, header.tag, |
|||
header.tag_len); |
|||
memset(key, 0, key_len); |
|||
|
|||
if (result != 0) { |
|||
ERROR("File decryption failed (%i)\n", result); |
|||
return -ENOENT; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
static int enc_file_close(io_entity_t *entity) |
|||
{ |
|||
io_close(backend_handle); |
|||
|
|||
backend_image_spec = (uintptr_t)NULL; |
|||
entity->info = 0; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* Exported functions */ |
|||
|
|||
/* Register the Encrypted Firmware driver with the IO abstraction */ |
|||
int register_io_dev_enc(const io_dev_connector_t **dev_con) |
|||
{ |
|||
int result; |
|||
|
|||
assert(dev_con != NULL); |
|||
|
|||
result = io_register_device(&enc_dev_info); |
|||
if (result == 0) |
|||
*dev_con = &enc_dev_connector; |
|||
|
|||
return result; |
|||
} |
@ -0,0 +1,15 @@ |
|||
/*
|
|||
* Copyright (c) 2020, Linaro Limited. All rights reserved. |
|||
* Author: Sumit Garg <sumit.garg@linaro.org> |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef IO_ENCRYPTED_H |
|||
#define IO_ENCRYPTED_H |
|||
|
|||
struct io_dev_connector; |
|||
|
|||
int register_io_dev_enc(const struct io_dev_connector **dev_con); |
|||
|
|||
#endif /* IO_ENCRYPTED_H */ |
@ -0,0 +1,42 @@ |
|||
/*
|
|||
* Copyright (c) 2020, Linaro Limited. All rights reserved. |
|||
* Author: Sumit Garg <sumit.garg@linaro.org> |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef FIRMWARE_ENCRYPTED_H |
|||
#define FIRMWARE_ENCRYPTED_H |
|||
|
|||
#include <stdint.h> |
|||
|
|||
/* This is used as a signature to validate the encryption header */ |
|||
#define ENC_HEADER_MAGIC 0xAA640001U |
|||
|
|||
/* Firmware encryption status flag mask */ |
|||
#define FW_ENC_STATUS_FLAG_MASK 0x1 |
|||
|
|||
/*
|
|||
* SSK: Secret Symmetric Key |
|||
* BSSK: Binding Secret Symmetric Key |
|||
*/ |
|||
enum fw_enc_status_t { |
|||
FW_ENC_WITH_SSK = 0, |
|||
FW_ENC_WITH_BSSK = 1, |
|||
}; |
|||
|
|||
#define ENC_MAX_IV_SIZE 16U |
|||
#define ENC_MAX_TAG_SIZE 16U |
|||
#define ENC_MAX_KEY_SIZE 32U |
|||
|
|||
struct fw_enc_hdr { |
|||
uint32_t magic; |
|||
uint16_t dec_algo; |
|||
uint16_t flags; |
|||
uint16_t iv_len; |
|||
uint16_t tag_len; |
|||
uint8_t iv[ENC_MAX_IV_SIZE]; |
|||
uint8_t tag[ENC_MAX_TAG_SIZE]; |
|||
}; |
|||
|
|||
#endif /* FIRMWARE_ENCRYPTED_H */ |
@ -0,0 +1,65 @@ |
|||
#
|
|||
# Copyright (c) 2019, Linaro Limited. All rights reserved.
|
|||
#
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
|||
#
|
|||
|
|||
PROJECT := encrypt_fw |
|||
V ?= 0 |
|||
BUILD_INFO ?= 1 |
|||
DEBUG := 0 |
|||
BINARY := ${PROJECT}${BIN_EXT} |
|||
OPENSSL_DIR := /usr |
|||
|
|||
OBJECTS := src/encrypt.o \
|
|||
src/cmd_opt.o \
|
|||
src/main.o |
|||
|
|||
HOSTCCFLAGS := -Wall -std=c99 |
|||
|
|||
MAKE_HELPERS_DIRECTORY := ../../make_helpers/ |
|||
include ${MAKE_HELPERS_DIRECTORY}build_macros.mk |
|||
include ${MAKE_HELPERS_DIRECTORY}build_env.mk |
|||
|
|||
ifeq (${DEBUG},1) |
|||
HOSTCCFLAGS += -g -O0 -DDEBUG -DLOG_LEVEL=40 |
|||
else |
|||
ifeq (${BUILD_INFO},1) |
|||
HOSTCCFLAGS += -O2 -DLOG_LEVEL=20 |
|||
else |
|||
HOSTCCFLAGS += -O2 -DLOG_LEVEL=10 |
|||
endif |
|||
endif |
|||
ifeq (${V},0) |
|||
Q := @ |
|||
else |
|||
Q := |
|||
endif |
|||
|
|||
# Make soft links and include from local directory otherwise wrong headers
|
|||
# could get pulled in from firmware tree.
|
|||
INC_DIR := -I ./include -I ../../include/tools_share -I ${OPENSSL_DIR}/include |
|||
LIB_DIR := -L ${OPENSSL_DIR}/lib |
|||
LIB := -lssl -lcrypto |
|||
|
|||
HOSTCC ?= gcc |
|||
|
|||
.PHONY: all clean realclean |
|||
|
|||
all: clean ${BINARY} |
|||
|
|||
${BINARY}: ${OBJECTS} Makefile |
|||
@echo " HOSTLD $@" |
|||
@echo 'const char build_msg[] = "Built : "__TIME__", "__DATE__;' | \
|
|||
${HOSTCC} -c ${HOSTCCFLAGS} -xc - -o src/build_msg.o |
|||
${Q}${HOSTCC} src/build_msg.o ${OBJECTS} ${LIB_DIR} ${LIB} -o $@ |
|||
|
|||
%.o: %.c |
|||
@echo " HOSTCC $<" |
|||
${Q}${HOSTCC} -c ${HOSTCCFLAGS} ${INC_DIR} $< -o $@ |
|||
|
|||
clean: |
|||
$(call SHELL_DELETE_ALL, src/build_msg.o ${OBJECTS}) |
|||
|
|||
realclean: clean |
|||
$(call SHELL_DELETE,${BINARY}) |
@ -0,0 +1,32 @@ |
|||
/*
|
|||
* Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
|||
* Copyright (c) 2019, Linaro Limited. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef CMD_OPT_H |
|||
#define CMD_OPT_H |
|||
|
|||
#include <getopt.h> |
|||
|
|||
#define CMD_OPT_MAX_NUM 64 |
|||
|
|||
/* Supported long command line option types */ |
|||
enum { |
|||
CMD_OPT_FW |
|||
}; |
|||
|
|||
/* Structure to define a command line option */ |
|||
typedef struct cmd_opt_s { |
|||
struct option long_opt; |
|||
const char *help_msg; |
|||
} cmd_opt_t; |
|||
|
|||
/* Exported API*/ |
|||
void cmd_opt_add(const cmd_opt_t *cmd_opt); |
|||
const struct option *cmd_opt_get_array(void); |
|||
const char *cmd_opt_get_name(int idx); |
|||
const char *cmd_opt_get_help_msg(int idx); |
|||
|
|||
#endif /* CMD_OPT_H */ |
@ -0,0 +1,59 @@ |
|||
/*
|
|||
* Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef DEBUG_H |
|||
#define DEBUG_H |
|||
|
|||
#include <stdio.h> |
|||
|
|||
/* The log output macros print output to the console. These macros produce
|
|||
* compiled log output only if the LOG_LEVEL defined in the makefile (or the |
|||
* make command line) is greater or equal than the level required for that |
|||
* type of log output. |
|||
* The format expected is the same as for printf(). For example: |
|||
* INFO("Info %s.\n", "message") -> INFO: Info message. |
|||
* WARN("Warning %s.\n", "message") -> WARNING: Warning message. |
|||
*/ |
|||
|
|||
#define LOG_LEVEL_NONE 0 |
|||
#define LOG_LEVEL_ERROR 10 |
|||
#define LOG_LEVEL_NOTICE 20 |
|||
#define LOG_LEVEL_WARNING 30 |
|||
#define LOG_LEVEL_INFO 40 |
|||
#define LOG_LEVEL_VERBOSE 50 |
|||
|
|||
|
|||
#if LOG_LEVEL >= LOG_LEVEL_NOTICE |
|||
# define NOTICE(...) printf("NOTICE: " __VA_ARGS__) |
|||
#else |
|||
# define NOTICE(...) |
|||
#endif |
|||
|
|||
#if LOG_LEVEL >= LOG_LEVEL_ERROR |
|||
# define ERROR(...) printf("ERROR: " __VA_ARGS__) |
|||
#else |
|||
# define ERROR(...) |
|||
#endif |
|||
|
|||
#if LOG_LEVEL >= LOG_LEVEL_WARNING |
|||
# define WARN(...) printf("WARNING: " __VA_ARGS__) |
|||
#else |
|||
# define WARN(...) |
|||
#endif |
|||
|
|||
#if LOG_LEVEL >= LOG_LEVEL_INFO |
|||
# define INFO(...) printf("INFO: " __VA_ARGS__) |
|||
#else |
|||
# define INFO(...) |
|||
#endif |
|||
|
|||
#if LOG_LEVEL >= LOG_LEVEL_VERBOSE |
|||
# define VERBOSE(...) printf("VERBOSE: " __VA_ARGS__) |
|||
#else |
|||
# define VERBOSE(...) |
|||
#endif |
|||
|
|||
#endif /* DEBUG_H */ |
@ -0,0 +1,19 @@ |
|||
/*
|
|||
* Copyright (c) 2019, Linaro Limited. All rights reserved. |
|||
* Author: Sumit Garg <sumit.garg@linaro.org> |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef ENCRYPT_H |
|||
#define ENCRYPT_H |
|||
|
|||
/* Supported key algorithms */ |
|||
enum { |
|||
KEY_ALG_GCM /* AES-GCM (default) */ |
|||
}; |
|||
|
|||
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); |
|||
|
|||
#endif /* ENCRYPT_H */ |
@ -0,0 +1,59 @@ |
|||
/*
|
|||
* Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <assert.h> |
|||
#include <cmd_opt.h> |
|||
#include <getopt.h> |
|||
#include <stddef.h> |
|||
#include <stdlib.h> |
|||
#include "debug.h" |
|||
|
|||
/* Command line options */ |
|||
static struct option long_opt[CMD_OPT_MAX_NUM+1]; |
|||
static const char *help_msg[CMD_OPT_MAX_NUM+1]; |
|||
static int num_reg_opt; |
|||
|
|||
void cmd_opt_add(const cmd_opt_t *cmd_opt) |
|||
{ |
|||
assert(cmd_opt != NULL); |
|||
|
|||
if (num_reg_opt >= CMD_OPT_MAX_NUM) { |
|||
ERROR("Out of memory. Please increase CMD_OPT_MAX_NUM\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
long_opt[num_reg_opt].name = cmd_opt->long_opt.name; |
|||
long_opt[num_reg_opt].has_arg = cmd_opt->long_opt.has_arg; |
|||
long_opt[num_reg_opt].flag = 0; |
|||
long_opt[num_reg_opt].val = cmd_opt->long_opt.val; |
|||
|
|||
help_msg[num_reg_opt] = cmd_opt->help_msg; |
|||
|
|||
num_reg_opt++; |
|||
} |
|||
|
|||
const struct option *cmd_opt_get_array(void) |
|||
{ |
|||
return long_opt; |
|||
} |
|||
|
|||
const char *cmd_opt_get_name(int idx) |
|||
{ |
|||
if (idx >= num_reg_opt) { |
|||
return NULL; |
|||
} |
|||
|
|||
return long_opt[idx].name; |
|||
} |
|||
|
|||
const char *cmd_opt_get_help_msg(int idx) |
|||
{ |
|||
if (idx >= num_reg_opt) { |
|||
return NULL; |
|||
} |
|||
|
|||
return help_msg[idx]; |
|||
} |
@ -0,0 +1,167 @@ |
|||
/*
|
|||
* 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; |
|||
} |
|||
} |
@ -0,0 +1,224 @@ |
|||
/*
|
|||
* Copyright (c) 2019, Linaro Limited. All rights reserved. |
|||
* Author: Sumit Garg <sumit.garg@linaro.org> |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <assert.h> |
|||
#include <ctype.h> |
|||
#include <getopt.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdbool.h> |
|||
|
|||
#include <openssl/conf.h> |
|||
|
|||
#include "cmd_opt.h" |
|||
#include "debug.h" |
|||
#include "encrypt.h" |
|||
#include "firmware_encrypted.h" |
|||
|
|||
#define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0]))) |
|||
#define HELP_OPT_MAX_LEN 128 |
|||
|
|||
/* Global options */ |
|||
|
|||
/* Info messages created in the Makefile */ |
|||
extern const char build_msg[]; |
|||
|
|||
static char *key_algs_str[] = { |
|||
[KEY_ALG_GCM] = "gcm", |
|||
}; |
|||
|
|||
static void print_help(const char *cmd, const struct option *long_opt) |
|||
{ |
|||
int rem, i = 0; |
|||
const struct option *opt; |
|||
char line[HELP_OPT_MAX_LEN]; |
|||
char *p; |
|||
|
|||
assert(cmd != NULL); |
|||
assert(long_opt != NULL); |
|||
|
|||
printf("\n\n"); |
|||
printf("The firmware encryption tool loads the binary image and\n" |
|||
"outputs encrypted binary image using an encryption key\n" |
|||
"provided as an input hex string.\n"); |
|||
printf("\n"); |
|||
printf("Usage:\n"); |
|||
printf("\t%s [OPTIONS]\n\n", cmd); |
|||
|
|||
printf("Available options:\n"); |
|||
opt = long_opt; |
|||
while (opt->name) { |
|||
p = line; |
|||
rem = HELP_OPT_MAX_LEN; |
|||
if (isalpha(opt->val)) { |
|||
/* Short format */ |
|||
sprintf(p, "-%c,", (char)opt->val); |
|||
p += 3; |
|||
rem -= 3; |
|||
} |
|||
snprintf(p, rem, "--%s %s", opt->name, |
|||
(opt->has_arg == required_argument) ? "<arg>" : ""); |
|||
printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i)); |
|||
opt++; |
|||
i++; |
|||
} |
|||
printf("\n"); |
|||
} |
|||
|
|||
static int get_key_alg(const char *key_alg_str) |
|||
{ |
|||
int i; |
|||
|
|||
for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) { |
|||
if (strcmp(key_alg_str, key_algs_str[i]) == 0) { |
|||
return i; |
|||
} |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
static void parse_fw_enc_status_flag(const char *arg, |
|||
unsigned short *fw_enc_status) |
|||
{ |
|||
unsigned long flag; |
|||
char *endptr; |
|||
|
|||
flag = strtoul(arg, &endptr, 16); |
|||
if (*endptr != '\0' || flag > FW_ENC_WITH_BSSK) { |
|||
ERROR("Invalid fw_enc_status flag '%s'\n", arg); |
|||
exit(1); |
|||
} |
|||
|
|||
*fw_enc_status = flag & FW_ENC_STATUS_FLAG_MASK; |
|||
} |
|||
|
|||
/* Common command line options */ |
|||
static const cmd_opt_t common_cmd_opt[] = { |
|||
{ |
|||
{ "help", no_argument, NULL, 'h' }, |
|||
"Print this message and exit" |
|||
}, |
|||
{ |
|||
{ "fw-enc-status", required_argument, NULL, 'f' }, |
|||
"Firmware encryption status flag (with SSK=0 or BSSK=1)." |
|||
}, |
|||
{ |
|||
{ "key-alg", required_argument, NULL, 'a' }, |
|||
"Encryption key algorithm: 'gcm' (default)" |
|||
}, |
|||
{ |
|||
{ "key", required_argument, NULL, 'k' }, |
|||
"Encryption key (for supported algorithm)." |
|||
}, |
|||
{ |
|||
{ "nonce", required_argument, NULL, 'n' }, |
|||
"Nonce or Initialization Vector (for supported algorithm)." |
|||
}, |
|||
{ |
|||
{ "in", required_argument, NULL, 'i' }, |
|||
"Input filename to be encrypted." |
|||
}, |
|||
{ |
|||
{ "out", required_argument, NULL, 'o' }, |
|||
"Encrypted output filename." |
|||
}, |
|||
}; |
|||
|
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
int i, key_alg, ret; |
|||
int c, opt_idx = 0; |
|||
const struct option *cmd_opt; |
|||
char *key = NULL; |
|||
char *nonce = NULL; |
|||
char *in_fn = NULL; |
|||
char *out_fn = NULL; |
|||
unsigned short fw_enc_status = 0; |
|||
|
|||
NOTICE("Firmware Encryption Tool: %s\n", build_msg); |
|||
|
|||
/* Set default options */ |
|||
key_alg = KEY_ALG_GCM; |
|||
|
|||
/* Add common command line options */ |
|||
for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) { |
|||
cmd_opt_add(&common_cmd_opt[i]); |
|||
} |
|||
|
|||
/* Get the command line options populated during the initialization */ |
|||
cmd_opt = cmd_opt_get_array(); |
|||
|
|||
while (1) { |
|||
/* getopt_long stores the option index here. */ |
|||
c = getopt_long(argc, argv, "a:f:hi:k:n:o:", cmd_opt, &opt_idx); |
|||
|
|||
/* Detect the end of the options. */ |
|||
if (c == -1) { |
|||
break; |
|||
} |
|||
|
|||
switch (c) { |
|||
case 'a': |
|||
key_alg = get_key_alg(optarg); |
|||
if (key_alg < 0) { |
|||
ERROR("Invalid key algorithm '%s'\n", optarg); |
|||
exit(1); |
|||
} |
|||
break; |
|||
case 'f': |
|||
parse_fw_enc_status_flag(optarg, &fw_enc_status); |
|||
break; |
|||
case 'k': |
|||
key = optarg; |
|||
break; |
|||
case 'i': |
|||
in_fn = optarg; |
|||
break; |
|||
case 'o': |
|||
out_fn = optarg; |
|||
break; |
|||
case 'n': |
|||
nonce = optarg; |
|||
break; |
|||
case 'h': |
|||
print_help(argv[0], cmd_opt); |
|||
exit(0); |
|||
case '?': |
|||
default: |
|||
print_help(argv[0], cmd_opt); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
if (!key) { |
|||
ERROR("Key must not be NULL\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (!nonce) { |
|||
ERROR("Nonce must not be NULL\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (!in_fn) { |
|||
ERROR("Input filename must not be NULL\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (!out_fn) { |
|||
ERROR("Output filename must not be NULL\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
ret = encrypt_file(fw_enc_status, key_alg, key, nonce, in_fn, out_fn); |
|||
|
|||
CRYPTO_cleanup_all_ex_data(); |
|||
|
|||
return ret; |
|||
} |
Loading…
Reference in new issue