1 /*
2  * Copyright (c) 2019, Linaro Limited. All rights reserved.
3  * Author: Sumit Garg <sumit.garg@linaro.org>
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <firmware_encrypted.h>
9 #include <openssl/evp.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include "debug.h"
13 #include "encrypt.h"
14 
15 #define BUFFER_SIZE		256
16 #define IV_SIZE			12
17 #define IV_STRING_SIZE		24
18 #define TAG_SIZE		16
19 #define KEY_SIZE		32
20 #define KEY_STRING_SIZE		64
21 
gcm_encrypt(unsigned short fw_enc_status,char * key_string,char * nonce_string,const char * ip_name,const char * op_name)22 static int gcm_encrypt(unsigned short fw_enc_status, char *key_string,
23 		       char *nonce_string, const char *ip_name,
24 		       const char *op_name)
25 {
26 	FILE *ip_file;
27 	FILE *op_file;
28 	EVP_CIPHER_CTX *ctx;
29 	unsigned char data[BUFFER_SIZE], enc_data[BUFFER_SIZE];
30 	unsigned char key[KEY_SIZE], iv[IV_SIZE], tag[TAG_SIZE];
31 	int bytes, enc_len = 0, i, j, ret = 0;
32 	struct fw_enc_hdr header;
33 
34 	memset(&header, 0, sizeof(struct fw_enc_hdr));
35 
36 	if (strlen(key_string) != KEY_STRING_SIZE) {
37 		ERROR("Unsupported key size: %lu\n", strlen(key_string));
38 		return -1;
39 	}
40 
41 	for (i = 0, j = 0; i < KEY_SIZE; i++, j += 2) {
42 		if (sscanf(&key_string[j], "%02hhx", &key[i]) != 1) {
43 			ERROR("Incorrect key format\n");
44 			return -1;
45 		}
46 	}
47 
48 	if (strlen(nonce_string) != IV_STRING_SIZE) {
49 		ERROR("Unsupported IV size: %lu\n", strlen(nonce_string));
50 		return -1;
51 	}
52 
53 	for (i = 0, j = 0; i < IV_SIZE; i++, j += 2) {
54 		if (sscanf(&nonce_string[j], "%02hhx", &iv[i]) != 1) {
55 			ERROR("Incorrect IV format\n");
56 			return -1;
57 		}
58 	}
59 
60 	ip_file = fopen(ip_name, "rb");
61 	if (ip_file == NULL) {
62 		ERROR("Cannot read %s\n", ip_name);
63 		return -1;
64 	}
65 
66 	op_file = fopen(op_name, "wb");
67 	if (op_file == NULL) {
68 		ERROR("Cannot write %s\n", op_name);
69 		fclose(ip_file);
70 		return -1;
71 	}
72 
73 	ret = fseek(op_file, sizeof(struct fw_enc_hdr), SEEK_SET);
74 	if (ret) {
75 		ERROR("fseek failed\n");
76 		goto out_file;
77 	}
78 
79 	ctx = EVP_CIPHER_CTX_new();
80 	if (ctx == NULL) {
81 		ERROR("EVP_CIPHER_CTX_new failed\n");
82 		ret = -1;
83 		goto out_file;
84 	}
85 
86 	ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
87 	if (ret != 1) {
88 		ERROR("EVP_EncryptInit_ex failed\n");
89 		ret = -1;
90 		goto out;
91 	}
92 
93 	ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv);
94 	if (ret != 1) {
95 		ERROR("EVP_EncryptInit_ex failed\n");
96 		goto out;
97 	}
98 
99 	while ((bytes = fread(data, 1, BUFFER_SIZE, ip_file)) != 0) {
100 		ret = EVP_EncryptUpdate(ctx, enc_data, &enc_len, data, bytes);
101 		if (ret != 1) {
102 			ERROR("EVP_EncryptUpdate failed\n");
103 			ret = -1;
104 			goto out;
105 		}
106 
107 		fwrite(enc_data, 1, enc_len, op_file);
108 	}
109 
110 	ret = EVP_EncryptFinal_ex(ctx, enc_data, &enc_len);
111 	if (ret != 1) {
112 		ERROR("EVP_EncryptFinal_ex failed\n");
113 		ret = -1;
114 		goto out;
115 	}
116 
117 	ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_SIZE, tag);
118 	if (ret != 1) {
119 		ERROR("EVP_CIPHER_CTX_ctrl failed\n");
120 		ret = -1;
121 		goto out;
122 	}
123 
124 	header.magic = ENC_HEADER_MAGIC;
125 	header.flags |= fw_enc_status & FW_ENC_STATUS_FLAG_MASK;
126 	header.dec_algo = KEY_ALG_GCM;
127 	header.iv_len = IV_SIZE;
128 	header.tag_len = TAG_SIZE;
129 	memcpy(header.iv, iv, IV_SIZE);
130 	memcpy(header.tag, tag, TAG_SIZE);
131 
132 	ret = fseek(op_file, 0, SEEK_SET);
133 	if (ret) {
134 		ERROR("fseek failed\n");
135 		goto out;
136 	}
137 
138 	fwrite(&header, 1, sizeof(struct fw_enc_hdr), op_file);
139 
140 out:
141 	EVP_CIPHER_CTX_free(ctx);
142 
143 out_file:
144 	fclose(ip_file);
145 	fclose(op_file);
146 
147 	/*
148 	 * EVP_* APIs returns 1 as success but enctool considers
149 	 * 0 as success.
150 	 */
151 	if (ret == 1)
152 		ret = 0;
153 
154 	return ret;
155 }
156 
encrypt_file(unsigned short fw_enc_status,int enc_alg,char * key_string,char * nonce_string,const char * ip_name,const char * op_name)157 int encrypt_file(unsigned short fw_enc_status, int enc_alg, char *key_string,
158 		 char *nonce_string, const char *ip_name, const char *op_name)
159 {
160 	switch (enc_alg) {
161 	case KEY_ALG_GCM:
162 		return gcm_encrypt(fw_enc_status, key_string, nonce_string,
163 				   ip_name, op_name);
164 	default:
165 		return -1;
166 	}
167 }
168