xref: /aosp_15_r20/external/libbrillo/brillo/secure_blob_test.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1 // Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Unit tests for SecureBlob.
6 
7 #include "brillo/asan.h"
8 #include "brillo/secure_allocator.h"
9 #include "brillo/secure_blob.h"
10 
11 #include <algorithm>
12 #include <iterator>
13 #include <limits>
14 #include <numeric>
15 
16 #include <base/logging.h>
17 #include <gtest/gtest.h>
18 
19 namespace brillo {
20 using std::string;
21 
22 // Tests BlobToString() and BlobFromString().
TEST(BlobTest,StringConversions)23 TEST(BlobTest, StringConversions) {
24   const char kTestBytes[] = {'\0', '\x1', 'a', std::numeric_limits<char>::min(),
25                              std::numeric_limits<char>::max()};
26   const Blob blob(std::begin(kTestBytes), std::end(kTestBytes));
27   const string obtained_string = BlobToString(blob);
28   EXPECT_EQ(string(std::begin(kTestBytes), std::end(kTestBytes)),
29             obtained_string);
30   const Blob obtained_blob = BlobFromString(obtained_string);
31   EXPECT_EQ(blob, obtained_blob);
32 }
33 
34 // Tests CombineBlobs().
TEST(BlobTest,CombineBlobs)35 TEST(BlobTest, CombineBlobs) {
36   const Blob kEmpty;
37   const Blob kBlob1 = {1};
38   const Blob kBlob2 = {2};
39   const Blob kBlob3 = {3};
40   const Blob kBlob12 = {1, 2};
41   const Blob kBlob123 = {1, 2, 3};
42   EXPECT_EQ(kBlob123, CombineBlobs({kBlob12, kBlob3}));
43   EXPECT_EQ(kBlob123, CombineBlobs({kBlob1, kBlob2, kBlob3}));
44   EXPECT_EQ(kBlob12, CombineBlobs({kBlob12}));
45   EXPECT_EQ(kBlob12, CombineBlobs({kEmpty, kBlob1, kEmpty, kBlob2, kEmpty}));
46   EXPECT_EQ(kEmpty, CombineBlobs({}));
47 }
48 
49 class SecureBlobTest : public ::testing::Test {
50  public:
SecureBlobTest()51   SecureBlobTest() {}
~SecureBlobTest()52   virtual ~SecureBlobTest() {}
53 
FindBlobInBlob(const brillo::SecureBlob & haystack,const brillo::SecureBlob & needle)54   static bool FindBlobInBlob(const brillo::SecureBlob& haystack,
55                              const brillo::SecureBlob& needle) {
56     auto pos = std::search(
57         haystack.begin(), haystack.end(), needle.begin(), needle.end());
58     return (pos != haystack.end());
59   }
60 
FindBlobIndexInBlob(const brillo::SecureBlob & haystack,const brillo::SecureBlob & needle)61   static int FindBlobIndexInBlob(const brillo::SecureBlob& haystack,
62                                  const brillo::SecureBlob& needle) {
63     auto pos = std::search(
64         haystack.begin(), haystack.end(), needle.begin(), needle.end());
65     if (pos == haystack.end()) {
66       return -1;
67     }
68     return std::distance(haystack.begin(), pos);
69   }
70 
71  private:
72   DISALLOW_COPY_AND_ASSIGN(SecureBlobTest);
73 };
74 
TEST_F(SecureBlobTest,AllocationSizeTest)75 TEST_F(SecureBlobTest, AllocationSizeTest) {
76   // Checks that allocating a SecureBlob of a specified size works.
77   SecureBlob blob(32);
78 
79   EXPECT_EQ(32, blob.size());
80 }
81 
TEST_F(SecureBlobTest,ConstructorCountValueTest)82 TEST_F(SecureBlobTest, ConstructorCountValueTest) {
83   // Checks that constructing a SecureBlob with |count| copies of |value| works.
84   SecureBlob blob(32, 'a');
85 
86   for (size_t i = 0; i < blob.size(); i++) {
87     EXPECT_EQ('a', blob[i]);
88   }
89 }
90 
TEST_F(SecureBlobTest,ConstructorAmbiguousTest)91 TEST_F(SecureBlobTest, ConstructorAmbiguousTest) {
92   // This test will become important once SecureBlob stops inheriting from Blob.
93   SecureBlob blob(32, 0);
94 
95   for (size_t i = 0; i < blob.size(); i++) {
96     EXPECT_EQ(0, blob[i]);
97   }
98 }
99 
TEST_F(SecureBlobTest,ConstructorIteratorTest)100 TEST_F(SecureBlobTest, ConstructorIteratorTest) {
101   // Checks that constructing a SecureBlob with an iterator works.
102   unsigned char from_data[32];
103   std::iota(std::begin(from_data), std::end(from_data), 0);
104 
105   SecureBlob blob(std::begin(from_data), std::end(from_data));
106 
107   EXPECT_EQ(sizeof(from_data), blob.size());
108 
109   for (unsigned int i = 0; i < sizeof(from_data); i++) {
110     EXPECT_EQ(from_data[i], blob[i]);
111   }
112 }
113 
TEST_F(SecureBlobTest,BlobConstructorTest)114 TEST_F(SecureBlobTest, BlobConstructorTest) {
115   // Check that constructing a SecureBlob from a Blob works.
116   const std::vector<uint8_t> bytes = {0, 1, 255};
117   const Blob blob(bytes);
118   const SecureBlob secure_blob(blob);
119   EXPECT_EQ(bytes,
120             std::vector<uint8_t>(secure_blob.begin(), secure_blob.end()));
121 }
122 
TEST_F(SecureBlobTest,IteratorTest)123 TEST_F(SecureBlobTest, IteratorTest) {
124   // Checks that SecureBlob::begin(), SecureBlob::end() work.
125   unsigned char from_data[32];
126   std::iota(std::begin(from_data), std::end(from_data), 0);
127 
128   SecureBlob blob(std::begin(from_data), std::end(from_data));
129 
130   EXPECT_EQ(sizeof(from_data), blob.size());
131 
132   size_t i = 0;
133   for (auto it = blob.begin(); it != blob.end(); ++it) {
134     EXPECT_EQ(from_data[i], *it);
135     ++i;
136   }
137 }
138 
TEST_F(SecureBlobTest,AssignTest)139 TEST_F(SecureBlobTest, AssignTest) {
140   // Checks that .assign() works.
141   unsigned char from_data[32];
142   std::iota(std::begin(from_data), std::end(from_data), 0);
143 
144   SecureBlob blob;
145   blob.assign(std::begin(from_data), std::end(from_data));
146 
147   EXPECT_EQ(sizeof(from_data), blob.size());
148 
149   size_t i = 0;
150   for (auto it = blob.begin(); it != blob.end(); ++it) {
151     EXPECT_EQ(from_data[i], *it);
152     ++i;
153   }
154 
155   SecureBlob blob2;
156   blob2.assign(blob.begin(), blob.end());
157 
158   EXPECT_EQ(blob, blob2);
159 }
160 
161 // Disable ResizeTest with Address Sanitizer.
162 // https://crbug.com/806013
163 #ifndef BRILLO_ASAN_BUILD
TEST_F(SecureBlobTest,ResizeTest)164 TEST_F(SecureBlobTest, ResizeTest) {
165   // Check that resizing a SecureBlob wipes the excess memory. The test assumes
166   // that resize() down by one will not re-allocate the memory, so the last byte
167   // will still be part of the SecureBlob's allocation.
168   size_t length = 1024;
169   SecureBlob blob(length);
170   void* original_data = blob.data();
171   for (size_t i = 0; i < length; i++) {
172     blob[i] = i;
173   }
174 
175   blob.resize(length - 1);
176 
177   EXPECT_EQ(original_data, blob.data());
178   EXPECT_EQ(length - 1, blob.size());
179   EXPECT_EQ(0, blob.data()[length - 1]);
180 }
181 #endif
182 
TEST_F(SecureBlobTest,CombineTest)183 TEST_F(SecureBlobTest, CombineTest) {
184   SecureBlob blob1(32);
185   SecureBlob blob2(32);
186   std::iota(blob1.begin(), blob1.end(), 0);
187   std::iota(blob2.begin(), blob2.end(), 32);
188   SecureBlob combined_blob = SecureBlob::Combine(blob1, blob2);
189   EXPECT_EQ(combined_blob.size(), (blob1.size() + blob2.size()));
190   EXPECT_TRUE(SecureBlobTest::FindBlobInBlob(combined_blob, blob1));
191   EXPECT_TRUE(SecureBlobTest::FindBlobInBlob(combined_blob, blob2));
192   int blob1_index = SecureBlobTest::FindBlobIndexInBlob(combined_blob, blob1);
193   int blob2_index = SecureBlobTest::FindBlobIndexInBlob(combined_blob, blob2);
194   EXPECT_EQ(blob1_index, 0);
195   EXPECT_EQ(blob2_index, 32);
196 }
197 
TEST_F(SecureBlobTest,BlobToStringTest)198 TEST_F(SecureBlobTest, BlobToStringTest) {
199   std::string test_string("Test String");
200   SecureBlob blob = SecureBlob(test_string.begin(), test_string.end());
201   EXPECT_EQ(blob.size(), test_string.length());
202   std::string result_string = blob.to_string();
203   EXPECT_EQ(test_string.compare(result_string), 0);
204 }
205 
TEST_F(SecureBlobTest,HexStringToSecureBlob)206 TEST_F(SecureBlobTest, HexStringToSecureBlob) {
207   std::string hex_string("112233445566778899aabbccddeeff0f");
208 
209   SecureBlob blob;
210   SecureBlob::HexStringToSecureBlob(hex_string, &blob);
211 
212   EXPECT_EQ(blob.size(), 16u);
213   EXPECT_EQ(blob[0],  0x11);
214   EXPECT_EQ(blob[1],  0x22);
215   EXPECT_EQ(blob[2],  0x33);
216   EXPECT_EQ(blob[3],  0x44);
217   EXPECT_EQ(blob[4],  0x55);
218   EXPECT_EQ(blob[5],  0x66);
219   EXPECT_EQ(blob[6],  0x77);
220   EXPECT_EQ(blob[7],  0x88);
221   EXPECT_EQ(blob[8],  0x99);
222   EXPECT_EQ(blob[9],  0xaa);
223   EXPECT_EQ(blob[10], 0xbb);
224   EXPECT_EQ(blob[11], 0xcc);
225   EXPECT_EQ(blob[12], 0xdd);
226   EXPECT_EQ(blob[13], 0xee);
227   EXPECT_EQ(blob[14], 0xff);
228   EXPECT_EQ(blob[15], 0x0f);
229 }
230 
231 // Override clear_contents() to check whether memory has been cleared.
232 template <typename T>
233 class TestSecureAllocator : public SecureAllocator<T> {
234  public:
235   using typename SecureAllocator<T>::size_type;
236   using typename SecureAllocator<T>::value_type;
237 
GetErasedCount()238   int GetErasedCount() { return erased_count; }
239 
240  protected:
clear_contents(value_type * p,size_type n)241   void clear_contents(value_type* p, size_type n) override {
242     SecureAllocator<T>::clear_contents(p, n);
243     unsigned char *v = reinterpret_cast<unsigned char*>(p);
244     for (int i = 0; i < n; i++) {
245       EXPECT_EQ(v[i], 0);
246       erased_count++;
247     }
248   }
249 
250  private:
251   int erased_count = 0;
252 };
253 
TEST(SecureAllocator,ErasureOnDeallocation)254 TEST(SecureAllocator, ErasureOnDeallocation) {
255   // Make sure that the contents are cleared on deallocation.
256   TestSecureAllocator<char> e;
257   constexpr size_t test_string_sz = 15;
258 
259   char *test_string_addr = e.allocate(test_string_sz);
260   snprintf(test_string_addr, test_string_sz, "Test String");
261 
262   // Deallocate memory; the mock class should check for cleared data.
263   e.deallocate(test_string_addr, 15);
264   // The deallocation should have traversed the complete page.
265   EXPECT_EQ(e.GetErasedCount(), 4096);
266 }
267 
TEST(SecureAllocator,MultiPageCorrectness)268 TEST(SecureAllocator, MultiPageCorrectness) {
269   // Make sure that the contents are cleared on deallocation.
270   TestSecureAllocator<uint64_t> e;
271 
272   // Allocate 4100*8 bytes.
273   uint64_t *test_array = e.allocate(4100);
274 
275   // Check if the space was correctly allocated for long long.
276   for (int i = 0; i < 4100; i++)
277       test_array[i] = 0xF0F0F0F0F0F0F0F0;
278 
279   // Deallocate memory; the mock class should check for cleared data.
280   e.deallocate(test_array, 4100);
281   // 36864 bytes is the next highest size that is a multiple of the page size.
282   EXPECT_EQ(e.GetErasedCount(), 36864);
283 }
284 
285 // DeathTests fork a new process and check how it proceeds. Take advantage
286 // of this and check if the value of SecureString is passed on to
287 // forked children.
288 #if GTEST_IS_THREADSAFE
289 // Check if the contents of the container are zeroed out.
CheckPropagationOnFork(const brillo::SecureBlob & forked_blob,const Blob & reference)290 void CheckPropagationOnFork(const brillo::SecureBlob& forked_blob,
291                             const Blob& reference) {
292   LOG(INFO) << forked_blob.to_string();
293   for (int i = 0; i < forked_blob.size(); i++) {
294     CHECK_NE(reference[i], forked_blob[i]);
295     CHECK_EQ(forked_blob[i], 0);
296   }
297   exit(0);
298 }
299 
TEST(SecureAllocatorDeathTest,ErasureOnFork)300 TEST(SecureAllocatorDeathTest, ErasureOnFork) {
301   Blob reference = BlobFromString("Test String");
302   SecureBlob erasable_blob(reference.begin(), reference.end());
303 
304   EXPECT_EXIT(CheckPropagationOnFork(erasable_blob, reference),
305               ::testing::ExitedWithCode(0), "");
306 
307   // In the original process, check the SecureBlob to see if it has not
308   // changed.
309   for (int i = 0; i < erasable_blob.size(); i++)
310     EXPECT_EQ(erasable_blob[i], reference[i]);
311 }
312 #endif  // GTEST_IS_THREADSAFE
313 
314 }  // namespace brillo
315