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