security: reimplement RSA and EC key generation for OpenSSL 3.0

Also suppress deprecation warnings related to the RAND_METHOD API.

Refs: #5154
Change-Id: I977b902465f4e4cae663c21fbeda5ab808a6e66c
diff --git a/ndn-cxx/security/transform/private-key.cpp b/ndn-cxx/security/transform/private-key.cpp
index b2e2aad..831ab8a 100644
--- a/ndn-cxx/security/transform/private-key.cpp
+++ b/ndn-cxx/security/transform/private-key.cpp
@@ -30,7 +30,9 @@
 #include "ndn-cxx/security/key-params.hpp"
 #include "ndn-cxx/encoding/buffer-stream.hpp"
 #include "ndn-cxx/util/random.hpp"
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 #include "ndn-cxx/util/scope.hpp"
+#endif
 
 #include <boost/lexical_cast.hpp>
 #include <cstring>
@@ -417,17 +419,26 @@
 unique_ptr<PrivateKey>
 PrivateKey::generateRsaKey(uint32_t keySize)
 {
+  auto privateKey = make_unique<PrivateKey>();
+  BOOST_ASSERT(privateKey->m_impl->key == nullptr);
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+  privateKey->m_impl->key = EVP_RSA_gen(keySize);
+
+  if (privateKey->m_impl->key == nullptr)
+    NDN_THROW(Error("Failed to generate RSA key"));
+#else // OPENSSL_VERSION_NUMBER
   detail::EvpPkeyCtx kctx(EVP_PKEY_RSA);
 
   if (EVP_PKEY_keygen_init(kctx) <= 0)
-    NDN_THROW(PrivateKey::Error("Failed to initialize RSA keygen context"));
+    NDN_THROW(Error("Failed to initialize RSA keygen context"));
 
   if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, static_cast<int>(keySize)) <= 0)
-    NDN_THROW(PrivateKey::Error("Failed to set RSA key length"));
+    NDN_THROW(Error("Failed to set RSA key length"));
 
-  auto privateKey = make_unique<PrivateKey>();
   if (EVP_PKEY_keygen(kctx, &privateKey->m_impl->key) <= 0)
-    NDN_THROW(PrivateKey::Error("Failed to generate RSA key"));
+    NDN_THROW(Error("Failed to generate RSA key"));
+#endif // OPENSSL_VERSION_NUMBER
 
   return privateKey;
 }
@@ -435,6 +446,24 @@
 unique_ptr<PrivateKey>
 PrivateKey::generateEcKey(uint32_t keySize)
 {
+  auto privateKey = make_unique<PrivateKey>();
+  BOOST_ASSERT(privateKey->m_impl->key == nullptr);
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+  switch (keySize) {
+  case 224:
+  case 256:
+  case 384:
+  case 521:
+    privateKey->m_impl->key = EVP_EC_gen(("P-" + to_string(keySize)).data());
+    break;
+  default:
+    NDN_THROW(std::invalid_argument("Unsupported EC key length " + to_string(keySize)));
+  }
+
+  if (privateKey->m_impl->key == nullptr)
+    NDN_THROW(Error("Failed to generate EC key"));
+#else // OPENSSL_VERSION_NUMBER
   EC_KEY* eckey = nullptr;
   switch (keySize) {
   case 224:
@@ -461,12 +490,12 @@
     NDN_THROW(Error("Failed to generate EC key"));
   }
 
-  auto privateKey = make_unique<PrivateKey>();
   privateKey->m_impl->key = EVP_PKEY_new();
   if (privateKey->m_impl->key == nullptr)
     NDN_THROW(Error("Failed to create EVP_PKEY"));
   if (EVP_PKEY_set1_EC_KEY(privateKey->m_impl->key, eckey) != 1)
     NDN_THROW(Error("Failed to assign EC key"));
+#endif // OPENSSL_VERSION_NUMBER
 
   return privateKey;
 }
diff --git a/tests/unit/util/random.t.cpp b/tests/unit/util/random.t.cpp
index 73c3825..8875cce 100644
--- a/tests/unit/util/random.t.cpp
+++ b/tests/unit/util/random.t.cpp
@@ -46,6 +46,13 @@
   BOOST_CHECK_EQUAL(r1, r3);
 }
 
+// RAND_get_rand_method() and RAND_set_rand_method() are deprecated in OpenSSL 3.0
+// and there is no straightforward replacement. Suppress the warnings for now, but
+// we may have to drop this test case when OpenSSL removes the RAND_METHOD API.
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 // This fixture uses OpenSSL routines to set a dummy random generator that always fails
 class FailRandMethodFixture
 {
@@ -110,7 +117,7 @@
 
 BOOST_FIXTURE_TEST_CASE(Error, FailRandMethodFixture)
 {
-  std::array<uint8_t, 1024> buf;
+  std::array<uint8_t, 512> buf;
   BOOST_CHECK_THROW(random::generateSecureBytes(buf), std::runtime_error);
 }