Enabled OpenSSL based signature validation functions

Added support for OpenSSL based function for the signed
image validation.

Change-Id: I45bb677276694b0bbb92c7c694c7f95b8b2c011d
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/image_verify.cpp b/image_verify.cpp
index 5b50f39..99c7d2b 100644
--- a/image_verify.cpp
+++ b/image_verify.cpp
@@ -1,4 +1,8 @@
 #include <set>
+#include <fstream>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <openssl/err.h>
 
 #include "image_verify.hpp"
 #include "config.h"
@@ -152,25 +156,35 @@
 
     // Verify the file signature with available key types
     // public keys and hash function.
+    // For any internal failure during the key/hash pair specific
+    // validation, should continue the validation with next
+    // available Key/hash pair.
     for (const auto& keyType : keyTypes)
     {
         auto keyHashPair = getKeyHashFileNames(keyType);
 
         auto hashFunc = Version::getValue(keyHashPair.first, hashFunctionTag);
 
-        // Verify manifest file signature
-        valid = verifyFile(manifestFile, manifestFileSig, keyHashPair.second,
-                           hashFunc);
-        if (valid)
+        try
         {
-            // Verify publickey file signature.
-            valid =
-                verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second, hashFunc);
+            // Verify manifest file signature
+            valid = verifyFile(manifestFile, manifestFileSig,
+                               keyHashPair.second, hashFunc);
             if (valid)
             {
-                break;
+                // Verify publickey file signature.
+                valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
+                                   hashFunc);
+                if (valid)
+                {
+                    break;
+                }
             }
         }
+        catch (const InternalFailure& e)
+        {
+            valid = false;
+        }
     }
     return valid;
 }
@@ -179,9 +193,119 @@
                            const fs::path& publicKey,
                            const std::string& hashFunc)
 {
+
+    // Check existence of the files in the system.
+    if (!(fs::exists(file) && fs::exists(sigFile)))
+    {
+        log<level::ERR>("Failed to find the Data or signature file.",
+                        entry("FILE=%s", file.c_str()));
+        elog<InternalFailure>();
+    }
+
+    // Create RSA.
+    auto publicRSA = createPublicRSA(publicKey);
+    if (publicRSA == nullptr)
+    {
+        log<level::ERR>("Failed to create RSA",
+                        entry("FILE=%s", publicKey.c_str()));
+        elog<InternalFailure>();
+    }
+
+    // Assign key to RSA.
+    EVP_PKEY_Ptr pKeyPtr(EVP_PKEY_new(), ::EVP_PKEY_free);
+    EVP_PKEY_assign_RSA(pKeyPtr.get(), publicRSA);
+
+    // Initializes a digest context.
+    EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_create(), ::EVP_MD_CTX_destroy);
+
+    // Adds all digest algorithms to the internal table
+    OpenSSL_add_all_digests();
+
+    // Create Hash structre.
+    auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
+    if (!hashStruct)
+    {
+        log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
+                        entry("HASH=%s", hashFunc.c_str()));
+        elog<InternalFailure>();
+    }
+
+    auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
+                                       nullptr, pKeyPtr.get());
+
+    if (result <= 0)
+    {
+        log<level::ERR>("Error occured during EVP_DigestVerifyInit",
+                        entry("ERRCODE=%lu", ERR_get_error()));
+        elog<InternalFailure>();
+    }
+
+    // Hash the data file and update the verification context
+    auto size = fs::file_size(file);
+    auto dataPtr = mapFile(file, size);
+
+    result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
+    if (result <= 0)
+    {
+        log<level::ERR>("Error occured during EVP_DigestVerifyUpdate",
+                        entry("ERRCODE=%lu", ERR_get_error()));
+        elog<InternalFailure>();
+    }
+
+    // Verify the data with signature.
+    size = fs::file_size(sigFile);
+    auto signature = mapFile(sigFile, size);
+
+    result = EVP_DigestVerifyFinal(
+        rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
+        size);
+
+    // Check the verification result.
+    if (result < 0)
+    {
+        log<level::ERR>("Error occured during EVP_DigestVerifyFinal",
+                        entry("ERRCODE=%lu", ERR_get_error()));
+        elog<InternalFailure>();
+    }
+
+    if (result == 0)
+    {
+        log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
+                        entry("PATH=%s", sigFile.c_str()));
+        return false;
+    }
     return true;
 }
 
+inline RSA* Signature::createPublicRSA(const fs::path& publicKey)
+{
+    RSA* rsa = nullptr;
+    auto size = fs::file_size(publicKey);
+
+    // Read public key file
+    auto data = mapFile(publicKey, size);
+
+    BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
+    if (keyBio.get() == nullptr)
+    {
+        log<level::ERR>("Failed to create new BIO Memory buffer");
+        elog<InternalFailure>();
+    }
+
+    rsa = PEM_read_bio_RSA_PUBKEY(keyBio.get(), &rsa, nullptr, nullptr);
+
+    return rsa;
+}
+
+CustomMap Signature::mapFile(const fs::path& path, size_t size)
+{
+
+    CustomFd fd(open(path.c_str(), O_RDONLY));
+
+    return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
+                     size);
+}
+
 } // namespace image
 } // namespace software
 } // namespace phosphor
diff --git a/image_verify.hpp b/image_verify.hpp
index 3e65288..5b590f4 100644
--- a/image_verify.hpp
+++ b/image_verify.hpp
@@ -1,6 +1,11 @@
 #pragma once
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
 #include <experimental/filesystem>
 #include <set>
+#include <unistd.h>
+#include <sys/mman.h>
 
 namespace phosphor
 {
@@ -17,9 +22,93 @@
 using KeyHashPathPair = std::pair<HashFilePath, PublicKeyPath>;
 using AvailableKeyTypes = std::set<Key_t>;
 
+// RAII support for openSSL functions.
+using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
+using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
+using EVP_MD_CTX_Ptr =
+    std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
+
 // BMC flash image file name list.
 const std::vector<std::string> bmcImages = {"image-kernel", "image-rofs",
                                             "image-rwfs", "image-u-boot"};
+/** @struct CustomFd
+ *
+ *  RAII wrapper for file descriptor.
+ */
+struct CustomFd
+{
+  public:
+    CustomFd() = delete;
+    CustomFd(const CustomFd&) = delete;
+    CustomFd& operator=(const CustomFd&) = delete;
+    CustomFd(CustomFd&&) = default;
+    CustomFd& operator=(CustomFd&&) = default;
+    /** @brief Saves File descriptor and uses it to do file operation
+     *
+     *  @param[in] fd - File descriptor
+     */
+    CustomFd(int fd) : fd(fd)
+    {
+    }
+
+    ~CustomFd()
+    {
+        if (fd >= 0)
+        {
+            close(fd);
+        }
+    }
+
+    int operator()() const
+    {
+        return fd;
+    }
+
+  private:
+    /** @brief File descriptor */
+    int fd = -1;
+};
+
+/** @struct CustomMap
+ *
+ *  RAII wrapper for mmap.
+ */
+struct CustomMap
+{
+  private:
+    /** @brief starting address of the map   */
+    void* addr;
+
+    /** @brief length of the mapping   */
+    size_t length;
+
+  public:
+    CustomMap() = delete;
+    CustomMap(const CustomMap&) = delete;
+    CustomMap& operator=(const CustomMap&) = delete;
+    CustomMap(CustomMap&&) = default;
+    CustomMap& operator=(CustomMap&&) = default;
+
+    /** @brief Saves starting address of the map and
+     *         and length of the file.
+     *  @param[in]  addr - Starting address of the map
+     *  @param[in]  length - length of the map
+     */
+    CustomMap(void* addr, size_t length) : addr(addr), length(length)
+    {
+    }
+
+    ~CustomMap()
+    {
+        munmap(addr, length);
+    }
+
+    void* operator()() const
+    {
+        return addr;
+    }
+};
+
 /** @class Signature
  *  @brief Contains signature verification functions.
  *  @details The software image class that contains the signature
@@ -95,7 +184,22 @@
     bool verifyFile(const fs::path& file, const fs::path& signature,
                     const fs::path& publicKey, const std::string& hashFunc);
 
-    /** @brief Directory where software images are placed */
+    /**
+     * @brief Create RSA object from the public key
+     * @param[in]  - publickey
+     * @param[out] - RSA Object.
+     */
+    inline RSA* createPublicRSA(const fs::path& publicKey);
+
+    /**
+     * @brief Memory map the  file
+     * @param[in]  - file path
+     * @param[in]  - file size
+     * @param[out] - Custom Mmap address
+     */
+    CustomMap mapFile(const fs::path& path, size_t size);
+
+    /** @brief Directory where software images are placed*/
     fs::path imageDirPath;
 
     /** @brief Path of public key and hash function files */