tools: add an option -hash-alg for cert_create

This option enables the user to select the secure hash algorithm
to be used for generating the hash. It supports the following
options:
    - sha256 (default)
    - sha384
    - sha512

Change-Id: Icb093cec1b5715e248c3d1c3749a2479a7ab4b89
Signed-off-by: Qixiang Xu <qixiang.xu@arm.com>
diff --git a/tools/cert_create/include/cert.h b/tools/cert_create/include/cert.h
index 256e7af..9b4ef5a 100644
--- a/tools/cert_create/include/cert.h
+++ b/tools/cert_create/include/cert.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -48,7 +48,13 @@
 int cert_init(void);
 cert_t *cert_get_by_opt(const char *opt);
 int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value);
-int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSION) * sk);
+int cert_new(
+	int key_alg,
+	int md_alg,
+	cert_t *cert,
+	int days,
+	int ca,
+	STACK_OF(X509_EXTENSION) * sk);
 
 /* Macro to register the certificates used in the CoT */
 #define REGISTER_COT(_certs) \
diff --git a/tools/cert_create/include/key.h b/tools/cert_create/include/key.h
index 304fa61..1a253cc 100644
--- a/tools/cert_create/include/key.h
+++ b/tools/cert_create/include/key.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -30,6 +30,13 @@
 	KEY_ALG_MAX_NUM
 };
 
+/* Supported hash algorithms */
+enum{
+	HASH_ALG_SHA256,
+	HASH_ALG_SHA384,
+	HASH_ALG_SHA512,
+};
+
 /*
  * This structure contains the relevant information to create the keys
  * required to sign the certificates.
diff --git a/tools/cert_create/include/sha.h b/tools/cert_create/include/sha.h
index 6907fa1..4d07a1e 100644
--- a/tools/cert_create/include/sha.h
+++ b/tools/cert_create/include/sha.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -7,6 +7,6 @@
 #ifndef SHA_H_
 #define SHA_H_
 
-int sha_file(const char *filename, unsigned char *md);
+int sha_file(int md_alg, const char *filename, unsigned char *md);
 
 #endif /* SHA_H_ */
diff --git a/tools/cert_create/src/cert.c b/tools/cert_create/src/cert.c
index 3f0b4d3..8e8aee6 100644
--- a/tools/cert_create/src/cert.c
+++ b/tools/cert_create/src/cert.c
@@ -56,6 +56,19 @@
 
 	return ret;
 }
+const EVP_MD *get_digest(int alg)
+{
+	switch (alg) {
+	case HASH_ALG_SHA256:
+		return EVP_sha256();
+	case HASH_ALG_SHA384:
+		return EVP_sha384();
+	case HASH_ALG_SHA512:
+		return EVP_sha512();
+	default:
+		return NULL;
+	}
+}
 
 int cert_add_ext(X509 *issuer, X509 *subject, int nid, char *value)
 {
@@ -79,7 +92,13 @@
 	return 1;
 }
 
-int cert_new(int key_alg, cert_t *cert, int days, int ca, STACK_OF(X509_EXTENSION) * sk)
+int cert_new(
+	int key_alg,
+	int md_alg,
+	cert_t *cert,
+	int days,
+	int ca,
+	STACK_OF(X509_EXTENSION) * sk)
 {
 	EVP_PKEY *pkey = keys[cert->key].key;
 	cert_t *issuer_cert = &certs[cert->issuer];
@@ -118,7 +137,7 @@
 	}
 
 	/* Sign the certificate with the issuer key */
-	if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, EVP_sha256(), NULL, ikey)) {
+	if (!EVP_DigestSignInit(mdCtx, &pKeyCtx, get_digest(md_alg), NULL, ikey)) {
 		ERR_print_errors_fp(stdout);
 		goto END;
 	}
@@ -138,7 +157,7 @@
 			goto END;
 		}
 
-		if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, EVP_sha256())) {
+		if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pKeyCtx, get_digest(md_alg))) {
 			ERR_print_errors_fp(stdout);
 			goto END;
 		}
diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c
index 741242f..4abfe6d 100644
--- a/tools/cert_create/src/main.c
+++ b/tools/cert_create/src/main.c
@@ -68,6 +68,7 @@
 
 /* Global options */
 static int key_alg;
+static int hash_alg;
 static int new_keys;
 static int save_keys;
 static int print_cert;
@@ -95,6 +96,12 @@
 #endif /* OPENSSL_NO_EC */
 };
 
+static const char *hash_algs_str[] = {
+	[HASH_ALG_SHA256] = "sha256",
+	[HASH_ALG_SHA384] = "sha384",
+	[HASH_ALG_SHA512] = "sha512",
+};
+
 static void print_help(const char *cmd, const struct option *long_opt)
 {
 	int rem, i = 0;
@@ -150,6 +157,19 @@
 	return -1;
 }
 
+static int get_hash_alg(const char *hash_alg_str)
+{
+	int i;
+
+	for (i = 0 ; i < NUM_ELEM(hash_algs_str) ; i++) {
+		if (0 == strcmp(hash_alg_str, hash_algs_str[i])) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
 static void check_cmd_params(void)
 {
 	cert_t *cert;
@@ -228,6 +248,10 @@
 PKCS#1 v2.1, 'rsa_1_5' - RSA PKCS#1 v1.5, 'ecdsa'"
 	},
 	{
+		{ "hash-alg", required_argument, NULL, 's' },
+		"Hash algorithm : 'sha256' (default), 'sha384', 'sha512'"
+	},
+	{
 		{ "save-keys", no_argument, NULL, 'k' },
 		"Save key pairs into files. Filenames must be provided"
 	},
@@ -254,7 +278,8 @@
 	const struct option *cmd_opt;
 	const char *cur_opt;
 	unsigned int err_code;
-	unsigned char md[SHA256_DIGEST_LENGTH];
+	unsigned char md[SHA512_DIGEST_LENGTH];
+	unsigned int  md_len;
 	const EVP_MD *md_info;
 
 	NOTICE("CoT Generation Tool: %s\n", build_msg);
@@ -262,6 +287,7 @@
 
 	/* Set default options */
 	key_alg = KEY_ALG_RSA;
+	hash_alg = HASH_ALG_SHA256;
 
 	/* Add common command line options */
 	for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
@@ -291,7 +317,7 @@
 
 	while (1) {
 		/* getopt_long stores the option index here. */
-		c = getopt_long(argc, argv, "a:hknp", cmd_opt, &opt_idx);
+		c = getopt_long(argc, argv, "a:hknps:", cmd_opt, &opt_idx);
 
 		/* Detect the end of the options. */
 		if (c == -1) {
@@ -318,6 +344,13 @@
 		case 'p':
 			print_cert = 1;
 			break;
+		case 's':
+			hash_alg = get_hash_alg(optarg);
+			if (hash_alg < 0) {
+				ERROR("Invalid hash algorithm '%s'\n", optarg);
+				exit(1);
+			}
+			break;
 		case CMD_OPT_EXT:
 			cur_opt = cmd_opt_get_name(opt_idx);
 			ext = ext_get_by_opt(cur_opt);
@@ -343,9 +376,18 @@
 	/* Check command line arguments */
 	check_cmd_params();
 
-	/* Indicate SHA256 as image hash algorithm in the certificate
+	/* Indicate SHA as image hash algorithm in the certificate
 	 * extension */
-	md_info = EVP_sha256();
+	if (hash_alg == HASH_ALG_SHA384) {
+		md_info = EVP_sha384();
+		md_len  = SHA384_DIGEST_LENGTH;
+	} else if (hash_alg == HASH_ALG_SHA512) {
+		md_info = EVP_sha512();
+		md_len  = SHA512_DIGEST_LENGTH;
+	} else {
+		md_info = EVP_sha256();
+		md_len  = SHA256_DIGEST_LENGTH;
+	}
 
 	/* Load private keys from files (or generate new ones) */
 	for (i = 0 ; i < num_keys ; i++) {
@@ -421,14 +463,14 @@
 				if (ext->arg == NULL) {
 					if (ext->optional) {
 						/* Include a hash filled with zeros */
-						memset(md, 0x0, SHA256_DIGEST_LENGTH);
+						memset(md, 0x0, SHA512_DIGEST_LENGTH);
 					} else {
 						/* Do not include this hash in the certificate */
 						break;
 					}
 				} else {
 					/* Calculate the hash of the file */
-					if (!sha_file(ext->arg, md)) {
+					if (!sha_file(hash_alg, ext->arg, md)) {
 						ERROR("Cannot calculate hash of %s\n",
 							ext->arg);
 						exit(1);
@@ -436,7 +478,7 @@
 				}
 				CHECK_NULL(cert_ext, ext_new_hash(ext_nid,
 						EXT_CRIT, md_info, md,
-						SHA256_DIGEST_LENGTH));
+						md_len));
 				break;
 			case EXT_TYPE_PKEY:
 				CHECK_NULL(cert_ext, ext_new_key(ext_nid,
@@ -453,7 +495,7 @@
 		}
 
 		/* Create certificate. Signed with corresponding key */
-		if (cert->fn && !cert_new(key_alg, cert, VAL_DAYS, 0, sk)) {
+		if (cert->fn && !cert_new(key_alg, hash_alg, cert, VAL_DAYS, 0, sk)) {
 			ERROR("Cannot create %s\n", cert->cn);
 			exit(1);
 		}
diff --git a/tools/cert_create/src/sha.c b/tools/cert_create/src/sha.c
index 2971593..3d977fb 100644
--- a/tools/cert_create/src/sha.c
+++ b/tools/cert_create/src/sha.c
@@ -1,20 +1,21 @@
 /*
- * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <openssl/sha.h>
 #include <stdio.h>
-
 #include "debug.h"
+#include "key.h"
 
 #define BUFFER_SIZE	256
 
-int sha_file(const char *filename, unsigned char *md)
+int sha_file(int md_alg, const char *filename, unsigned char *md)
 {
 	FILE *inFile;
 	SHA256_CTX shaContext;
+	SHA512_CTX sha512Context;
 	int bytes;
 	unsigned char data[BUFFER_SIZE];
 
@@ -29,11 +30,25 @@
 		return 0;
 	}
 
-	SHA256_Init(&shaContext);
-	while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) {
-		SHA256_Update(&shaContext, data, bytes);
+	if (md_alg == HASH_ALG_SHA384) {
+		SHA384_Init(&sha512Context);
+		while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) {
+			SHA384_Update(&sha512Context, data, bytes);
+		}
+		SHA384_Final(md, &sha512Context);
+	} else if (md_alg == HASH_ALG_SHA512) {
+		SHA512_Init(&sha512Context);
+		while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) {
+			SHA512_Update(&sha512Context, data, bytes);
+		}
+		SHA512_Final(md, &sha512Context);
+	} else {
+		SHA256_Init(&shaContext);
+		while ((bytes = fread(data, 1, BUFFER_SIZE, inFile)) != 0) {
+			SHA256_Update(&shaContext, data, bytes);
+		}
+		SHA256_Final(md, &shaContext);
 	}
-	SHA256_Final(md, &shaContext);
 
 	fclose(inFile);
 	return 1;