lib/rsa: Use EVP_PKEY instead of RSA

Most modern OpenSSL engines have methods overridden at the EVP level rather
than at RSA level, to make these engines work properly with mkimage, the RSA
signing code needs to switch to using EVP_* APIs as much as possible.

Signed-off-by: Donald Chan <hoiho@lab126.com>
[trini: Rebase on top of keyfile changes]
Signed-off-by: Tom Rini <trini@konsulko.com>
diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
index 65c6d44..5a1583b 100644
--- a/lib/rsa/rsa-sign.c
+++ b/lib/rsa/rsa-sign.c
@@ -11,6 +11,7 @@
 #include <time.h>
 #include <u-boot/fdt-libcrypto.h>
 #include <openssl/bn.h>
+#include <openssl/ec.h>
 #include <openssl/rsa.h>
 #include <openssl/pem.h>
 #include <openssl/err.h>
@@ -52,19 +53,21 @@
  *
  * @keydir:	Directory containins the key
  * @name	Name of key file (will have a .crt extension)
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
-static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap)
+static int rsa_pem_get_pub_key(const char *keydir, const char *name, EVP_PKEY **evpp)
 {
 	char path[1024];
-	EVP_PKEY *key;
+	EVP_PKEY *key = NULL;
 	X509 *cert;
-	RSA *rsa;
 	FILE *f;
 	int ret;
 
+	if (!evpp)
+		return -EINVAL;
+
-	*rsap = NULL;
+	*evpp = NULL;
 	snprintf(path, sizeof(path), "%s/%s.crt", keydir, name);
 	f = fopen(path, "r");
 	if (!f) {
@@ -89,22 +92,12 @@
 		goto err_pubkey;
 	}
 
-	/* Convert to a RSA_style key. */
-	rsa = EVP_PKEY_get1_RSA(key);
-	if (!rsa) {
-		rsa_err("Couldn't convert to a RSA style key");
-		ret = -EINVAL;
-		goto err_rsa;
-	}
 	fclose(f);
-	EVP_PKEY_free(key);
+	*evpp = key;
 	X509_free(cert);
-	*rsap = rsa;
 
 	return 0;
 
-err_rsa:
-	EVP_PKEY_free(key);
 err_pubkey:
 	X509_free(cert);
 err_cert:
@@ -118,19 +111,20 @@
  * @keydir:	Key prefix
  * @name	Name of key
  * @engine	Engine to use
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
 static int rsa_engine_get_pub_key(const char *keydir, const char *name,
-				  ENGINE *engine, RSA **rsap)
+				  ENGINE *engine, EVP_PKEY **evpp)
 {
 	const char *engine_id;
 	char key_id[1024];
-	EVP_PKEY *key;
-	RSA *rsa;
-	int ret;
+	EVP_PKEY *key = NULL;
+
+	if (!evpp)
+		return -EINVAL;
 
-	*rsap = NULL;
+	*evpp = NULL;
 
 	engine_id = ENGINE_get_id(engine);
 
@@ -166,22 +160,9 @@
 	if (!key)
 		return rsa_err("Failure loading public key from engine");
 
-	/* Convert to a RSA_style key. */
-	rsa = EVP_PKEY_get1_RSA(key);
-	if (!rsa) {
-		rsa_err("Couldn't convert to a RSA style key");
-		ret = -EINVAL;
-		goto err_rsa;
-	}
-
-	EVP_PKEY_free(key);
-	*rsap = rsa;
+	*evpp = key;
 
 	return 0;
-
-err_rsa:
-	EVP_PKEY_free(key);
-	return ret;
 }
 
 /**
@@ -190,15 +171,15 @@
  * @keydir:	Directory containing the key (PEM file) or key prefix (engine)
  * @name	Name of key file (will have a .crt extension)
  * @engine	Engine to use
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
 static int rsa_get_pub_key(const char *keydir, const char *name,
-			   ENGINE *engine, RSA **rsap)
+			   ENGINE *engine, EVP_PKEY **evpp)
 {
 	if (engine)
-		return rsa_engine_get_pub_key(keydir, name, engine, rsap);
-	return rsa_pem_get_pub_key(keydir, name, rsap);
+		return rsa_engine_get_pub_key(keydir, name, engine, evpp);
+	return rsa_pem_get_pub_key(keydir, name, evpp);
 }
 
 /**
@@ -206,17 +187,19 @@
  *
  * @keydir:	Directory containing the key
  * @name	Name of key file (will have a .key extension)
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
 static int rsa_pem_get_priv_key(const char *keydir, const char *name,
-				const char *keyfile, RSA **rsap)
+				const char *keyfile, EVP_PKEY **evpp)
 {
-	char path[1024];
-	RSA *rsa;
-	FILE *f;
+	char path[1024] = {0};
+	FILE *f = NULL;
+
+	if (!evpp)
+		return -EINVAL;
 
-	*rsap = NULL;
+	*evpp = NULL;
 	if (keydir && name)
 		snprintf(path, sizeof(path), "%s/%s.key", keydir, name);
 	else if (keyfile)
@@ -231,14 +214,12 @@
 		return -ENOENT;
 	}
 
-	rsa = PEM_read_RSAPrivateKey(f, 0, NULL, path);
-	if (!rsa) {
+	if (!PEM_read_PrivateKey(f, evpp, NULL, path)) {
 		rsa_err("Failure reading private key");
 		fclose(f);
 		return -EPROTO;
 	}
 	fclose(f);
-	*rsap = rsa;
 
 	return 0;
 }
@@ -249,20 +230,19 @@
  * @keydir:	Key prefix
  * @name	Name of key
  * @engine	Engine to use
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
 static int rsa_engine_get_priv_key(const char *keydir, const char *name,
 				   const char *keyfile,
-				   ENGINE *engine, RSA **rsap)
+				   ENGINE *engine, EVP_PKEY **evpp)
 {
 	const char *engine_id;
 	char key_id[1024];
-	EVP_PKEY *key;
-	RSA *rsa;
-	int ret;
+	EVP_PKEY *key = NULL;
 
-	*rsap = NULL;
+	if (!evpp)
+		return -EINVAL;
 
 	engine_id = ENGINE_get_id(engine);
 
@@ -307,22 +287,9 @@
 	if (!key)
 		return rsa_err("Failure loading private key from engine");
 
-	/* Convert to a RSA_style key. */
-	rsa = EVP_PKEY_get1_RSA(key);
-	if (!rsa) {
-		rsa_err("Couldn't convert to a RSA style key");
-		ret = -EINVAL;
-		goto err_rsa;
-	}
-
-	EVP_PKEY_free(key);
-	*rsap = rsa;
+	*evpp = key;
 
 	return 0;
-
-err_rsa:
-	EVP_PKEY_free(key);
-	return ret;
 }
 
 /**
@@ -331,16 +298,16 @@
  * @keydir:	Directory containing the key (PEM file) or key prefix (engine)
  * @name	Name of key
  * @engine	Engine to use for signing
- * @rsap	Returns RSA object, or NULL on failure
- * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
+ * @evpp	Returns EVP_PKEY object, or NULL on failure
+ * @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
  */
 static int rsa_get_priv_key(const char *keydir, const char *name,
-			    const char *keyfile, ENGINE *engine, RSA **rsap)
+			    const char *keyfile, ENGINE *engine, EVP_PKEY **evpp)
 {
 	if (engine)
 		return rsa_engine_get_priv_key(keydir, name, keyfile, engine,
-					       rsap);
-	return rsa_pem_get_priv_key(keydir, name, keyfile, rsap);
+					       evpp);
+	return rsa_pem_get_priv_key(keydir, name, keyfile, evpp);
 }
 
 static int rsa_init(void)
@@ -434,12 +401,11 @@
 	}
 }
 
-static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
+static int rsa_sign_with_key(EVP_PKEY *pkey, struct padding_algo *padding_algo,
 			     struct checksum_algo *checksum_algo,
 		const struct image_region region[], int region_count,
 		uint8_t **sigp, uint *sig_size)
 {
-	EVP_PKEY *key;
 	EVP_PKEY_CTX *ckey;
 	EVP_MD_CTX *context;
 	int ret = 0;
@@ -447,16 +413,7 @@
 	uint8_t *sig;
 	int i;
 
-	key = EVP_PKEY_new();
-	if (!key)
-		return rsa_err("EVP_PKEY object creation failed");
-
-	if (!EVP_PKEY_set1_RSA(key, rsa)) {
-		ret = rsa_err("EVP key setup failed");
-		goto err_set;
-	}
-
-	size = EVP_PKEY_size(key);
+	size = EVP_PKEY_size(pkey);
 	sig = malloc(size);
 	if (!sig) {
 		fprintf(stderr, "Out of memory for signature (%zu bytes)\n",
@@ -472,7 +429,7 @@
 	}
 	EVP_MD_CTX_init(context);
 
-	ckey = EVP_PKEY_CTX_new(key, NULL);
+	ckey = EVP_PKEY_CTX_new(pkey, NULL);
 	if (!ckey) {
 		ret = rsa_err("EVP key context creation failed");
 		goto err_create;
@@ -480,7 +437,7 @@
 
 	if (EVP_DigestSignInit(context, &ckey,
 			       checksum_algo->calculate_sign(),
-			       NULL, key) <= 0) {
+			       NULL, pkey) <= 0) {
 		ret = rsa_err("Signer setup failed");
 		goto err_sign;
 	}
@@ -515,7 +472,6 @@
 		EVP_MD_CTX_reset(context);
 	#endif
 	EVP_MD_CTX_destroy(context);
-	EVP_PKEY_free(key);
 
 	debug("Got signature: %d bytes, expected %zu\n", *sig_size, size);
 	*sigp = sig;
@@ -528,8 +484,6 @@
 err_create:
 	free(sig);
 err_alloc:
-err_set:
-	EVP_PKEY_free(key);
 	return ret;
 }
 
@@ -537,7 +491,7 @@
 	     const struct image_region region[], int region_count,
 	     uint8_t **sigp, uint *sig_len)
 {
-	RSA *rsa;
+	EVP_PKEY *pkey = NULL;
 	ENGINE *e = NULL;
 	int ret;
 
@@ -552,15 +506,15 @@
 	}
 
 	ret = rsa_get_priv_key(info->keydir, info->keyname, info->keyfile,
-			       e, &rsa);
+			       e, &pkey);
 	if (ret)
 		goto err_priv;
-	ret = rsa_sign_with_key(rsa, info->padding, info->checksum, region,
+	ret = rsa_sign_with_key(pkey, info->padding, info->checksum, region,
 				region_count, sigp, sig_len);
 	if (ret)
 		goto err_sign;
 
-	RSA_free(rsa);
+	EVP_PKEY_free(pkey);
 	if (info->engine_id)
 		rsa_engine_remove(e);
 	rsa_remove();
@@ -568,7 +522,7 @@
 	return ret;
 
 err_sign:
-	RSA_free(rsa);
+	EVP_PKEY_free(pkey);
 err_priv:
 	if (info->engine_id)
 		rsa_engine_remove(e);
@@ -709,6 +663,7 @@
 	int ret;
 	int bits;
 	RSA *rsa;
+	EVP_PKEY *pkey = NULL;
 	ENGINE *e = NULL;
 
 	debug("%s: Getting verification data\n", __func__);
@@ -717,9 +672,15 @@
 		if (ret)
 			return ret;
 	}
-	ret = rsa_get_pub_key(info->keydir, info->keyname, e, &rsa);
+	ret = rsa_get_pub_key(info->keydir, info->keyname, e, &pkey);
 	if (ret)
 		goto err_get_pub_key;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
+	rsa = EVP_PKEY_get1_RSA(pkey);
+#else
+	rsa = EVP_PKEY_get0_RSA(pkey);
+#endif
 	ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
 	if (ret)
 		goto err_get_params;
@@ -789,7 +750,11 @@
 	if (ret)
 		ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
 err_get_params:
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
 	RSA_free(rsa);
+#endif
+	EVP_PKEY_free(pkey);
 err_get_pub_key:
 	if (info->engine_id)
 		rsa_engine_remove(e);