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 <assert.h>
9 #include <ctype.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdbool.h>
15 
16 #include <openssl/conf.h>
17 
18 #include "cmd_opt.h"
19 #include "debug.h"
20 #include "encrypt.h"
21 #include "firmware_encrypted.h"
22 
23 #define NUM_ELEM(x)			((sizeof(x)) / (sizeof(x[0])))
24 #define HELP_OPT_MAX_LEN		128
25 
26 /* Global options */
27 
28 /* Info messages created in the Makefile */
29 extern const char build_msg[];
30 
31 static char *key_algs_str[] = {
32 	[KEY_ALG_GCM] = "gcm",
33 };
34 
print_help(const char * cmd,const struct option * long_opt)35 static void print_help(const char *cmd, const struct option *long_opt)
36 {
37 	int rem, i = 0;
38 	const struct option *opt;
39 	char line[HELP_OPT_MAX_LEN];
40 	char *p;
41 
42 	assert(cmd != NULL);
43 	assert(long_opt != NULL);
44 
45 	printf("\n\n");
46 	printf("The firmware encryption tool loads the binary image and\n"
47 	       "outputs encrypted binary image using an encryption key\n"
48 	       "provided as an input hex string.\n");
49 	printf("\n");
50 	printf("Usage:\n");
51 	printf("\t%s [OPTIONS]\n\n", cmd);
52 
53 	printf("Available options:\n");
54 	opt = long_opt;
55 	while (opt->name) {
56 		p = line;
57 		rem = HELP_OPT_MAX_LEN;
58 		if (isalpha(opt->val)) {
59 			/* Short format */
60 			sprintf(p, "-%c,", (char)opt->val);
61 			p += 3;
62 			rem -= 3;
63 		}
64 		snprintf(p, rem, "--%s %s", opt->name,
65 			 (opt->has_arg == required_argument) ? "<arg>" : "");
66 		printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i));
67 		opt++;
68 		i++;
69 	}
70 	printf("\n");
71 }
72 
get_key_alg(const char * key_alg_str)73 static int get_key_alg(const char *key_alg_str)
74 {
75 	int i;
76 
77 	for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) {
78 		if (strcmp(key_alg_str, key_algs_str[i]) == 0) {
79 			return i;
80 		}
81 	}
82 
83 	return -1;
84 }
85 
parse_fw_enc_status_flag(const char * arg,unsigned short * fw_enc_status)86 static void parse_fw_enc_status_flag(const char *arg,
87 				     unsigned short *fw_enc_status)
88 {
89 	unsigned long flag;
90 	char *endptr;
91 
92 	flag = strtoul(arg, &endptr, 16);
93 	if (*endptr != '\0' || flag > FW_ENC_WITH_BSSK) {
94 		ERROR("Invalid fw_enc_status flag '%s'\n", arg);
95 		exit(1);
96 	}
97 
98 	*fw_enc_status = flag & FW_ENC_STATUS_FLAG_MASK;
99 }
100 
101 /* Common command line options */
102 static const cmd_opt_t common_cmd_opt[] = {
103 	{
104 		{ "help", no_argument, NULL, 'h' },
105 		"Print this message and exit"
106 	},
107 	{
108 		{ "fw-enc-status", required_argument, NULL, 'f' },
109 		"Firmware encryption status flag (with SSK=0 or BSSK=1)."
110 	},
111 	{
112 		{ "key-alg", required_argument, NULL, 'a' },
113 		"Encryption key algorithm: 'gcm' (default)"
114 	},
115 	{
116 		{ "key", required_argument, NULL, 'k' },
117 		"Encryption key (for supported algorithm)."
118 	},
119 	{
120 		{ "nonce", required_argument, NULL, 'n' },
121 		"Nonce or Initialization Vector (for supported algorithm)."
122 	},
123 	{
124 		{ "in", required_argument, NULL, 'i' },
125 		"Input filename to be encrypted."
126 	},
127 	{
128 		{ "out", required_argument, NULL, 'o' },
129 		"Encrypted output filename."
130 	},
131 };
132 
main(int argc,char * argv[])133 int main(int argc, char *argv[])
134 {
135 	int i, key_alg, ret;
136 	int c, opt_idx = 0;
137 	const struct option *cmd_opt;
138 	char *key = NULL;
139 	char *nonce = NULL;
140 	char *in_fn = NULL;
141 	char *out_fn = NULL;
142 	unsigned short fw_enc_status = 0;
143 
144 	NOTICE("Firmware Encryption Tool: %s\n", build_msg);
145 
146 	/* Set default options */
147 	key_alg = KEY_ALG_GCM;
148 
149 	/* Add common command line options */
150 	for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
151 		cmd_opt_add(&common_cmd_opt[i]);
152 	}
153 
154 	/* Get the command line options populated during the initialization */
155 	cmd_opt = cmd_opt_get_array();
156 
157 	while (1) {
158 		/* getopt_long stores the option index here. */
159 		c = getopt_long(argc, argv, "a:f:hi:k:n:o:", cmd_opt, &opt_idx);
160 
161 		/* Detect the end of the options. */
162 		if (c == -1) {
163 			break;
164 		}
165 
166 		switch (c) {
167 		case 'a':
168 			key_alg = get_key_alg(optarg);
169 			if (key_alg < 0) {
170 				ERROR("Invalid key algorithm '%s'\n", optarg);
171 				exit(1);
172 			}
173 			break;
174 		case 'f':
175 			parse_fw_enc_status_flag(optarg, &fw_enc_status);
176 			break;
177 		case 'k':
178 			key = optarg;
179 			break;
180 		case 'i':
181 			in_fn = optarg;
182 			break;
183 		case 'o':
184 			out_fn = optarg;
185 			break;
186 		case 'n':
187 			nonce = optarg;
188 			break;
189 		case 'h':
190 			print_help(argv[0], cmd_opt);
191 			exit(0);
192 		case '?':
193 		default:
194 			print_help(argv[0], cmd_opt);
195 			exit(1);
196 		}
197 	}
198 
199 	if (!key) {
200 		ERROR("Key must not be NULL\n");
201 		exit(1);
202 	}
203 
204 	if (!nonce) {
205 		ERROR("Nonce must not be NULL\n");
206 		exit(1);
207 	}
208 
209 	if (!in_fn) {
210 		ERROR("Input filename must not be NULL\n");
211 		exit(1);
212 	}
213 
214 	if (!out_fn) {
215 		ERROR("Output filename must not be NULL\n");
216 		exit(1);
217 	}
218 
219 	ret = encrypt_file(fw_enc_status, key_alg, key, nonce, in_fn, out_fn);
220 
221 	CRYPTO_cleanup_all_ex_data();
222 
223 	return ret;
224 }
225