security: insert OpenSSL initialization call in PrivateKey::loadPkcs8

Change-Id: I71dc0c3ac71fa5db4f3be8ce51aea06e4cf7c088
Refs: #4204
diff --git a/src/security/transform/private-key.cpp b/src/security/transform/private-key.cpp
index 9cad918..c67d3d9 100644
--- a/src/security/transform/private-key.cpp
+++ b/src/security/transform/private-key.cpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
@@ -41,6 +41,18 @@
 namespace security {
 namespace transform {
 
+static void
+opensslInitAlgorithms()
+{
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+  static bool isInitialized = false;
+  if (!isInitialized) {
+    OpenSSL_add_all_algorithms();
+    isInitialized = true;
+  }
+#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+}
+
 class PrivateKey::Impl
 {
 public:
@@ -104,6 +116,7 @@
 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, const char* pw, size_t pwLen)
 {
   BOOST_ASSERT(std::strlen(pw) == pwLen);
+  opensslInitAlgorithms();
 
   detail::Bio mem(BIO_s_mem());
   BIO_write(mem.get(), buf, size);
@@ -116,14 +129,16 @@
 static inline int
 passwordCallback(char* buf, int size, int rwflag, void* u)
 {
+  BOOST_ASSERT(size >= 0);
   auto cb = reinterpret_cast<PrivateKey::PasswordCallback*>(u);
-  return (*cb)(buf, size, rwflag);
+  return (*cb)(buf, static_cast<size_t>(size), rwflag);
 }
 
 void
 PrivateKey::loadPkcs8(const uint8_t* buf, size_t size, PasswordCallback pwCallback)
 {
-  OpenSSL_add_all_algorithms();
+  opensslInitAlgorithms();
+
   detail::Bio mem(BIO_s_mem());
   BIO_write(mem.get(), buf, size);
 
@@ -246,10 +261,10 @@
 #else
   switch (EVP_PKEY_base_id(m_impl->key)) {
 #endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
-  case EVP_PKEY_RSA:
-    return rsaDecrypt(cipherText, cipherLen);
-  default:
-    BOOST_THROW_EXCEPTION(Error("Decryption is not supported for this key type"));
+    case EVP_PKEY_RSA:
+      return rsaDecrypt(cipherText, cipherLen);
+    default:
+      BOOST_THROW_EXCEPTION(Error("Decryption is not supported for this key type"));
   }
 }
 
@@ -263,8 +278,8 @@
 PrivateKey::toPkcs1() const
 {
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  opensslInitAlgorithms();
 
-  OpenSSL_add_all_algorithms();
   detail::Bio mem(BIO_s_mem());
   int ret = i2d_PrivateKey_bio(mem.get(), m_impl->key);
   if (ret != 1)
@@ -280,11 +295,10 @@
 ConstBufferPtr
 PrivateKey::toPkcs8(const char* pw, size_t pwLen) const
 {
-  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
-
   BOOST_ASSERT(std::strlen(pw) == pwLen);
+  ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  opensslInitAlgorithms();
 
-  OpenSSL_add_all_algorithms();
   detail::Bio mem(BIO_s_mem());
   int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
                                     const_cast<char*>(pw), pwLen, nullptr, nullptr);
@@ -302,8 +316,8 @@
 PrivateKey::toPkcs8(PasswordCallback pwCallback) const
 {
   ENSURE_PRIVATE_KEY_LOADED(m_impl->key);
+  opensslInitAlgorithms();
 
-  OpenSSL_add_all_algorithms();
   detail::Bio mem(BIO_s_mem());
   int ret = i2d_PKCS8PrivateKey_bio(mem.get(), m_impl->key, EVP_des_cbc(),
                                     nullptr, 0,
diff --git a/src/security/transform/private-key.hpp b/src/security/transform/private-key.hpp
index 3eb1d7a..500386b 100644
--- a/src/security/transform/private-key.hpp
+++ b/src/security/transform/private-key.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2013-2017 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
@@ -53,9 +53,10 @@
   /**
    * @brief Callback for application to handle password input
    *
-   * Password should be stored in @p buf and should not be longer than @p size.  It is
-   * recommended to ask the user to verify the passphrase if @p shouldConfirm is true, e.g., by
-   * prompting for the password twice.
+   * The password must be written to @p buf and must not be longer than @p bufSize chars.
+   * It is recommended to ask the user to verify the password if @p shouldConfirm is true,
+   * e.g., by prompting for it twice. The callback must return the number of characters
+   * in the password or 0 if an error occurred.
    */
   typedef function<int(char* buf, size_t bufSize, bool shouldConfirm)> PasswordCallback;
 
diff --git a/tests/unit-tests/security/transform/private-key.t.cpp b/tests/unit-tests/security/transform/private-key.t.cpp
index d1ab196..49a5ea1 100644
--- a/tests/unit-tests/security/transform/private-key.t.cpp
+++ b/tests/unit-tests/security/transform/private-key.t.cpp
@@ -25,7 +25,7 @@
 #include "encoding/buffer-stream.hpp"
 
 #include "boost-test.hpp"
-#include <boost/mpl/list.hpp>
+#include <boost/mpl/vector.hpp>
 
 #include <sstream>
 
@@ -158,27 +158,25 @@
   std::string publicKeyPkcs8;
 };
 
-typedef boost::mpl::list<RsaKeyTestData,
-                         EcKeyTestData> KeyTestDataSets;
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
 
-void
+static void
 checkPkcs8Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
 {
   PrivateKey sKey;
-  BOOST_REQUIRE_NO_THROW(sKey.loadPkcs8(encoding->buf(), encoding->size(),
-                                        password.c_str(), password.size()));
+  sKey.loadPkcs8(encoding->buf(), encoding->size(), password.c_str(), password.size());
   OBufferStream os;
-  BOOST_REQUIRE_NO_THROW(sKey.savePkcs1(os));
+  sKey.savePkcs1(os);
   ConstBufferPtr keyPkcs1Str = os.buf();
-  BOOST_CHECK_EQUAL_COLLECTIONS(pkcs1->begin(), pkcs1->end(), keyPkcs1Str->begin(), keyPkcs1Str->end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(pkcs1->begin(), pkcs1->end(),
+                                keyPkcs1Str->begin(), keyPkcs1Str->end());
 }
 
-void
+static void
 checkPkcs8Base64Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
 {
   OBufferStream os;
   bufferSource(*encoding) >> base64Decode() >> streamSink(os);
-
   checkPkcs8Encoding(os.buf(), password, pkcs1);
 }
 
@@ -195,7 +193,7 @@
   const uint8_t* sKeyPkcs1 = sKeyPkcs1Buf->buf();
   size_t sKeyPkcs1Len = sKeyPkcs1Buf->size();
 
-  // load key in base64 encoded pkcs1 format
+  // load key in base64-encoded pkcs1 format
   PrivateKey sKey;
   BOOST_REQUIRE_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
 
@@ -212,7 +210,7 @@
   PrivateKey sKey4;
   BOOST_REQUIRE_NO_THROW(sKey4.loadPkcs1(ss4));
 
-  // save key in base64 encoded pkcs1 format
+  // save key in base64-encoded pkcs1 format
   OBufferStream os2;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs1Base64(os2));
   ConstBufferPtr keyPkcs1Base64Str = os2.buf();
@@ -239,13 +237,13 @@
 
   std::string password("password");
   std::string wrongpw("wrongpw");
-  auto pwCallback = [&] (char* buf, size_t size, int rwflag) -> int {
+  auto pwCallback = [&password] (char* buf, size_t size, int) -> int {
+    BOOST_REQUIRE_LE(password.size(), size);
     std::copy(password.begin(), password.end(), buf);
-    return password.size();
+    return static_cast<int>(password.size());
   };
 
-
-  // load key in base64 encoded pkcs8 format
+  // load key in base64-encoded pkcs8 format
   PrivateKey sKey5;
   BOOST_REQUIRE_NO_THROW(sKey5.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
                                                password.c_str(), password.size()));
@@ -281,10 +279,11 @@
 
   // load key using wrong password, Error is expected.
   PrivateKey sKey13;
-  BOOST_REQUIRE_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, wrongpw.c_str(), wrongpw.size()),
-                      PrivateKey::Error);
+  BOOST_CHECK_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
+                                           wrongpw.c_str(), wrongpw.size()),
+                    PrivateKey::Error);
 
-  // save key in base64 encoded pkcs8 format
+  // save key in base64-encoded pkcs8 format
   OBufferStream os14;
   BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os14, password.c_str(), password.size()));
   ConstBufferPtr encoded14 = os14.buf();
@@ -377,10 +376,9 @@
                                 decryptText->begin(), decryptText->end());
 }
 
-typedef boost::mpl::list<RsaKeyParams,
-                         EcKeyParams> TestKeyParams;
+using KeyParams = boost::mpl::vector<RsaKeyParams, EcKeyParams>;
 
-BOOST_AUTO_TEST_CASE_TEMPLATE(GenerateKey, T, TestKeyParams)
+BOOST_AUTO_TEST_CASE_TEMPLATE(GenerateKey, T, KeyParams)
 {
   BOOST_REQUIRE_NO_THROW(generatePrivateKey(T()));
 
@@ -389,8 +387,7 @@
   ConstBufferPtr pKeyBits = sKey->derivePublicKey();
   pKey.loadPkcs8(pKeyBits->buf(), pKeyBits->size());
 
-  uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
-
+  const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
   OBufferStream os;
   BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
                          signerFilter(DigestAlgorithm::SHA256, *sKey) >>
@@ -401,10 +398,8 @@
   BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
                          verifierFilter(DigestAlgorithm::SHA256, pKey, sig->buf(), sig->size()) >>
                          boolSink(result));
-
   BOOST_CHECK(result);
 
-
   unique_ptr<PrivateKey> sKey2 = generatePrivateKey(T());
 
   OBufferStream os1;