blob: 361367e8b509181e1a855637a215dc4446229fa3 [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
77/** @brief Make sure we correctly get the Id from getId()*/
78TEST_F(VersionTest, TestGetId)
79{
Gunnar Mills01d55c32017-04-20 10:52:15 -050080 auto version = "test-id";
Saqib Khan26a960d2017-09-19 14:23:28 -050081 unsigned char digest[SHA512_DIGEST_LENGTH];
82 SHA512_CTX ctx;
83 SHA512_Init(&ctx);
84 SHA512_Update(&ctx, version, strlen(version));
85 SHA512_Final(digest, &ctx);
Adriana Kobylak2285fe02018-02-27 15:36:59 -060086 char mdString[SHA512_DIGEST_LENGTH * 2 + 1];
Saqib Khan26a960d2017-09-19 14:23:28 -050087 for (int i = 0; i < SHA512_DIGEST_LENGTH; i++)
88 {
Adriana Kobylak2285fe02018-02-27 15:36:59 -060089 snprintf(&mdString[i * 2], 3, "%02x", (unsigned int)digest[i]);
Saqib Khan26a960d2017-09-19 14:23:28 -050090 }
91 std::string hexId = std::string(mdString);
92 hexId = hexId.substr(0, 8);
93 EXPECT_EQ(Version::getId(version), hexId);
Gunnar Mills01d55c32017-04-20 10:52:15 -050094}
Jayanth Othayoth6be275b2018-02-27 08:36:38 -060095
96class SignatureTest : public testing::Test
97{
98 static constexpr auto opensslCmd = "openssl dgst -sha256 -sign ";
99 static constexpr auto testPath = "/tmp/_testSig";
100
101 protected:
102 void command(const std::string& cmd)
103 {
104 auto val = std::system(cmd.c_str());
105 if (val)
106 {
107 std::cout << "COMMAND Error: " << val << std::endl;
108 }
109 }
110 virtual void SetUp()
111 {
112 // Create test base directory.
113 fs::create_directories(testPath);
114
115 // Create unique temporary path for images
116 std::string tmpDir(testPath);
117 tmpDir += "/extractXXXXXX";
118 std::string imageDir = mkdtemp(const_cast<char*>(tmpDir.c_str()));
119
120 // Create unique temporary configuration path
121 std::string tmpConfDir(testPath);
122 tmpConfDir += "/confXXXXXX";
123 std::string confDir = mkdtemp(const_cast<char*>(tmpConfDir.c_str()));
124
125 extractPath = imageDir;
126 extractPath /= "images";
127
128 signedConfPath = confDir;
129 signedConfPath /= "conf";
130
131 signedConfOpenBMCPath = confDir;
132 signedConfOpenBMCPath /= "conf";
133 signedConfOpenBMCPath /= "OpenBMC";
134
135 std::cout << "SETUP " << std::endl;
136
137 command("mkdir " + extractPath.string());
138 command("mkdir " + signedConfPath.string());
139 command("mkdir " + signedConfOpenBMCPath.string());
140
141 std::string hashFile = signedConfOpenBMCPath.string() + "/hashfunc";
142 command("echo \"HashType=RSA-SHA256\" > " + hashFile);
143
144 std::string manifestFile = extractPath.string() + "/" + "MANIFEST";
145 command("echo \"HashType=RSA-SHA256\" > " + manifestFile);
146 command("echo \"KeyType=OpenBMC\" >> " + manifestFile);
147
148 std::string kernelFile = extractPath.string() + "/" + "image-kernel";
149 command("echo \"image-kernel file \" > " + kernelFile);
150
151 std::string rofsFile = extractPath.string() + "/" + "image-rofs";
152 command("echo \"image-rofs file \" > " + rofsFile);
153
154 std::string rwfsFile = extractPath.string() + "/" + "image-rwfs";
155 command("echo \"image-rwfs file \" > " + rwfsFile);
156
157 std::string ubootFile = extractPath.string() + "/" + "image-u-boot";
158 command("echo \"image-u-boot file \" > " + ubootFile);
159
160 std::string pkeyFile = extractPath.string() + "/" + "private.pem";
161 command("openssl genrsa -out " + pkeyFile + " 2048");
162
163 std::string pubkeyFile = extractPath.string() + "/" + "publickey";
164 command("openssl rsa -in " + pkeyFile + " -outform PEM " +
165 "-pubout -out " + pubkeyFile);
166
167 std::string pubKeyConfFile =
168 signedConfOpenBMCPath.string() + "/" + "publickey";
169 command("cp " + pubkeyFile + " " + signedConfOpenBMCPath.string());
170 command(opensslCmd + pkeyFile + " -out " + kernelFile + ".sig " +
171 kernelFile);
172
173 command(opensslCmd + pkeyFile + " -out " + manifestFile + ".sig " +
174 manifestFile);
175 command(opensslCmd + pkeyFile + " -out " + rofsFile + ".sig " +
176 rofsFile);
177 command(opensslCmd + pkeyFile + " -out " + rwfsFile + ".sig " +
178 rwfsFile);
179 command(opensslCmd + pkeyFile + " -out " + ubootFile + ".sig " +
180 ubootFile);
181 command(opensslCmd + pkeyFile + " -out " + pubkeyFile + ".sig " +
182 pubkeyFile);
183
184 signature = std::make_unique<Signature>(extractPath, signedConfPath);
185 }
186 virtual void TearDown()
187 {
188 command("rm -rf " + std::string(testPath));
189 }
190
191 std::unique_ptr<Signature> signature;
192 fs::path extractPath;
193 fs::path signedConfPath;
194 fs::path signedConfOpenBMCPath;
195};
196
Gunnar Mills2bcba022018-04-08 15:02:04 -0500197/** @brief Test for success scenario*/
Jayanth Othayoth6be275b2018-02-27 08:36:38 -0600198TEST_F(SignatureTest, TestSignatureVerify)
199{
200 EXPECT_TRUE(signature->verify());
201}
202
203/** @brief Test failure scenario with corrupted signature file*/
204TEST_F(SignatureTest, TestCorruptSignatureFile)
205{
206 // corrupt the image-kernel.sig file and ensure that verification fails
207 std::string kernelFile = extractPath.string() + "/" + "image-kernel";
208 command("echo \"dummy data\" > " + kernelFile + ".sig ");
209 EXPECT_FALSE(signature->verify());
210}
211
212/** @brief Test failure scenario with no public key in the image*/
213TEST_F(SignatureTest, TestNoPublicKeyInImage)
214{
215 // Remove publickey file from the image and ensure that verify fails
216 std::string pubkeyFile = extractPath.string() + "/" + "publickey";
217 command("rm " + pubkeyFile);
218 EXPECT_FALSE(signature->verify());
219}
220
221/** @brief Test failure scenario with invalid hash function value*/
222TEST_F(SignatureTest, TestInvalidHashValue)
223{
224 // Change the hashfunc value and ensure that verification fails
225 std::string hashFile = signedConfOpenBMCPath.string() + "/hashfunc";
226 command("echo \"HashType=md5\" > " + hashFile);
227 EXPECT_FALSE(signature->verify());
228}
229
230/** @brief Test for failure scenario with no config file in system*/
231TEST_F(SignatureTest, TestNoConfigFileInSystem)
232{
233 // Remove the conf folder in the system and ensure that verify fails
234 command("rm -rf " + signedConfOpenBMCPath.string());
235 EXPECT_FALSE(signature->verify());
236}