blob: 20b2c2aec149c8e28b1a99b83c433c3e8c39fdb8 [file] [log] [blame]
Gunnar Millsb0ce9962018-09-07 13:39:10 -05001#include "image_verify.hpp"
Gunnar Mills01d55c32017-04-20 10:52:15 -05002#include "version.hpp"
Gunnar Millsb0ce9962018-09-07 13:39:10 -05003
4#include <openssl/sha.h>
Gunnar Mills01d55c32017-04-20 10:52:15 -05005#include <stdlib.h>
Gunnar Millsb0ce9962018-09-07 13:39:10 -05006
7#include <experimental/filesystem>
Gunnar Mills01d55c32017-04-20 10:52:15 -05008#include <fstream>
9#include <iostream>
10#include <sstream>
11#include <string>
Gunnar Millsb0ce9962018-09-07 13:39:10 -050012
13#include <gtest/gtest.h>
Gunnar Mills01d55c32017-04-20 10:52:15 -050014
15using namespace phosphor::software::manager;
Jayanth Othayoth6be275b2018-02-27 08:36:38 -060016using namespace phosphor::software::image;
Gunnar Mills01d55c32017-04-20 10:52:15 -050017
Gunnar Mills01d55c32017-04-20 10:52:15 -050018class VersionTest : public testing::Test
19{
Adriana Kobylak2285fe02018-02-27 15:36:59 -060020 protected:
21 virtual void SetUp()
22 {
23 char versionDir[] = "./versionXXXXXX";
24 _directory = mkdtemp(versionDir);
Gunnar Mills01d55c32017-04-20 10:52:15 -050025
Adriana Kobylak2285fe02018-02-27 15:36:59 -060026 if (_directory.empty())
Gunnar Mills01d55c32017-04-20 10:52:15 -050027 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -060028 throw std::bad_alloc();
Gunnar Mills01d55c32017-04-20 10:52:15 -050029 }
Adriana Kobylak2285fe02018-02-27 15:36:59 -060030 }
Gunnar Mills01d55c32017-04-20 10:52:15 -050031
Adriana Kobylak2285fe02018-02-27 15:36:59 -060032 virtual void TearDown()
33 {
34 fs::remove_all(_directory);
35 }
Gunnar Mills01d55c32017-04-20 10:52:15 -050036
Adriana Kobylak2285fe02018-02-27 15:36:59 -060037 std::string _directory;
Gunnar Mills01d55c32017-04-20 10:52:15 -050038};
39
40/** @brief Make sure we correctly get the version and purpose from getValue()*/
41TEST_F(VersionTest, TestGetValue)
42{
43 auto manifestFilePath = _directory + "/" + "MANIFEST";
44 auto version = "test-version";
45 auto purpose = "BMC";
46
47 std::ofstream file;
48 file.open(manifestFilePath, std::ofstream::out);
49 ASSERT_TRUE(file.is_open());
50
Lei YU5a7363b2019-10-18 16:50:59 +080051 file << "version=" << version << "\n";
52 file << "purpose=" << purpose << "\n";
53 file.close();
54
55 EXPECT_EQ(Version::getValue(manifestFilePath, "version"), version);
56 EXPECT_EQ(Version::getValue(manifestFilePath, "purpose"), purpose);
57}
58
59TEST_F(VersionTest, TestGetValueWithCRLF)
60{
61 auto manifestFilePath = _directory + "/" + "MANIFEST";
62 auto version = "test-version";
63 auto purpose = "BMC";
64
65 std::ofstream file;
66 file.open(manifestFilePath, std::ofstream::out);
67 ASSERT_TRUE(file.is_open());
68
69 file << "version=" << version << "\r\n";
70 file << "purpose=" << purpose << "\r\n";
Gunnar Mills01d55c32017-04-20 10:52:15 -050071 file.close();
72
73 EXPECT_EQ(Version::getValue(manifestFilePath, "version"), version);
74 EXPECT_EQ(Version::getValue(manifestFilePath, "purpose"), purpose);
75}
76
Adriana Kobylak2a3d9f52020-05-06 10:46:32 -050077TEST_F(VersionTest, TestGetVersionWithQuotes)
78{
79 auto releasePath = _directory + "/" + "os-release";
80 auto version = "1.2.3-test-version";
81
82 std::ofstream file;
83 file.open(releasePath, std::ofstream::out);
84 ASSERT_TRUE(file.is_open());
85
86 file << "VERSION_ID=\"" << version << "\"\n";
87 file.close();
88
89 EXPECT_EQ(Version::getBMCVersion(releasePath), version);
90}
91
92TEST_F(VersionTest, TestGetVersionWithoutQuotes)
93{
94 auto releasePath = _directory + "/" + "os-release";
95 auto version = "9.88.1-test-version";
96
97 std::ofstream file;
98 file.open(releasePath, std::ofstream::out);
99 ASSERT_TRUE(file.is_open());
100
101 file << "VERSION_ID=" << version << "\n";
102 file.close();
103
104 EXPECT_EQ(Version::getBMCVersion(releasePath), version);
105}
106
Gunnar Mills01d55c32017-04-20 10:52:15 -0500107/** @brief Make sure we correctly get the Id from getId()*/
108TEST_F(VersionTest, TestGetId)
109{
Gunnar Mills01d55c32017-04-20 10:52:15 -0500110 auto version = "test-id";
Saqib Khan26a960d2017-09-19 14:23:28 -0500111 unsigned char digest[SHA512_DIGEST_LENGTH];
112 SHA512_CTX ctx;
113 SHA512_Init(&ctx);
114 SHA512_Update(&ctx, version, strlen(version));
115 SHA512_Final(digest, &ctx);
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600116 char mdString[SHA512_DIGEST_LENGTH * 2 + 1];
Saqib Khan26a960d2017-09-19 14:23:28 -0500117 for (int i = 0; i < SHA512_DIGEST_LENGTH; i++)
118 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -0600119 snprintf(&mdString[i * 2], 3, "%02x", (unsigned int)digest[i]);
Saqib Khan26a960d2017-09-19 14:23:28 -0500120 }
121 std::string hexId = std::string(mdString);
122 hexId = hexId.substr(0, 8);
123 EXPECT_EQ(Version::getId(version), hexId);
Gunnar Mills01d55c32017-04-20 10:52:15 -0500124}
Jayanth Othayoth6be275b2018-02-27 08:36:38 -0600125
126class SignatureTest : public testing::Test
127{
128 static constexpr auto opensslCmd = "openssl dgst -sha256 -sign ";
129 static constexpr auto testPath = "/tmp/_testSig";
130
131 protected:
132 void command(const std::string& cmd)
133 {
134 auto val = std::system(cmd.c_str());
135 if (val)
136 {
137 std::cout << "COMMAND Error: " << val << std::endl;
138 }
139 }
140 virtual void SetUp()
141 {
142 // Create test base directory.
143 fs::create_directories(testPath);
144
145 // Create unique temporary path for images
146 std::string tmpDir(testPath);
147 tmpDir += "/extractXXXXXX";
148 std::string imageDir = mkdtemp(const_cast<char*>(tmpDir.c_str()));
149
150 // Create unique temporary configuration path
151 std::string tmpConfDir(testPath);
152 tmpConfDir += "/confXXXXXX";
153 std::string confDir = mkdtemp(const_cast<char*>(tmpConfDir.c_str()));
154
155 extractPath = imageDir;
156 extractPath /= "images";
157
158 signedConfPath = confDir;
159 signedConfPath /= "conf";
160
161 signedConfOpenBMCPath = confDir;
162 signedConfOpenBMCPath /= "conf";
163 signedConfOpenBMCPath /= "OpenBMC";
164
165 std::cout << "SETUP " << std::endl;
166
167 command("mkdir " + extractPath.string());
168 command("mkdir " + signedConfPath.string());
169 command("mkdir " + signedConfOpenBMCPath.string());
170
171 std::string hashFile = signedConfOpenBMCPath.string() + "/hashfunc";
172 command("echo \"HashType=RSA-SHA256\" > " + hashFile);
173
174 std::string manifestFile = extractPath.string() + "/" + "MANIFEST";
175 command("echo \"HashType=RSA-SHA256\" > " + manifestFile);
176 command("echo \"KeyType=OpenBMC\" >> " + manifestFile);
177
178 std::string kernelFile = extractPath.string() + "/" + "image-kernel";
179 command("echo \"image-kernel file \" > " + kernelFile);
180
181 std::string rofsFile = extractPath.string() + "/" + "image-rofs";
182 command("echo \"image-rofs file \" > " + rofsFile);
183
184 std::string rwfsFile = extractPath.string() + "/" + "image-rwfs";
185 command("echo \"image-rwfs file \" > " + rwfsFile);
186
187 std::string ubootFile = extractPath.string() + "/" + "image-u-boot";
188 command("echo \"image-u-boot file \" > " + ubootFile);
189
190 std::string pkeyFile = extractPath.string() + "/" + "private.pem";
191 command("openssl genrsa -out " + pkeyFile + " 2048");
192
193 std::string pubkeyFile = extractPath.string() + "/" + "publickey";
194 command("openssl rsa -in " + pkeyFile + " -outform PEM " +
195 "-pubout -out " + pubkeyFile);
196
Jayanth Othayoth6be275b2018-02-27 08:36:38 -0600197 command("cp " + pubkeyFile + " " + signedConfOpenBMCPath.string());
198 command(opensslCmd + pkeyFile + " -out " + kernelFile + ".sig " +
199 kernelFile);
200
201 command(opensslCmd + pkeyFile + " -out " + manifestFile + ".sig " +
202 manifestFile);
203 command(opensslCmd + pkeyFile + " -out " + rofsFile + ".sig " +
204 rofsFile);
205 command(opensslCmd + pkeyFile + " -out " + rwfsFile + ".sig " +
206 rwfsFile);
207 command(opensslCmd + pkeyFile + " -out " + ubootFile + ".sig " +
208 ubootFile);
209 command(opensslCmd + pkeyFile + " -out " + pubkeyFile + ".sig " +
210 pubkeyFile);
211
212 signature = std::make_unique<Signature>(extractPath, signedConfPath);
213 }
214 virtual void TearDown()
215 {
216 command("rm -rf " + std::string(testPath));
217 }
218
219 std::unique_ptr<Signature> signature;
220 fs::path extractPath;
221 fs::path signedConfPath;
222 fs::path signedConfOpenBMCPath;
223};
224
Gunnar Mills2bcba022018-04-08 15:02:04 -0500225/** @brief Test for success scenario*/
Jayanth Othayoth6be275b2018-02-27 08:36:38 -0600226TEST_F(SignatureTest, TestSignatureVerify)
227{
228 EXPECT_TRUE(signature->verify());
229}
230
231/** @brief Test failure scenario with corrupted signature file*/
232TEST_F(SignatureTest, TestCorruptSignatureFile)
233{
234 // corrupt the image-kernel.sig file and ensure that verification fails
235 std::string kernelFile = extractPath.string() + "/" + "image-kernel";
236 command("echo \"dummy data\" > " + kernelFile + ".sig ");
237 EXPECT_FALSE(signature->verify());
238}
239
240/** @brief Test failure scenario with no public key in the image*/
241TEST_F(SignatureTest, TestNoPublicKeyInImage)
242{
243 // Remove publickey file from the image and ensure that verify fails
244 std::string pubkeyFile = extractPath.string() + "/" + "publickey";
245 command("rm " + pubkeyFile);
246 EXPECT_FALSE(signature->verify());
247}
248
249/** @brief Test failure scenario with invalid hash function value*/
250TEST_F(SignatureTest, TestInvalidHashValue)
251{
252 // Change the hashfunc value and ensure that verification fails
253 std::string hashFile = signedConfOpenBMCPath.string() + "/hashfunc";
254 command("echo \"HashType=md5\" > " + hashFile);
255 EXPECT_FALSE(signature->verify());
256}
257
258/** @brief Test for failure scenario with no config file in system*/
259TEST_F(SignatureTest, TestNoConfigFileInSystem)
260{
261 // Remove the conf folder in the system and ensure that verify fails
262 command("rm -rf " + signedConfOpenBMCPath.string());
263 EXPECT_FALSE(signature->verify());
264}