Blob Blame History Raw
From 2b6b45878b1be4d77eec34ab5bc80b626995a8c5 Mon Sep 17 00:00:00 2001
From: Jeremy Barton <jbarton@microsoft.com>
Date: Fri, 19 Mar 2021 15:05:41 -0700
Subject: [PATCH 01/11] Use EVP_PKEY for RSA key generation

---
 .../Interop.EvpPkey.Rsa.cs                    | 16 ++++++++
 .../Interop.Rsa.cs                            |  3 --
 .../Security/Cryptography/RSAOpenSsl.cs       | 37 ++++---------------
 .../apibridge.c                               |  8 ++++
 .../apibridge.h                               |  1 +
 .../opensslshim.h                             | 23 ++++++++++++
 .../pal_evp_pkey_rsa.c                        | 29 +++++++++++++++
 .../pal_evp_pkey_rsa.h                        |  5 +++
 .../pal_rsa.c                                 |  5 ---
 .../pal_rsa.h                                 |  7 ----
 ...em.Security.Cryptography.Algorithms.csproj |  3 ++
 11 files changed, 93 insertions(+), 44 deletions(-)

diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
index 1f61a826a9..c28522784b 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs
@@ -10,6 +10,22 @@ internal static partial class Interop
 {
     internal static partial class Crypto
     {
+        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaGenerateKey")]
+        private static extern SafeEvpPKeyHandle CryptoNative_RsaGenerateKey(int keySize);
+
+        internal static SafeEvpPKeyHandle RsaGenerateKey(int keySize)
+        {
+            SafeEvpPKeyHandle pkey = CryptoNative_RsaGenerateKey(keySize);
+
+            if (pkey.IsInvalid)
+            {
+                pkey.Dispose();
+                throw CreateOpenSslCryptographicException();
+            }
+
+            return pkey;
+        }
+
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")]
         internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey);
 
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
index a7b85ae011..a05f020ada 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs
@@ -89,9 +89,6 @@ internal static partial class Crypto
         [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSize")]
         internal static extern int RsaSize(SafeRsaHandle rsa);
 
-        [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaGenerateKeyEx")]
-        internal static extern int RsaGenerateKeyEx(SafeRsaHandle rsa, int bits, SafeBignumHandle e);
-
         internal static bool RsaSign(int type, ReadOnlySpan<byte> m, int m_len, Span<byte> sigret, out int siglen, SafeRsaHandle rsa) =>
             RsaSign(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigret), out siglen, rsa);
 
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
index 6741d28bba..4d4a8414b3 100644
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
@@ -25,9 +25,6 @@ public sealed partial class RSAOpenSsl : RSA
     {
         private const int BitsPerByte = 8;
 
-        // 65537 (0x10001) in big-endian form
-        private static readonly byte[] s_defaultExponent = { 0x01, 0x00, 0x01 };
-
         private Lazy<SafeRsaHandle> _key;
 
         public RSAOpenSsl()
@@ -585,36 +582,18 @@ private static void CheckBoolReturn(int returnValue)
 
         private SafeRsaHandle GenerateKey()
         {
-            SafeRsaHandle key = Interop.Crypto.RsaCreate();
-            bool generated = false;
-
-            Interop.Crypto.CheckValidOpenSslHandle(key);
-
-            try
+            using (SafeEvpPKeyHandle pkey = Interop.Crypto.RsaGenerateKey(KeySize))
             {
-                using (SafeBignumHandle exponent = Interop.Crypto.CreateBignum(s_defaultExponent))
-                {
-                    // The documentation for RSA_generate_key_ex does not say that it returns only
-                    // 0 or 1, so the call marshals it back as a full Int32 and checks for a value
-                    // of 1 explicitly.
-                    int response = Interop.Crypto.RsaGenerateKeyEx(
-                        key,
-                        KeySize,
-                        exponent);
-
-                    CheckBoolReturn(response);
-                    generated = true;
-                }
-            }
-            finally
-            {
-                if (!generated)
+                SafeRsaHandle rsa = Interop.Crypto.EvpPkeyGetRsa(pkey);
+
+                if (rsa.IsInvalid)
                 {
-                    key.Dispose();
+                    rsa.Dispose();
+                    throw Interop.Crypto.CreateOpenSslCryptographicException();
                 }
-            }
 
-            return key;
+                return rsa;
+            }
         }
 
         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
index 167de7fd8e..def7198deb 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.c
@@ -728,4 +728,12 @@ void local_SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level)
     (void)ctx;
     (void)level;
 }
+
+int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2)
+{
+    // On OpenSSL 1.0.2 there aren't two different identifiers for RSA,
+    // so just pass the request on th EVP_PKEY_CTX_ctrl with the only identifier defined.
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, optype, cmd, p1, p2);
+}
+
 #endif
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
index 5f62864b24..b58611ae73 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/apibridge.h
@@ -26,6 +26,7 @@ int32_t local_RSA_meth_get_flags(const RSA_METHOD* meth);
 int32_t local_RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
 int32_t local_RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
 int32_t local_RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
+int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
 int32_t local_SSL_is_init_finished(const SSL* ssl);
 unsigned long local_SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options);
 void local_SSL_CTX_set_security_level(SSL_CTX* ctx, int32_t level);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
index ed3994926d..dff6091e9e 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/opensslshim.h
@@ -145,6 +145,7 @@ void RSA_get0_factors(const RSA* rsa, const BIGNUM** p, const BIGNUM** q);
 void RSA_get0_key(const RSA* rsa, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d);
 int32_t RSA_meth_get_flags(const RSA_METHOD* meth);
 const RSA_METHOD* RSA_PKCS1_OpenSSL(void);
+int32_t RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2);
 int32_t RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp);
 int32_t RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q);
 int32_t RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d);
@@ -170,6 +171,13 @@ const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x509);
 X509_PUBKEY* X509_get_X509_PUBKEY(const X509* x509);
 int32_t X509_get_version(const X509* x509);
 int32_t X509_up_ref(X509* x509);
+
+// Redefine EVP_PKEY_CTX_set_rsa operations to use (local_)RSA_pkey_ctx_ctrl so the path is the same
+// for 1.0-built on 1.1 as on 1.1-built on 1.1.
+#undef EVP_PKEY_CTX_set_rsa_keygen_bits
+#define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \
+    RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL)
+
 #endif
 
 #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM
@@ -341,8 +349,12 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     RENAMED_FUNCTION(EVP_MD_CTX_free, EVP_MD_CTX_destroy) \
     RENAMED_FUNCTION(EVP_MD_CTX_new, EVP_MD_CTX_create) \
     REQUIRED_FUNCTION(EVP_MD_size) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \
     REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \
+    REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \
+    REQUIRED_FUNCTION(EVP_PKEY_base_id) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \
     REQUIRED_FUNCTION(EVP_PKEY_derive_init) \
     REQUIRED_FUNCTION(EVP_PKEY_derive) \
@@ -350,6 +362,8 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \
     REQUIRED_FUNCTION(EVP_PKEY_get1_RSA) \
+    REQUIRED_FUNCTION(EVP_PKEY_keygen) \
+    REQUIRED_FUNCTION(EVP_PKEY_keygen_init) \
     REQUIRED_FUNCTION(EVP_PKEY_new) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \
     REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \
@@ -432,6 +446,7 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi
     FALLBACK_FUNCTION(RSA_get0_key) \
     FALLBACK_FUNCTION(RSA_meth_get_flags) \
     REQUIRED_FUNCTION(RSA_new) \
+    FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \
     RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \
     REQUIRED_FUNCTION(RSA_private_decrypt) \
     REQUIRED_FUNCTION(RSA_private_encrypt) \
@@ -727,8 +742,12 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_MD_CTX_free EVP_MD_CTX_free_ptr
 #define EVP_MD_CTX_new EVP_MD_CTX_new_ptr
 #define EVP_MD_size EVP_MD_size_ptr
+#define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr
 #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr
+#define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr
 #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr
+#define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr
+#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr
 #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr
 #define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr
 #define EVP_PKEY_derive EVP_PKEY_derive_ptr
@@ -736,6 +755,8 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr
 #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr
 #define EVP_PKEY_get1_RSA EVP_PKEY_get1_RSA_ptr
+#define EVP_PKEY_keygen EVP_PKEY_keygen_ptr
+#define EVP_PKEY_keygen_init EVP_PKEY_keygen_init_ptr
 #define EVP_PKEY_new EVP_PKEY_new_ptr
 #define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr
 #define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr
@@ -818,6 +839,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_get_method RSA_get_method_ptr
 #define RSA_meth_get_flags RSA_meth_get_flags_ptr
 #define RSA_new RSA_new_ptr
+#define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr
 #define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr
 #define RSA_private_decrypt RSA_private_decrypt_ptr
 #define RSA_private_encrypt RSA_private_encrypt_ptr
@@ -1026,6 +1048,7 @@ FOR_ALL_OPENSSL_FUNCTIONS
 #define RSA_set0_crt_params local_RSA_set0_crt_params
 #define RSA_set0_factors local_RSA_set0_factors
 #define RSA_set0_key local_RSA_set0_key
+#define RSA_pkey_ctx_ctrl local_RSA_pkey_ctx_ctrl
 #define SSL_CTX_set_security_level local_SSL_CTX_set_security_level
 #define SSL_is_init_finished local_SSL_is_init_finished
 #define X509_CRL_get0_nextUpdate local_X509_CRL_get0_nextUpdate
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
index e8d961dbd2..29f9238ce9 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c
@@ -4,6 +4,35 @@
 
 #include "pal_evp_pkey_rsa.h"
 
+EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize)
+{
+    EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+
+    if (ctx == NULL)
+    {
+        return NULL;
+    }
+
+    EVP_PKEY* pkey = NULL;
+    EVP_PKEY* ret = NULL;
+
+    if (EVP_PKEY_keygen_init(ctx) == 1 &&
+        EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 &&
+        EVP_PKEY_keygen(ctx, &pkey) == 1)
+    {
+        ret = pkey;
+        pkey = NULL;
+    }
+
+    if (pkey != NULL)
+    {
+        EVP_PKEY_free(pkey);
+    }
+
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
 RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey)
 {
     return EVP_PKEY_get1_RSA(pkey);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
index d8ff369670..1fda149414 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h
@@ -6,6 +6,11 @@
 #include "pal_compiler.h"
 #include "opensslshim.h"
 
+/*
+Creates an RSA key of the requested size.
+*/
+DLLEXPORT EVP_PKEY* CryptoNative_RsaGenerateKey(int32_t keySize);
+
 /*
 Shims the EVP_PKEY_get1_RSA method.
 
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
index f764815a04..080027de0e 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c
@@ -143,11 +143,6 @@ int32_t CryptoNative_RsaSize(RSA* rsa)
     return RSA_size(rsa);
 }
 
-int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e)
-{
-    return RSA_generate_key_ex(rsa, bits, e, NULL);
-}
-
 int32_t
 CryptoNative_RsaSign(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa)
 {
diff --git a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
index b85fed627f..1c0bc2df47 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h
@@ -86,13 +86,6 @@ Returns the RSA modulus size in bytes.
 */
 DLLEXPORT int32_t CryptoNative_RsaSize(RSA* rsa);
 
-/*
-Shims the RSA_generate_key_ex method.
-
-Returns 1 upon success, otherwise 0.
-*/
-DLLEXPORT int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e);
-
 /*
 Shims the RSA_sign method.
 
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
index fa63b6e2fe..6ad2b78e01 100644
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
@@ -507,6 +507,9 @@
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Ecdh.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Ecdh.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs">
+      <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
       <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
     </Compile>
-- 
2.31.1