Add support for signature verification routines

Enabled high level logic flow for the signed image
signature validation routines.

Includes reading hash type, key type from Manifest file.

Change-Id: I9b0213042bb15882f351e7937fd17fb0a3e9fb33
Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
diff --git a/configure.ac b/configure.ac
index d89cee5..6caf4b9 100755
--- a/configure.ac
+++ b/configure.ac
@@ -108,6 +108,22 @@
 AS_IF([test "x$MANIFEST_FILE_NAME" == "x"], [MANIFEST_FILE_NAME="MANIFEST"])
 AC_DEFINE_UNQUOTED([MANIFEST_FILE_NAME], ["$MANIFEST_FILE_NAME"], [The name of the MANIFEST file])
 
+AC_ARG_VAR(PUBLICKEY_FILE_NAME, [The name of the public key file])
+AS_IF([test "x$PUBLICKEY_FILE_NAME" == "x"], [PUBLICKEY_FILE_NAME="publickey"])
+AC_DEFINE_UNQUOTED([PUBLICKEY_FILE_NAME], ["$PUBLICKEY_FILE_NAME"], [The name of the public key file])
+
+AC_ARG_VAR(HASH_FILE_NAME, [Hash file name])
+AS_IF([test "x$HASH_FILE_NAME" == "x"], [HASH_FILE_NAME="hashfunc"])
+AC_DEFINE_UNQUOTED([HASH_FILE_NAME], ["$HASH_FILE_NAME"], [The name of the hash file])
+
+AC_ARG_VAR(SIGNED_IMAGE_CONF_PATH, [Path of public key and hash function files])
+AS_IF([test "x$SIGNED_IMAGE_CONF_PATH" == "x"], [SIGNED_IMAGE_CONF_PATH="/etc/activationdata/"])
+AC_DEFINE_UNQUOTED([SIGNED_IMAGE_CONF_PATH], ["$SIGNED_IMAGE_CONF_PATH"], [Path of public key and hash function files])
+
+AC_ARG_VAR(SIGNATURE_FILE_EXT, [The extension of the Signature file])
+AS_IF([test "x$SIGNATURE_FILE_EXT" == "x"], [SIGNATURE_FILE_EXT=".sig"])
+AC_DEFINE_UNQUOTED([SIGNATURE_FILE_EXT], ["$SIGNATURE_FILE_EXT"], [The extension of the Signature file])
+
 AC_ARG_VAR(ACTIVE_BMC_MAX_ALLOWED, [The maximum allowed active BMC versions])
 AS_IF([test "x$ACTIVE_BMC_MAX_ALLOWED" == "x"], [ACTIVE_BMC_MAX_ALLOWED=2])
 AC_DEFINE_UNQUOTED([ACTIVE_BMC_MAX_ALLOWED], [$ACTIVE_BMC_MAX_ALLOWED],
diff --git a/image_verify.cpp b/image_verify.cpp
index be5536c..5b50f39 100644
--- a/image_verify.cpp
+++ b/image_verify.cpp
@@ -1,4 +1,13 @@
+#include <set>
+
 #include "image_verify.hpp"
+#include "config.h"
+#include "version.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
 
 namespace phosphor
 {
@@ -7,8 +16,169 @@
 namespace image
 {
 
+using namespace phosphor::logging;
+using namespace phosphor::software::manager;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+constexpr auto keyTypeTag = "KeyType";
+constexpr auto hashFunctionTag = "HashType";
+
+Signature::Signature(const fs::path& imageDirPath,
+                     const fs::path& signedConfPath) :
+    imageDirPath(imageDirPath),
+    signedConfPath(signedConfPath)
+{
+    fs::path file(imageDirPath / MANIFEST_FILE_NAME);
+
+    keyType = Version::getValue(file, keyTypeTag);
+    hashType = Version::getValue(file, hashFunctionTag);
+}
+
+AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
+{
+    AvailableKeyTypes keyTypes{};
+
+    // Find the path of all the files
+    if (!fs::is_directory(signedConfPath))
+    {
+        log<level::ERR>("Signed configuration path not found in the system");
+        elog<InternalFailure>();
+    }
+
+    // Look for all the hash and public key file names get the key value
+    // For example:
+    // /etc/activationdata/OpenBMC/publickey
+    // /etc/activationdata/OpenBMC/hashfunc
+    // /etc/activationdata/GA/publickey
+    // /etc/activationdata/GA/hashfunc
+    // Set will have OpenBMC, GA
+
+    for (const auto& p : fs::recursive_directory_iterator(signedConfPath))
+    {
+        if ((p.path().filename() == HASH_FILE_NAME) ||
+            (p.path().filename() == PUBLICKEY_FILE_NAME))
+        {
+            // extract the key types
+            // /etc/activationdata/OpenBMC/  -> get OpenBMC from the path
+            auto key = p.path().parent_path();
+            keyTypes.insert(key.filename());
+        }
+    }
+
+    return keyTypes;
+}
+
+inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
+{
+    fs::path hashpath(signedConfPath / key / HASH_FILE_NAME);
+    fs::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
+
+    return std::make_pair(std::move(hashpath), std::move(keyPath));
+}
+
 bool Signature::verify()
 {
+    try
+    {
+        // Verify the MANIFEST and publickey file using available
+        // public keys and hash on the system.
+        if (false == systemLevelVerify())
+        {
+            log<level::ERR>("System level Signature Validation failed");
+            return false;
+        }
+
+        // image specfic publickey file name.
+        fs::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
+
+        // Validate the BMC image files.
+        for (const auto& bmcImage : bmcImages)
+        {
+            // Build Image File name
+            fs::path file(imageDirPath);
+            file /= bmcImage;
+
+            // Build Signature File name
+            fs::path sigFile(file);
+            sigFile.replace_extension(SIGNATURE_FILE_EXT);
+
+            // Verify the signature.
+            auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
+            if (valid == false)
+            {
+                log<level::ERR>("Image file Signature Validation failed",
+                                entry("IMAGE=%s", bmcImage.c_str()));
+                return false;
+            }
+        }
+
+        log<level::DEBUG>("Sucessfully completed Signature vaildation.");
+
+        return true;
+    }
+    catch (const InternalFailure& e)
+    {
+        return false;
+    }
+    catch (const std::exception& e)
+    {
+        log<level::ERR>(e.what());
+        return false;
+    }
+}
+
+bool Signature::systemLevelVerify()
+{
+    // Get available key types from the system.
+    auto keyTypes = getAvailableKeyTypesFromSystem();
+    if (keyTypes.empty())
+    {
+        log<level::ERR>("Missing Signature configuration data in system");
+        elog<InternalFailure>();
+    }
+
+    // Build publickey and its signature file name.
+    fs::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
+    fs::path pkeyFileSig(pkeyFile);
+    pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
+
+    // Build manifest and its signature file name.
+    fs::path manifestFile(imageDirPath / MANIFEST_FILE_NAME);
+    fs::path manifestFileSig(manifestFile);
+    manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
+
+    auto valid = false;
+
+    // Verify the file signature with available key types
+    // public keys and hash function.
+    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)
+        {
+            // Verify publickey file signature.
+            valid =
+                verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second, hashFunc);
+            if (valid)
+            {
+                break;
+            }
+        }
+    }
+    return valid;
+}
+
+bool Signature::verifyFile(const fs::path& file, const fs::path& sigFile,
+                           const fs::path& publicKey,
+                           const std::string& hashFunc)
+{
     return true;
 }
 
diff --git a/image_verify.hpp b/image_verify.hpp
index b5c4514..3e65288 100644
--- a/image_verify.hpp
+++ b/image_verify.hpp
@@ -1,5 +1,6 @@
 #pragma once
 #include <experimental/filesystem>
+#include <set>
 
 namespace phosphor
 {
@@ -9,7 +10,16 @@
 {
 
 namespace fs = std::experimental::filesystem;
+using Key_t = std::string;
+using Hash_t = std::string;
+using PublicKeyPath = fs::path;
+using HashFilePath = fs::path;
+using KeyHashPathPair = std::pair<HashFilePath, PublicKeyPath>;
+using AvailableKeyTypes = std::set<Key_t>;
 
+// BMC flash image file name list.
+const std::vector<std::string> bmcImages = {"image-kernel", "image-rofs",
+                                            "image-rwfs", "image-u-boot"};
 /** @class Signature
  *  @brief Contains signature verification functions.
  *  @details The software image class that contains the signature
@@ -25,11 +35,13 @@
     Signature& operator=(Signature&&) = default;
     ~Signature() = default;
 
-    /** @brief Constructs Verify Class
-     *
-     * @param[in]  imageDirPath - file path
+    /**
+     * @brief Constructs Signature.
+     * @param[in]  imageDirPath - image path
+     * @param[in]  signedConfPath - Path of public key
+     *                              hash function files
      */
-    Signature(const fs::path& imageDirPath) : imageDirPath(imageDirPath){};
+    Signature(const fs::path& imageDirPath, const fs::path& signedConfPath);
 
     /**
      * @brief Image signature verification function.
@@ -45,8 +57,55 @@
     bool verify();
 
   private:
-    /** @brief Directory where software images are placed*/
+    /**
+     * @brief Function used for system level file signature validation
+     *        of image specfic publickey file and manifest file
+     *        using the available public keys and hash functions
+     *        in the system.
+     *        Refer code-update documenation for more details.
+     */
+    bool systemLevelVerify();
+
+    /**
+     *  @brief Return all key types stored in the BMC based on the
+     *         public key and hashfunc files stored in the BMC.
+     *
+     *  @return list
+     */
+    AvailableKeyTypes getAvailableKeyTypesFromSystem() const;
+
+    /**
+     *  @brief Return public key and hash function file names for the
+     *  corresponding key type
+     *
+     *  @param[in]  key - key type
+     *  @return Pair of hash and public key file names
+     */
+    inline KeyHashPathPair getKeyHashFileNames(const Key_t& key) const;
+
+    /**
+     * @brief Verify the file signature using public key and hash function
+     *
+     * @param[in]  - Image file path
+     * @param[in]  - Signature file path
+     * @param[in]  - Public key
+     * @param[in]  - Hash function name
+     * @return true if signature verification was successful, false if not
+     */
+    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 */
     fs::path imageDirPath;
+
+    /** @brief Path of public key and hash function files */
+    fs::path signedConfPath;
+
+    /** @brief key type defined in mainfest file */
+    Key_t keyType;
+
+    /** @brief Hash type defined in mainfest file */
+    Hash_t hashType;
 };
 
 } // namespace image
diff --git a/item_updater.cpp b/item_updater.cpp
index 4d07ac9..df73fc5 100644
--- a/item_updater.cpp
+++ b/item_updater.cpp
@@ -12,6 +12,7 @@
 #include <experimental/filesystem>
 #include "version.hpp"
 #include "serialize.hpp"
+#include "image_verify.hpp"
 
 namespace phosphor
 {
@@ -26,11 +27,9 @@
 
 using namespace phosphor::logging;
 using namespace sdbusplus::xyz::openbmc_project::Software::Version::Error;
+using namespace phosphor::software::image;
 namespace fs = std::experimental::filesystem;
 
-const std::vector<std::string> bmcImages = {"image-kernel", "image-rofs",
-                                            "image-rwfs", "image-u-boot"};
-
 void ItemUpdater::createActivation(sdbusplus::message::message& msg)
 {