xref: /openbmc/openpower-pnor-code-mgmt/image_verify.cpp (revision cbf707b6fd0b11b6879df8c7ef623c80417b5c08)
1f6ed5897SGunnar Mills #include "config.h"
270804dcdSJayanth Othayoth 
370804dcdSJayanth Othayoth #include "image_verify.hpp"
4f6ed5897SGunnar Mills 
570804dcdSJayanth Othayoth #include "version.hpp"
670804dcdSJayanth Othayoth 
7f6ed5897SGunnar Mills #include <fcntl.h>
8f6ed5897SGunnar Mills #include <openssl/err.h>
9f6ed5897SGunnar Mills #include <sys/stat.h>
10f6ed5897SGunnar Mills 
1170804dcdSJayanth Othayoth #include <phosphor-logging/elog-errors.hpp>
12f6ed5897SGunnar Mills #include <phosphor-logging/elog.hpp>
13f6ed5897SGunnar Mills #include <phosphor-logging/log.hpp>
1470804dcdSJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp>
1570804dcdSJayanth Othayoth 
168facccfaSBrad Bishop #include <fstream>
178facccfaSBrad Bishop #include <set>
188facccfaSBrad Bishop 
1970804dcdSJayanth Othayoth namespace openpower
2070804dcdSJayanth Othayoth {
2170804dcdSJayanth Othayoth namespace software
2270804dcdSJayanth Othayoth {
2370804dcdSJayanth Othayoth namespace image
2470804dcdSJayanth Othayoth {
2570804dcdSJayanth Othayoth 
2670804dcdSJayanth Othayoth using namespace phosphor::logging;
2770804dcdSJayanth Othayoth using namespace openpower::software::updater;
2870804dcdSJayanth Othayoth using InternalFailure =
2970804dcdSJayanth Othayoth     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
3070804dcdSJayanth Othayoth 
3170804dcdSJayanth Othayoth constexpr auto keyTypeTag = "KeyType";
3270804dcdSJayanth Othayoth constexpr auto hashFunctionTag = "HashType";
3370804dcdSJayanth Othayoth 
Signature(const std::filesystem::path & imageDirPath,const std::string & pnorFileName,const std::filesystem::path & signedConfPath)349f44c99aSBrad Bishop Signature::Signature(const std::filesystem::path& imageDirPath,
352b2d2298SLei YU                      const std::string& pnorFileName,
369f44c99aSBrad Bishop                      const std::filesystem::path& signedConfPath) :
37f8e02429SPatrick Williams     imageDirPath(imageDirPath), pnorFileName(pnorFileName),
38f8e02429SPatrick Williams     signedConfPath(signedConfPath)
3970804dcdSJayanth Othayoth {
409f44c99aSBrad Bishop     std::filesystem::path file(imageDirPath / MANIFEST_FILE);
4170804dcdSJayanth Othayoth 
4270804dcdSJayanth Othayoth     auto keyValues =
4370804dcdSJayanth Othayoth         Version::getValue(file, {{keyTypeTag, " "}, {hashFunctionTag, " "}});
4470804dcdSJayanth Othayoth     keyType = keyValues.at(keyTypeTag);
4570804dcdSJayanth Othayoth     hashType = keyValues.at(hashFunctionTag);
4670804dcdSJayanth Othayoth }
4770804dcdSJayanth Othayoth 
getAvailableKeyTypesFromSystem() const4870804dcdSJayanth Othayoth AvailableKeyTypes Signature::getAvailableKeyTypesFromSystem() const
4970804dcdSJayanth Othayoth {
5070804dcdSJayanth Othayoth     AvailableKeyTypes keyTypes{};
5170804dcdSJayanth Othayoth 
5270804dcdSJayanth Othayoth     // Find the path of all the files
539f44c99aSBrad Bishop     if (!std::filesystem::is_directory(signedConfPath))
5470804dcdSJayanth Othayoth     {
5570804dcdSJayanth Othayoth         log<level::ERR>("Signed configuration path not found in the system");
5670804dcdSJayanth Othayoth         elog<InternalFailure>();
5770804dcdSJayanth Othayoth     }
5870804dcdSJayanth Othayoth 
5970804dcdSJayanth Othayoth     // Look for all the hash and public key file names get the key value
6070804dcdSJayanth Othayoth     // For example:
6170804dcdSJayanth Othayoth     // /etc/activationdata/OpenPOWER/publickey
6270804dcdSJayanth Othayoth     // /etc/activationdata/OpenPOWER/hashfunc
6370804dcdSJayanth Othayoth     // /etc/activationdata/GA/publickey
6470804dcdSJayanth Othayoth     // /etc/activationdata/GA/hashfunc
6570804dcdSJayanth Othayoth     // Set will have OpenPOWER, GA
6670804dcdSJayanth Othayoth 
679f44c99aSBrad Bishop     for (const auto& p :
689f44c99aSBrad Bishop          std::filesystem::recursive_directory_iterator(signedConfPath))
6970804dcdSJayanth Othayoth     {
7070804dcdSJayanth Othayoth         if ((p.path().filename() == HASH_FILE_NAME) ||
7170804dcdSJayanth Othayoth             (p.path().filename() == PUBLICKEY_FILE_NAME))
7270804dcdSJayanth Othayoth         {
7370804dcdSJayanth Othayoth             // extract the key types
7470804dcdSJayanth Othayoth             // /etc/activationdata/GA/  -> get GA from the path
7570804dcdSJayanth Othayoth             auto key = p.path().parent_path();
7670804dcdSJayanth Othayoth             keyTypes.insert(key.filename());
7770804dcdSJayanth Othayoth         }
7870804dcdSJayanth Othayoth     }
7970804dcdSJayanth Othayoth 
8070804dcdSJayanth Othayoth     return keyTypes;
8170804dcdSJayanth Othayoth }
8270804dcdSJayanth Othayoth 
getKeyHashFileNames(const Key_t & key) const8370804dcdSJayanth Othayoth inline KeyHashPathPair Signature::getKeyHashFileNames(const Key_t& key) const
8470804dcdSJayanth Othayoth {
859f44c99aSBrad Bishop     std::filesystem::path hashpath(signedConfPath / key / HASH_FILE_NAME);
869f44c99aSBrad Bishop     std::filesystem::path keyPath(signedConfPath / key / PUBLICKEY_FILE_NAME);
8770804dcdSJayanth Othayoth 
8870804dcdSJayanth Othayoth     return std::make_pair(std::move(hashpath), std::move(keyPath));
8970804dcdSJayanth Othayoth }
9070804dcdSJayanth Othayoth 
verify()9170804dcdSJayanth Othayoth bool Signature::verify()
9270804dcdSJayanth Othayoth {
9370804dcdSJayanth Othayoth     try
9470804dcdSJayanth Othayoth     {
9570804dcdSJayanth Othayoth         // Verify the MANIFEST and publickey file using available
9670804dcdSJayanth Othayoth         // public keys and hash on the system.
9770804dcdSJayanth Othayoth         if (false == systemLevelVerify())
9870804dcdSJayanth Othayoth         {
9970804dcdSJayanth Othayoth             log<level::ERR>("System level Signature Validation failed");
10070804dcdSJayanth Othayoth             return false;
10170804dcdSJayanth Othayoth         }
10270804dcdSJayanth Othayoth 
1034c7d2060SGunnar Mills         // image specific publickey file name.
1049f44c99aSBrad Bishop         std::filesystem::path publicKeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
10570804dcdSJayanth Othayoth 
10670804dcdSJayanth Othayoth         // Validate the PNOR image file.
10770804dcdSJayanth Othayoth         // Build Image File name
1089f44c99aSBrad Bishop         std::filesystem::path file(imageDirPath);
1092b2d2298SLei YU         file /= pnorFileName;
11070804dcdSJayanth Othayoth 
11170804dcdSJayanth Othayoth         // Build Signature File name
11270804dcdSJayanth Othayoth         std::string fileName = file.filename();
1139f44c99aSBrad Bishop         std::filesystem::path sigFile(imageDirPath);
11470804dcdSJayanth Othayoth         sigFile /= fileName + SIGNATURE_FILE_EXT;
11570804dcdSJayanth Othayoth 
11670804dcdSJayanth Othayoth         // Verify the signature.
11770804dcdSJayanth Othayoth         auto valid = verifyFile(file, sigFile, publicKeyFile, hashType);
11870804dcdSJayanth Othayoth         if (valid == false)
11970804dcdSJayanth Othayoth         {
12070804dcdSJayanth Othayoth             log<level::ERR>("Image file Signature Validation failed",
1212b2d2298SLei YU                             entry("IMAGE=%s", pnorFileName.c_str()));
12270804dcdSJayanth Othayoth             return false;
12370804dcdSJayanth Othayoth         }
12470804dcdSJayanth Othayoth 
12596442c88SManojkiran Eda         log<level::DEBUG>("Successfully completed Signature validation.");
12670804dcdSJayanth Othayoth 
12770804dcdSJayanth Othayoth         return true;
12870804dcdSJayanth Othayoth     }
12970804dcdSJayanth Othayoth     catch (const InternalFailure& e)
13070804dcdSJayanth Othayoth     {
13170804dcdSJayanth Othayoth         return false;
13270804dcdSJayanth Othayoth     }
13370804dcdSJayanth Othayoth     catch (const std::exception& e)
13470804dcdSJayanth Othayoth     {
13570804dcdSJayanth Othayoth         log<level::ERR>(e.what());
13670804dcdSJayanth Othayoth         return false;
13770804dcdSJayanth Othayoth     }
13870804dcdSJayanth Othayoth }
13970804dcdSJayanth Othayoth 
systemLevelVerify()14070804dcdSJayanth Othayoth bool Signature::systemLevelVerify()
14170804dcdSJayanth Othayoth {
14270804dcdSJayanth Othayoth     // Get available key types from the system.
14370804dcdSJayanth Othayoth     auto keyTypes = getAvailableKeyTypesFromSystem();
14470804dcdSJayanth Othayoth     if (keyTypes.empty())
14570804dcdSJayanth Othayoth     {
14670804dcdSJayanth Othayoth         log<level::ERR>("Missing Signature configuration data in system");
14770804dcdSJayanth Othayoth         elog<InternalFailure>();
14870804dcdSJayanth Othayoth     }
14970804dcdSJayanth Othayoth 
15070804dcdSJayanth Othayoth     // Build publickey and its signature file name.
1519f44c99aSBrad Bishop     std::filesystem::path pkeyFile(imageDirPath / PUBLICKEY_FILE_NAME);
1529f44c99aSBrad Bishop     std::filesystem::path pkeyFileSig(pkeyFile);
15370804dcdSJayanth Othayoth     pkeyFileSig.replace_extension(SIGNATURE_FILE_EXT);
15470804dcdSJayanth Othayoth 
15570804dcdSJayanth Othayoth     // Build manifest and its signature file name.
1569f44c99aSBrad Bishop     std::filesystem::path manifestFile(imageDirPath / MANIFEST_FILE);
1579f44c99aSBrad Bishop     std::filesystem::path manifestFileSig(manifestFile);
15870804dcdSJayanth Othayoth     manifestFileSig.replace_extension(SIGNATURE_FILE_EXT);
15970804dcdSJayanth Othayoth 
16070804dcdSJayanth Othayoth     auto valid = false;
16170804dcdSJayanth Othayoth 
16270804dcdSJayanth Othayoth     // Verify the file signature with available key types
16370804dcdSJayanth Othayoth     // public keys and hash function.
16470804dcdSJayanth Othayoth     // For any internal failure during the key/hash pair specific
16570804dcdSJayanth Othayoth     // validation, should continue the validation with next
16670804dcdSJayanth Othayoth     // available Key/hash pair.
16770804dcdSJayanth Othayoth     for (const auto& keyType : keyTypes)
16870804dcdSJayanth Othayoth     {
16970804dcdSJayanth Othayoth         auto keyHashPair = getKeyHashFileNames(keyType);
170f8e02429SPatrick Williams         auto keyValues =
171f8e02429SPatrick Williams             Version::getValue(keyHashPair.first, {{hashFunctionTag, " "}});
17270804dcdSJayanth Othayoth         auto hashFunc = keyValues.at(hashFunctionTag);
17370804dcdSJayanth Othayoth 
17470804dcdSJayanth Othayoth         try
17570804dcdSJayanth Othayoth         {
17670804dcdSJayanth Othayoth             // Verify manifest file signature
17770804dcdSJayanth Othayoth             valid = verifyFile(manifestFile, manifestFileSig,
17870804dcdSJayanth Othayoth                                keyHashPair.second, hashFunc);
17970804dcdSJayanth Othayoth             if (valid)
18070804dcdSJayanth Othayoth             {
18170804dcdSJayanth Othayoth                 // Verify publickey file signature.
18270804dcdSJayanth Othayoth                 valid = verifyFile(pkeyFile, pkeyFileSig, keyHashPair.second,
18370804dcdSJayanth Othayoth                                    hashFunc);
18470804dcdSJayanth Othayoth                 if (valid)
18570804dcdSJayanth Othayoth                 {
18670804dcdSJayanth Othayoth                     break;
18770804dcdSJayanth Othayoth                 }
18870804dcdSJayanth Othayoth             }
18970804dcdSJayanth Othayoth         }
19070804dcdSJayanth Othayoth         catch (const InternalFailure& e)
19170804dcdSJayanth Othayoth         {
19270804dcdSJayanth Othayoth             valid = false;
19370804dcdSJayanth Othayoth         }
19470804dcdSJayanth Othayoth     }
19570804dcdSJayanth Othayoth     return valid;
19670804dcdSJayanth Othayoth }
19770804dcdSJayanth Othayoth 
verifyFile(const std::filesystem::path & file,const std::filesystem::path & sigFile,const std::filesystem::path & publicKey,const std::string & hashFunc)1989f44c99aSBrad Bishop bool Signature::verifyFile(const std::filesystem::path& file,
1999f44c99aSBrad Bishop                            const std::filesystem::path& sigFile,
2009f44c99aSBrad Bishop                            const std::filesystem::path& publicKey,
20170804dcdSJayanth Othayoth                            const std::string& hashFunc)
20270804dcdSJayanth Othayoth {
20370804dcdSJayanth Othayoth     // Check existence of the files in the system.
2049f44c99aSBrad Bishop     if (!(std::filesystem::exists(file) && std::filesystem::exists(sigFile)))
20570804dcdSJayanth Othayoth     {
20670804dcdSJayanth Othayoth         log<level::ERR>("Failed to find the Data or signature file.",
20770804dcdSJayanth Othayoth                         entry("FILE=%s", file.c_str()));
20870804dcdSJayanth Othayoth         elog<InternalFailure>();
20970804dcdSJayanth Othayoth     }
21070804dcdSJayanth Othayoth 
21170804dcdSJayanth Othayoth     // Create RSA.
21270804dcdSJayanth Othayoth     auto publicRSA = createPublicRSA(publicKey);
2133ce1a4cbSAdriana Kobylak     if (!publicRSA)
21470804dcdSJayanth Othayoth     {
21570804dcdSJayanth Othayoth         log<level::ERR>("Failed to create RSA",
21670804dcdSJayanth Othayoth                         entry("FILE=%s", publicKey.c_str()));
21770804dcdSJayanth Othayoth         elog<InternalFailure>();
21870804dcdSJayanth Othayoth     }
21970804dcdSJayanth Othayoth 
22070804dcdSJayanth Othayoth     // Initializes a digest context.
22170ca2422SAdriana Kobylak     EVP_MD_CTX_Ptr rsaVerifyCtx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);
22270804dcdSJayanth Othayoth 
22370804dcdSJayanth Othayoth     // Adds all digest algorithms to the internal table
22470804dcdSJayanth Othayoth     OpenSSL_add_all_digests();
22570804dcdSJayanth Othayoth 
226a9cfcedcSGunnar Mills     // Create Hash structure.
22770804dcdSJayanth Othayoth     auto hashStruct = EVP_get_digestbyname(hashFunc.c_str());
22870804dcdSJayanth Othayoth     if (!hashStruct)
22970804dcdSJayanth Othayoth     {
23070804dcdSJayanth Othayoth         log<level::ERR>("EVP_get_digestbynam: Unknown message digest",
23170804dcdSJayanth Othayoth                         entry("HASH=%s", hashFunc.c_str()));
23270804dcdSJayanth Othayoth         elog<InternalFailure>();
23370804dcdSJayanth Othayoth     }
23470804dcdSJayanth Othayoth 
23570804dcdSJayanth Othayoth     auto result = EVP_DigestVerifyInit(rsaVerifyCtx.get(), nullptr, hashStruct,
2363ce1a4cbSAdriana Kobylak                                        nullptr, publicRSA.get());
23770804dcdSJayanth Othayoth 
23870804dcdSJayanth Othayoth     if (result <= 0)
23970804dcdSJayanth Othayoth     {
2404c7d2060SGunnar Mills         log<level::ERR>("Error occurred during EVP_DigestVerifyInit",
24170804dcdSJayanth Othayoth                         entry("ERRCODE=%lu", ERR_get_error()));
24270804dcdSJayanth Othayoth         elog<InternalFailure>();
24370804dcdSJayanth Othayoth     }
24470804dcdSJayanth Othayoth 
24570804dcdSJayanth Othayoth     // Hash the data file and update the verification context
2469f44c99aSBrad Bishop     auto size = std::filesystem::file_size(file);
24770804dcdSJayanth Othayoth     auto dataPtr = mapFile(file, size);
24870804dcdSJayanth Othayoth 
24970804dcdSJayanth Othayoth     result = EVP_DigestVerifyUpdate(rsaVerifyCtx.get(), dataPtr(), size);
25070804dcdSJayanth Othayoth     if (result <= 0)
25170804dcdSJayanth Othayoth     {
2524c7d2060SGunnar Mills         log<level::ERR>("Error occurred during EVP_DigestVerifyUpdate",
25370804dcdSJayanth Othayoth                         entry("ERRCODE=%lu", ERR_get_error()));
25470804dcdSJayanth Othayoth         elog<InternalFailure>();
25570804dcdSJayanth Othayoth     }
25670804dcdSJayanth Othayoth 
25770804dcdSJayanth Othayoth     // Verify the data with signature.
2589f44c99aSBrad Bishop     size = std::filesystem::file_size(sigFile);
25970804dcdSJayanth Othayoth     auto signature = mapFile(sigFile, size);
26070804dcdSJayanth Othayoth 
26170804dcdSJayanth Othayoth     result = EVP_DigestVerifyFinal(
26270804dcdSJayanth Othayoth         rsaVerifyCtx.get(), reinterpret_cast<unsigned char*>(signature()),
26370804dcdSJayanth Othayoth         size);
26470804dcdSJayanth Othayoth 
26570804dcdSJayanth Othayoth     // Check the verification result.
26670804dcdSJayanth Othayoth     if (result < 0)
26770804dcdSJayanth Othayoth     {
2684c7d2060SGunnar Mills         log<level::ERR>("Error occurred during EVP_DigestVerifyFinal",
26970804dcdSJayanth Othayoth                         entry("ERRCODE=%lu", ERR_get_error()));
27070804dcdSJayanth Othayoth         elog<InternalFailure>();
27170804dcdSJayanth Othayoth     }
27270804dcdSJayanth Othayoth 
27370804dcdSJayanth Othayoth     if (result == 0)
27470804dcdSJayanth Othayoth     {
27570804dcdSJayanth Othayoth         log<level::ERR>("EVP_DigestVerifyFinal:Signature validation failed",
27670804dcdSJayanth Othayoth                         entry("PATH=%s", sigFile.c_str()));
27770804dcdSJayanth Othayoth         return false;
27870804dcdSJayanth Othayoth     }
27970804dcdSJayanth Othayoth     return true;
28070804dcdSJayanth Othayoth }
28170804dcdSJayanth Othayoth 
createPublicRSA(const std::filesystem::path & publicKey)282*cbf707b6SPatrick Williams inline EVP_PKEY_Ptr Signature::createPublicRSA(
283*cbf707b6SPatrick Williams     const std::filesystem::path& publicKey)
28470804dcdSJayanth Othayoth {
2859f44c99aSBrad Bishop     auto size = std::filesystem::file_size(publicKey);
28670804dcdSJayanth Othayoth 
28770804dcdSJayanth Othayoth     // Read public key file
28870804dcdSJayanth Othayoth     auto data = mapFile(publicKey, size);
28970804dcdSJayanth Othayoth 
29070804dcdSJayanth Othayoth     BIO_MEM_Ptr keyBio(BIO_new_mem_buf(data(), -1), &::BIO_free);
29170804dcdSJayanth Othayoth     if (keyBio.get() == nullptr)
29270804dcdSJayanth Othayoth     {
29370804dcdSJayanth Othayoth         log<level::ERR>("Failed to create new BIO Memory buffer");
29470804dcdSJayanth Othayoth         elog<InternalFailure>();
29570804dcdSJayanth Othayoth     }
29670804dcdSJayanth Othayoth 
2973ce1a4cbSAdriana Kobylak     return {PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr),
2983ce1a4cbSAdriana Kobylak             &::EVP_PKEY_free};
29970804dcdSJayanth Othayoth }
30070804dcdSJayanth Othayoth 
mapFile(const std::filesystem::path & path,size_t size)3019f44c99aSBrad Bishop CustomMap Signature::mapFile(const std::filesystem::path& path, size_t size)
30270804dcdSJayanth Othayoth {
30370804dcdSJayanth Othayoth     CustomFd fd(open(path.c_str(), O_RDONLY));
30470804dcdSJayanth Othayoth 
30570804dcdSJayanth Othayoth     return CustomMap(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd(), 0),
30670804dcdSJayanth Othayoth                      size);
30770804dcdSJayanth Othayoth }
30870804dcdSJayanth Othayoth 
30970804dcdSJayanth Othayoth } // namespace image
31070804dcdSJayanth Othayoth } // namespace software
31170804dcdSJayanth Othayoth } // namespace openpower
312