diff mbox

[cbootimage,v1,7/8] Add new command line option "--sign | -n" to sign binary image

Message ID 1441228760-26042-8-git-send-email-jimmzhang@nvidia.com
State Superseded, archived
Headers show

Commit Message

jimmzhang Sept. 2, 2015, 9:19 p.m. UTC
This option allows cbootimage to run as signing utility.

Command line option "--sign" syntax:

   $ cbootimage --sign <sign.cfg> <input_image> <output_image>

Where sign.cfg is the configuration file. Keyword "RsaSign" specifies
signing details such as signing sections starting offset, length, and
the location to store signature and pubkey.

Keyword "RsaSign" syntax:

    RsaSign = <sign_offset>, <sign_length>, <signature_loc>, [modulus_loc,] Complete;

If no values are specified, default values are assigned as:
    sign_offset: 0
    sign_length: input_image_size - sign_offset
    signature_loc: 0
    modulus_loc: 0

Parameter <modulus_loc> is optional.

A sample configuration file sign.cfg:
    PkcKey = rsa_priv.txt;
    RsaSign = 0x220,, 288, 16, Complete;

Command line example:

    $ cbootimage --sign sign.cfg image.bin image.bin-signed

Signed-off-by: Jimmy Zhang <jimmzhang@nvidia.com>
---
 src/cbootimage.c  |  29 ++++++-
 src/cbootimage.h  |  12 +++
 src/crypto.c      |   8 +-
 src/crypto.h      |   3 +
 src/data_layout.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/data_layout.h |   9 ++-
 src/parse.c       |  63 +++++++++++++++
 src/parse.h       |   1 +
 8 files changed, 353 insertions(+), 4 deletions(-)

Comments

Stephen Warren Sept. 21, 2015, 10:13 p.m. UTC | #1
On 09/02/2015 03:19 PM, Jimmy Zhang wrote:
> This option allows cbootimage to run as signing utility.

This seems rather unrelated to cbootimage's purpose. Wouldn't it be 
better to build a separate signing utility to do all the crypto? Can't 
the "openssl" application do any of this already?
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
jimmzhang Sept. 22, 2015, 12:36 a.m. UTC | #2
I will test again. If openssl can do the work, there is no need for patch 3 to 7.

> -----Original Message-----
> From: Stephen Warren [mailto:swarren@wwwdotorg.org]
> Sent: Monday, September 21, 2015 3:13 PM
> To: Jimmy Zhang
> Cc: Allen Martin; Stephen Warren; linux-tegra@vger.kernel.org
> Subject: Re: [cbootimage PATCH v1 7/8] Add new command line option "--
> sign | -n" to sign binary image
> 
> On 09/02/2015 03:19 PM, Jimmy Zhang wrote:
> > This option allows cbootimage to run as signing utility.
> 
> This seems rather unrelated to cbootimage's purpose. Wouldn't it be better
> to build a separate signing utility to do all the crypto? Can't the "openssl"
> application do any of this already?
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/src/cbootimage.c b/src/cbootimage.c
index 9b696519377a..4ff4ce52a490 100644
--- a/src/cbootimage.c
+++ b/src/cbootimage.c
@@ -50,6 +50,7 @@  struct option cbootcmd[] = {
 	{"odmdata", 1, NULL, 'o'},
 	{"soc", 1, NULL, 's'},
 	{"update", 0, NULL, 'u'},
+	{"sign", 0, NULL, 'n'},
 	{0, 0, 0, 0},
 };
 
@@ -80,9 +81,13 @@  usage(void)
 	printf("    -u|--update           Copy input image data and update bct\n");
 	printf("                          configs into new image file.\n");
 	printf("                          This feature is only for tegra114/124/210.\n");
+	printf("    -n|--sign             Sign input image data on specified\n");
+	printf("			  offset and length. Then save image\n");
+	printf("			  along with signature to output file\n");
 	printf("    configfile            File with configuration information\n");
 	printf("    inputimage            Input image name. This is required\n");
-	printf("                          if -u|--update option is used.\n");
+	printf("                          if either -u|--update option or \n");
+	printf("                                    -n|--sign option is used.\n");
 	printf("    outputimage           Output image name\n");
 }
 
@@ -93,7 +98,7 @@  process_command_line(int argc, char *argv[], build_image_context *context)
 
 	context->generate_bct = 0;
 
-	while ((c = getopt_long(argc, argv, "hdg:t:o:s:u", cbootcmd, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "hdg:t:o:s:un", cbootcmd, NULL)) != -1) {
 		switch (c) {
 		case 'h':
 			help_only = 1;
@@ -146,6 +151,10 @@  process_command_line(int argc, char *argv[], build_image_context *context)
 			context->update_image = 1;
 			num_filenames = 3;
 			break;
+		case 'n':
+			context->sign_image = 1;
+			num_filenames = 3;
+			break;
 		}
 	}
 
@@ -180,6 +189,12 @@  process_command_line(int argc, char *argv[], build_image_context *context)
 		context->input_image_filename = argv[optind++];
 	}
 
+	/* Record the input image filename if signing image */
+	if (context->sign_image)
+	{
+		context->input_image_filename = argv[optind++];
+	}
+
 	/* Record the output filename */
 	context->output_image_filename = argv[optind++];
 
@@ -279,6 +294,16 @@  main(int argc, char *argv[])
 			context.output_image_filename);
 		goto fail;
 	}
+	else if (context.sign_image) {
+		process_config_file(&context, 1);
+		if (save_rsa_signed_image(&context))
+			printf("Error writing image file %s.\n",
+				context.output_image_filename);
+		else
+			printf("Image file %s has been successfully generated!\n",
+				context.output_image_filename);
+		goto fail;
+	}
 	/* If we aren't generating the bct, read in config file */
 	else if (context.generate_bct == 0)
 		process_config_file(&context, 1);
diff --git a/src/cbootimage.h b/src/cbootimage.h
index afb72741c8d8..a8ed07ce3e4e 100644
--- a/src/cbootimage.h
+++ b/src/cbootimage.h
@@ -54,6 +54,12 @@ 
 
 #define NVBOOT_CONFIG_TABLE_SIZE_MAX (10 * 1024)
 
+#define INVALID_VALUE	(0x01 << 31)
+#define DATA_BLOCK_SIZE 0x8000
+
+#define is_overlapping(s1, l1, s2, l2)	\
+		(((s1 + l1) > s2) && (s1 < (s2 + l2)))
+
 /*
  * Enumerations
  */
@@ -128,6 +134,12 @@  typedef struct build_image_context_rec
 	u_int8_t secure_jtag_control; /* The flag for enabling jtag control */
 	u_int32_t secure_debug_control; /* The flag for enabling jtag control */
 	u_int8_t update_image; /* The flag for updating image */
+	u_int8_t sign_image; /* The flag for sign image */
+	u_int32_t sign_offset;
+	u_int32_t sign_length;
+	u_int32_t signature_offset;
+	u_int32_t modulus_offset;
+	void *rsa_signature;
 } build_image_context;
 
 /* Function prototypes */
diff --git a/src/crypto.c b/src/crypto.c
index c88573208ec6..6dadda10030c 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -258,7 +258,8 @@  sign_data_block(u_int8_t *source,
 				signature);
 }
 
-static u_int8_t *pkc_get_pubkey(void *key)
+u_int8_t
+*pkc_get_pubkey(void *key)
 {
 	NvTegraPkcKey *pPkcKey = (NvTegraPkcKey *)key;
 	return (u_int8_t *)pPkcKey->Modulus.Number;
@@ -273,6 +274,11 @@  pkc_sign_buffer(void *key, u_int8_t *buffer, u_int32_t size, u_int8_t **pSign)
 
 	NvTegraPkcKey *pPkcKey = (NvTegraPkcKey *)key;
 
+	if (pPkcKey == NULL) {
+		printf("Error: No valid pkc keys found\n");
+		return -ENODATA;
+	}
+
 	/* TODO: define constant for ssk.len */
 	ssk.len = (UINT)pPkcKey->Modulus.Digits; /* length in digits of modulus in term of 32 bits */
 	ssk.modulus = malloc(NBYTE(ssk.len));
diff --git a/src/crypto.h b/src/crypto.h
index 53a0d20f3441..c8ea0b66efaf 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -94,6 +94,9 @@  int pkckey_set(u_int8_t *key_buffer,
 		void **pkckey,
 		int save);
 
+u_int8_t
+*pkc_get_pubkey(void *key);
+
 int
 pkc_sign_buffer(void *key,
 		u_int8_t *buffer,
diff --git a/src/data_layout.c b/src/data_layout.c
index ce3739ac67d9..050f2bb9d2d1 100644
--- a/src/data_layout.c
+++ b/src/data_layout.c
@@ -1134,3 +1134,235 @@  int resign_bl(build_image_context *context)
 	free (image);
 	return ret;
 }
+
+int rsa_sign_image(build_image_context *context)
+{
+	int ret;
+	struct stat stats;
+	u_int8_t   *image = NULL;
+	u_int32_t  image_actual_size; /* In bytes */
+	u_int8_t   *sign;
+
+	if (stat(context->input_image_filename, &stats) != 0) {
+		printf("Error: Unable to query info on input file %s\n",
+			context->input_image_filename);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (enable_debug) {
+		printf("       image size %#x\n"
+			"       sign_offset %#x, sign_length %#x\n"
+			"       signature location %#x\n",
+				(unsigned int)stats.st_size,
+				context->sign_offset,
+				context->sign_length,
+				context->signature_offset);
+
+		if (context->modulus_offset != INVALID_VALUE)
+			printf("       rsa key modulus location %#x\n",
+				context->modulus_offset);
+	}
+
+	/* check signing section and signature location */
+	if (((context->sign_offset + context->sign_length) > stats.st_size) ||
+	    ((context->signature_offset + RSA_KEY_BYTE_SIZE) >
+		 stats.st_size)) {
+		printf("Error: Incorrect parameters are specified:\n"
+			"       image size %#x\n"
+			"       sign_offset %#x, sign_length %#x\n"
+			"       signature location %#x\n",
+				(unsigned int)stats.st_size,
+				context->sign_offset,
+				context->sign_length,
+				context->signature_offset);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/* check modulus location if specified */
+	if ((context->modulus_offset != INVALID_VALUE) &&
+	    ((context->modulus_offset + RSA_KEY_BYTE_SIZE) > stats.st_size)) {
+		printf("Error: Incorrect modulus location specified: "
+				"%#x in file %s\n",
+				context->modulus_offset,
+				context->input_image_filename);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/* update sign_length if default value is given */
+	if (context->sign_length == 0)
+		context->sign_length = stats.st_size - context->sign_offset;
+
+	/* check overlapping between signing section and signature location */
+	if (is_overlapping(context->signature_offset, RSA_KEY_BYTE_SIZE,
+				context->sign_offset, context->sign_length)) {
+		printf("Error: signature overlaps signed sections: "
+			"signed offset %#x length %#x, signature location %#x\n",
+			context->sign_offset,
+			context->sign_length,
+			context->signature_offset);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/*
+	 * check overlapping between modulus location and signature location
+	 * and overlapping between modulus location and signing section
+	 */
+	if (context->modulus_offset != INVALID_VALUE) {
+		if (is_overlapping(context->modulus_offset, RSA_KEY_BYTE_SIZE,
+			context->signature_offset, RSA_KEY_BYTE_SIZE)) {
+			printf("Error: modulus overlaps signature: "
+				"signature location %#x length %#x, modulus "
+				"location %#x length %#x\n",
+			context->signature_offset, RSA_KEY_BYTE_SIZE,
+			context->modulus_offset, RSA_KEY_BYTE_SIZE);
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		if (is_overlapping(context->modulus_offset, RSA_KEY_BYTE_SIZE,
+				context->sign_offset, context->sign_length)) {
+			printf("Error: modulus overlaps signed sections: "
+				"signed offset %#x length %#x, modulus "
+				"location %#x length %#x\n",
+			context->sign_offset,
+			context->sign_length,
+			context->modulus_offset, RSA_KEY_BYTE_SIZE);
+			ret = -EINVAL;
+			goto fail;
+		}
+	}
+
+	ret = read_from_image(context->input_image_filename,
+				context->sign_offset,
+				context->sign_length,
+				&image, &image_actual_size, file_type_blocks);
+
+	if (ret || (image_actual_size != context->sign_length)) {
+		printf("Error reading image file %s: offset %#x, length %#x\n",
+			context->input_image_filename,
+			context->sign_offset,
+			context->sign_length);
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* sign image */
+	if ((ret = pkc_sign_buffer(context->pkckey,
+					image,
+					image_actual_size,
+					&sign)) != 0)
+		goto fail;
+
+	/* return signature location */
+	context->rsa_signature = sign;
+
+ fail:
+	if (image)
+		free(image);
+
+	return ret;
+}
+
+/*
+ * To write output image:
+ *   Loop over all blocks:
+ *	read in a block (adjust size if necessary)
+ *	if signature section is in, fill in signature
+ *	save data block
+ * 	then loop
+ *
+ * @param context		The main context pointer
+ * @return 0 for success
+ */
+int
+save_rsa_signed_image(build_image_context *context)
+{
+	int ret = 0;
+	u_int32_t offset = 0, req_size, actual_size;
+	u_int8_t *data_block;
+	struct stat stats;
+
+	if (stat(context->input_image_filename, &stats) != 0) {
+		printf("Error: Unable to query info on input file path %s\n",
+			context->input_image_filename);
+		ret = -1;
+		goto fail;
+	}
+
+	while (stats.st_size > offset) {
+		if ((stats.st_size - offset) > DATA_BLOCK_SIZE)
+			req_size = DATA_BLOCK_SIZE;
+		else
+			req_size = stats.st_size - offset;
+
+		/* make sure signature's completion */
+		if (((offset + req_size) > context->signature_offset) &&
+			((offset + req_size) <
+			(context->signature_offset + RSA_KEY_BYTE_SIZE))) {
+			req_size = context->signature_offset + RSA_KEY_BYTE_SIZE
+					- offset;
+		}
+
+		/* if modulus is needed, make sure mudulus's completion */
+		if ((context->modulus_offset != INVALID_VALUE) &&
+		    (((offset + req_size) > context->modulus_offset) &&
+			((offset + req_size) <
+			(context->modulus_offset + RSA_KEY_BYTE_SIZE)))) {
+			req_size = context->modulus_offset + RSA_KEY_BYTE_SIZE
+					- offset;
+		}
+
+		/* Read a block of data into memory */
+		ret = read_from_image(context->input_image_filename,
+					offset,
+					req_size,
+					&data_block,
+					&actual_size,
+					file_type_blocks);
+		if (ret || (req_size != actual_size)) {
+			printf("Error reading image file %s.\n",
+				context->input_image_filename);
+			ret = -1;
+			goto fail;
+		}
+
+		/* fill in signature section */
+		if ((offset <= context->signature_offset) &&
+			((offset + req_size) >=
+			(context->signature_offset + RSA_KEY_BYTE_SIZE))) {
+			memcpy(data_block + (context->signature_offset - offset),
+				context->rsa_signature,
+				RSA_KEY_BYTE_SIZE);
+		}
+
+		/* if modulus is needed, fill in mudulus section */
+		if ((context->modulus_offset != INVALID_VALUE) &&
+		    ((offset <= context->modulus_offset) &&
+			((offset + req_size) >=
+			(context->modulus_offset + RSA_KEY_BYTE_SIZE)))) {
+			memcpy(data_block + (context->modulus_offset - offset),
+				pkc_get_pubkey(context->pkckey),
+				RSA_KEY_BYTE_SIZE);
+		}
+
+		/* Write the block of data to file */
+		if (actual_size != write_data_block(context->raw_file, offset,
+						 actual_size, data_block)) {
+			printf("Error writing image file %s.\n",
+				context->output_image_filename);
+			goto fail;
+		}
+
+		offset += actual_size;
+		free(data_block);
+		data_block = NULL;
+	}
+ fail:
+	if (data_block)
+		free(data_block);
+	return ret;
+}
diff --git a/src/data_layout.h b/src/data_layout.h
index 3b2cfb58f3e9..eb38bfab27b8 100644
--- a/src/data_layout.h
+++ b/src/data_layout.h
@@ -64,5 +64,12 @@  get_bct_size_from_image(build_image_context *context);
 int
 begin_update(build_image_context *context);
 
-int resign_bl(build_image_context *context);
+int
+resign_bl(build_image_context *context);
+
+int
+rsa_sign_image(build_image_context *context);
+
+int
+save_rsa_signed_image(build_image_context *context);
 #endif /* #ifndef INCLUDED_DATA_LAYOUT_H */
diff --git a/src/parse.c b/src/parse.c
index d37a442030c0..d21387943f28 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -76,6 +76,8 @@  static int
 parse_bct_file(build_image_context *context, parse_token token, char *rest);
 static int
 parse_pkckey_file(build_image_context *context, parse_token token, char *rest);
+static int
+parse_rsa_sign_image(build_image_context *context, parse_token token, char *rest);
 static char
 *parse_end_state(char *str, char *uname, int chars_remaining);
 static int
@@ -96,6 +98,7 @@  static parse_item parse_simple_items[] =
 	{ "Redundancy=",    token_redundancy,		parse_value_u32 },
 	{ "Bctcopy=",       token_bct_copy,		parse_value_u32 },
 	{ "PkcKey=",        token_pkckey_file,		parse_pkckey_file},
+	{ "RsaSign=",       token_rsa_sign_image,	parse_rsa_sign_image},
 	{ "MtsPreboot=",    token_mts_preboot,		parse_mts_image},
 	{ "Mts=",           token_mts,			parse_mts_image},
 	{ "Version=",       token_version,		parse_value_u32 },
@@ -437,6 +440,66 @@  static int parse_bootloader(build_image_context *context,
 }
 
 /*
+ * Parse the given string and find signing offset, length and signature location
+ *
+ * @param context	The main context pointer
+ * @param token  	The parse token value
+ * @param rest   	String to parse
+ * @return 0 and 1 for success and failure
+ */
+static int parse_rsa_sign_image(build_image_context *context,
+			parse_token token,
+			char *rest)
+{
+	char e_state[MAX_STR_LEN];
+
+	assert(context != NULL);
+	assert(rest != NULL);
+
+	/* Parse the sign section starting address. */
+	rest = parse_u32(rest, &context->sign_offset);
+	if (rest == NULL)
+		return 1;
+
+	PARSE_COMMA(1);
+
+	/* Parse the sign section length. */
+	rest = parse_u32(rest, &context->sign_length);
+	if (rest == NULL)
+		return 1;
+
+	PARSE_COMMA(1);
+
+	/* Parse the signature store location. */
+	rest = parse_u32(rest, &context->signature_offset);
+	if (rest == NULL)
+		return 1;
+
+	PARSE_COMMA(1);
+
+	/* parse the optional modulus location */
+	rest = parse_u32(rest, &context->modulus_offset);
+	if (rest == NULL)
+		return 1;
+
+	if (*rest == ',') {
+		/* modulus location is specified */
+		++rest;
+	} else
+		context->modulus_offset = INVALID_VALUE;
+
+	/* Parse the end state. */
+	rest = parse_end_state(rest, e_state, MAX_STR_LEN);
+	if (rest == NULL)
+		return 1;
+	if (strncmp(e_state, "Complete", strlen("Complete")))
+		return 1;
+
+	/* Parsing has finished - sign on specified image sections */
+	return rsa_sign_image(context);
+}
+
+/*
  * Parse the given string and find the MTS file name, load address and
  * entry point information then call set_mts_image function.
  *
diff --git a/src/parse.h b/src/parse.h
index 26618edf9c87..133d8ab4ae29 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -42,6 +42,7 @@  typedef enum
 	token_attribute,
 	token_bootloader,
 	token_pkckey_file,
+	token_rsa_sign_image,
 	token_mts_preboot,
 	token_mts,
 	token_block_size,