1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "VtsSecurityAvbTest"
18
19 #include <array>
20 #include <list>
21 #include <map>
22 #include <optional>
23 #include <set>
24 #include <tuple>
25 #include <vector>
26
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/properties.h>
30 #include <android-base/result.h>
31 #include <android-base/stringprintf.h>
32 #include <android-base/unique_fd.h>
33 #include <fs_avb/fs_avb_util.h>
34 #include <fs_mgr/roots.h>
35 #include <fstab/fstab.h>
36 #include <gtest/gtest.h>
37 #include <libavb/libavb.h>
38 #include <libavb_user/avb_ops_user.h>
39 #include <libdm/dm.h>
40 #include <log/log.h>
41
42 #include "gsi_validation_utils.h"
43
44 using android::base::Error;
45 using android::base::Result;
46
47 // Calculates the digest of a block filled with 0.
CalculateZeroDigest(const ShaHasher & hasher,size_t size,const void * salt,int32_t block_length,uint8_t * digest)48 static bool CalculateZeroDigest(const ShaHasher &hasher, size_t size,
49 const void *salt, int32_t block_length,
50 uint8_t *digest) {
51 const std::vector<uint8_t> buffer(size, 0);
52 return hasher.CalculateDigest(buffer.data(), size, salt, block_length,
53 digest);
54 }
55
56 // Logical structure of a hashtree:
57 //
58 // Level 2: [ root ]
59 // / \
60 // Level 1: [entry_0] [entry_1]
61 // / ... \ ... \
62 // Level 0: [entry_0_0] ... [entry_0_127] ... [entry_1_127]
63 // / ... \ / ... \ / ... \
64 // Data: blk_0 ... blk_127 blk_16256 ... blk_16383 blk_32640 ... blk_32767
65 //
66 // The digest of a data block or a hash block in level N is stored in level
67 // N + 1.
68 // The function VerifyHashtree allocates a HashtreeLevel for each level. It
69 // calculates the digests of the blocks in lower level and fills them in
70 // calculating_hash_block. When calculating_hash_block is full, it is compared
71 // with the hash block at comparing_tree_offset in the image. After comparison,
72 // calculating_hash_block is cleared and reused for the next hash block.
73 //
74 // comparing_tree_offset
75 // |
76 // v
77 // [<-------------------- level_size -------------------->]
78 // [entry_0_0] ... [entry_0_127 ] ... [entry_1_127]
79 //
80 // [calculating_hash_block]
81 // ^
82 // |
83 // calculating_offset
84 struct HashtreeLevel {
85 // Offset of an expected hash block to compare, relative to the beginning of
86 // the hashtree in the image file.
87 uint64_t comparing_tree_offset;
88 // Size of this level, in bytes.
89 const uint64_t level_size;
90 // Offset of a digest in calculating_hash_block.
91 size_t calculating_offset;
92 // The hash block containing the digests calculated from the lower level.
93 std::vector<uint8_t> calculating_hash_block;
94
HashtreeLevelHashtreeLevel95 HashtreeLevel(uint64_t lv_offset, uint64_t lv_size, size_t hash_block_size)
96 : comparing_tree_offset(lv_offset),
97 level_size(lv_size),
98 calculating_offset(0),
99 calculating_hash_block(hash_block_size) {}
100 };
101
102 // Calculates and verifies the image's hashtree.
103 //
104 // Arguments:
105 // image_fd: The raw image file.
106 // image_size, data_block_size, hash_block_size, tree_offset, tree_size: The
107 // fields in AvbHashtreeDescriptor.
108 // salt: The binary value of the salt in FsAvbHashtreeDescriptor.
109 // hasher: The ShaHasher object.
110 // root_digest: The binary value of the root_digest in
111 // FsAvbHashtreeDescriptor.
112 //
113 // Returns:
114 // An empty string if the function succeeds.
115 // Otherwise it returns the error message.
VerifyHashtree(int image_fd,uint64_t image_size,const std::vector<uint8_t> & salt,uint32_t data_block_size,uint32_t hash_block_size,uint64_t tree_offset,uint64_t tree_size,const ShaHasher & hasher,const std::vector<uint8_t> & root_digest)116 static std::string VerifyHashtree(int image_fd, uint64_t image_size,
117 const std::vector<uint8_t> &salt,
118 uint32_t data_block_size,
119 uint32_t hash_block_size,
120 uint64_t tree_offset, uint64_t tree_size,
121 const ShaHasher &hasher,
122 const std::vector<uint8_t> &root_digest) {
123 uint32_t digest_size = hasher.GetDigestSize();
124 uint32_t padded_digest_size = 1;
125 while (padded_digest_size < digest_size) {
126 padded_digest_size *= 2;
127 }
128
129 if (image_size % data_block_size != 0) {
130 return "Image size is not a multiple of data block size";
131 }
132
133 uint64_t data_block_count = image_size / data_block_size;
134 uint32_t digests_per_block = hash_block_size / padded_digest_size;
135
136 // Initialize HashtreeLevel in bottom-up order.
137 std::list<HashtreeLevel> levels;
138 {
139 uint64_t hash_block_count = 0;
140 uint32_t level_block_count = data_block_count;
141 // Calculate the hashtree until the root hash is reached.
142 while (level_block_count > 1) {
143 uint32_t next_level_block_count =
144 (level_block_count + digests_per_block - 1) / digests_per_block;
145 hash_block_count += next_level_block_count;
146 // comparing_tree_offset will be initialized later.
147 levels.emplace_back(0 /* comparing_tree_offset */,
148 next_level_block_count * hash_block_size,
149 hash_block_size);
150 level_block_count = next_level_block_count;
151 }
152 if (hash_block_count * hash_block_size != tree_size) {
153 return "Block count and tree size mismatch";
154 }
155 // Append the root digest. Its level_size is unused.
156 levels.emplace_back(0 /* comparing_tree_offset */, 0 /* level_size */,
157 digest_size);
158
159 // Initialize comparing_tree_offset of each level
160 for (auto level = std::prev(levels.end()); level != levels.begin();
161 level--) {
162 std::prev(level)->comparing_tree_offset =
163 level->comparing_tree_offset + level->level_size;
164 }
165 }
166
167 std::vector<uint8_t> padded_zero_digest(padded_digest_size, 0);
168 if (!CalculateZeroDigest(hasher, data_block_size, salt.data(), salt.size(),
169 padded_zero_digest.data())) {
170 return "CalculateZeroDigest fails";
171 }
172
173 std::vector<uint8_t> data_block(data_block_size);
174 std::vector<uint8_t> tree_block(hash_block_size);
175 for (uint64_t image_offset = 0; image_offset < image_size;
176 image_offset += data_block_size) {
177 ssize_t read_size = TEMP_FAILURE_RETRY(
178 pread64(image_fd, data_block.data(), data_block.size(), image_offset));
179 if (read_size != data_block.size()) {
180 return android::base::StringPrintf(
181 "Fail to read data block at offset %llu",
182 (unsigned long long)image_offset);
183 }
184
185 bool is_last_data = (image_offset + data_block.size() == image_size);
186 // The block to be digested
187 std::vector<uint8_t> *current_block = &data_block;
188 for (auto level = levels.begin(); true; level++) {
189 uint8_t *current_digest =
190 level->calculating_hash_block.data() + level->calculating_offset;
191 if (!hasher.CalculateDigest(current_block->data(), current_block->size(),
192 salt.data(), salt.size(), current_digest)) {
193 return "CalculateDigest fails";
194 }
195 // Stop at root digest
196 if (std::next(level) == levels.end()) {
197 break;
198 }
199
200 // Pad the digest
201 memset(current_digest + digest_size, 0, padded_digest_size - digest_size);
202 level->calculating_offset += padded_digest_size;
203 // Pad the last hash block of this level
204 if (is_last_data) {
205 memset(
206 level->calculating_hash_block.data() + level->calculating_offset, 0,
207 level->calculating_hash_block.size() - level->calculating_offset);
208 } else if (level->calculating_offset <
209 level->calculating_hash_block.size()) {
210 // Stop at this level if the hash block is not full, continue to read
211 // more data_blocks from the outside loop for digest calculation
212 break;
213 }
214 // Verify the full hash block
215 // current_block may point to tree_block. Since the following pread64
216 // changes tree_block, do not read current_block in the rest of this
217 // code block.
218 current_block = nullptr;
219 read_size = TEMP_FAILURE_RETRY(
220 pread64(image_fd, tree_block.data(), tree_block.size(),
221 tree_offset + level->comparing_tree_offset));
222 if (read_size != tree_block.size()) {
223 return android::base::StringPrintf(
224 "Fail to read tree block at offset %llu",
225 (unsigned long long)tree_offset + level->comparing_tree_offset);
226 }
227
228 for (uint32_t offset = 0; offset < tree_block.size();
229 offset += padded_digest_size) {
230 // If the digest in the hashtree is equal to the digest of zero block,
231 // it indicates the corresponding data block is in DONT_CARE chunk in
232 // sparse image. The block should not be verified.
233 if (level == levels.begin() &&
234 memcmp(tree_block.data() + offset, padded_zero_digest.data(),
235 padded_digest_size) == 0) {
236 continue;
237 }
238 if (memcmp(tree_block.data() + offset,
239 level->calculating_hash_block.data() + offset,
240 padded_digest_size) != 0) {
241 return android::base::StringPrintf(
242 "Hash blocks mismatch, block offset = %llu, digest offset = %u",
243 (unsigned long long)tree_offset + level->comparing_tree_offset,
244 offset);
245 }
246 }
247
248 level->calculating_offset = 0;
249 level->comparing_tree_offset += hash_block_size;
250 if (level->comparing_tree_offset > tree_size) {
251 return "Tree offset is out of bound";
252 }
253 // Prepare for next/upper level, to calculate the digest of this
254 // hash_block for comparison
255 current_block = &tree_block;
256 }
257 }
258
259 if (levels.back().calculating_hash_block != root_digest) {
260 return "Root digests mismatch";
261 }
262 return "";
263 }
264
265 // Gets the system partition's AvbHashtreeDescriptor and device file path.
266 //
267 // Arguments:
268 // out_verify_result: The result of vbmeta verification.
269 // out_system_path: The system's device file path.
270 //
271 // Returns:
272 // The pointer to the system's AvbHashtreeDescriptor.
273 // nullptr if any operation fails.
274 static std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor>
GetSystemHashtreeDescriptor(android::fs_mgr::VBMetaVerifyResult * out_verify_result,std::string * out_system_path)275 GetSystemHashtreeDescriptor(
276 android::fs_mgr::VBMetaVerifyResult *out_verify_result,
277 std::string *out_system_path) {
278 android::fs_mgr::Fstab default_fstab;
279 bool ok = ReadDefaultFstab(&default_fstab);
280 if (!ok) {
281 ALOGE("ReadDefaultFstab fails");
282 return nullptr;
283 }
284 android::fs_mgr::FstabEntry *system_fstab_entry =
285 GetEntryForPath(&default_fstab, "/system");
286 if (system_fstab_entry == nullptr) {
287 ALOGE("GetEntryForPath fails");
288 return nullptr;
289 }
290
291 ok = fs_mgr_update_logical_partition(system_fstab_entry);
292 if (!ok) {
293 ALOGE("fs_mgr_update_logical_partition fails");
294 return nullptr;
295 }
296
297 CHECK(out_system_path != nullptr);
298 *out_system_path = system_fstab_entry->blk_device;
299
300 std::string out_public_key_data;
301 std::string out_avb_partition_name;
302 std::unique_ptr<android::fs_mgr::VBMetaData> vbmeta =
303 android::fs_mgr::LoadAndVerifyVbmeta(
304 *system_fstab_entry, "" /* expected_key_blob */, &out_public_key_data,
305 &out_avb_partition_name, out_verify_result);
306 if (vbmeta == nullptr) {
307 ALOGE("LoadAndVerifyVbmeta fails");
308 return nullptr;
309 }
310
311 if (out_public_key_data.empty()) {
312 ALOGE("The GSI image is not signed");
313 return nullptr;
314 }
315
316 if (!ValidatePublicKeyBlob(out_public_key_data)) {
317 ALOGE("The GSI image is not signed by an official key");
318 return nullptr;
319 }
320
321 std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> descriptor =
322 android::fs_mgr::GetHashtreeDescriptor("system", std::move(*vbmeta));
323 if (descriptor == nullptr) {
324 ALOGE("GetHashtreeDescriptor fails");
325 return nullptr;
326 }
327
328 return descriptor;
329 }
330
331 // Loads contents and metadata of logical system partition, calculates
332 // the hashtree, and compares with the metadata.
TEST(AvbTest,SystemHashtree)333 TEST(AvbTest, SystemHashtree) {
334 android::fs_mgr::VBMetaVerifyResult verify_result;
335 std::string system_path;
336 std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> descriptor =
337 GetSystemHashtreeDescriptor(&verify_result, &system_path);
338 ASSERT_TRUE(descriptor);
339
340 ALOGI("System partition is %s", system_path.c_str());
341
342 // TODO: Skip assertion when running with non-compliance configuration.
343 EXPECT_EQ(verify_result, android::fs_mgr::VBMetaVerifyResult::kSuccess);
344 EXPECT_NE(verify_result,
345 android::fs_mgr::VBMetaVerifyResult::kErrorVerification)
346 << "The system image is not an officially signed GSI.";
347
348 const std::string &salt_str = descriptor->salt;
349 const std::string &expected_digest_str = descriptor->root_digest;
350
351 android::base::unique_fd fd(open(system_path.c_str(), O_RDONLY));
352 ASSERT_GE(fd, 0) << "Fail to open system partition. Try 'adb root'.";
353
354 const std::string hash_algorithm(
355 reinterpret_cast<const char *>(descriptor->hash_algorithm));
356 ALOGI("hash_algorithm = %s", hash_algorithm.c_str());
357
358 std::unique_ptr<ShaHasher> hasher = CreateShaHasher(hash_algorithm);
359 ASSERT_TRUE(hasher);
360
361 std::vector<uint8_t> salt, expected_digest;
362 bool ok = HexToBytes(salt_str, &salt);
363 ASSERT_TRUE(ok) << "Invalid salt in descriptor: " << salt_str;
364 ok = HexToBytes(expected_digest_str, &expected_digest);
365 ASSERT_TRUE(ok) << "Invalid digest in descriptor: " << expected_digest_str;
366 ASSERT_EQ(expected_digest.size(), hasher->GetDigestSize());
367
368 ALOGI("image_size = %llu", (unsigned long long)descriptor->image_size);
369 ALOGI("data_block_size = %u", descriptor->data_block_size);
370 ALOGI("hash_block_size = %u", descriptor->hash_block_size);
371 ALOGI("tree_offset = %llu", (unsigned long long)descriptor->tree_offset);
372 ALOGI("tree_size = %llu", (unsigned long long)descriptor->tree_size);
373
374 std::string error_message = VerifyHashtree(
375 fd, descriptor->image_size, salt, descriptor->data_block_size,
376 descriptor->hash_block_size, descriptor->tree_offset,
377 descriptor->tree_size, *hasher, expected_digest);
378 ASSERT_EQ(error_message, "");
379 }
380
381 // Finds the next word consisting of non-whitespace characters in a string.
382 //
383 // Arguments:
384 // str: The string to be searched for the next word.
385 // pos: The starting position to search for the next word.
386 // This function sets it to the past-the-end position of the word.
387 //
388 // Returns:
389 // The starting position of the word.
390 // If there is no next word, this function does not change pos and returns
391 // std::string::npos.
NextWord(const std::string & str,size_t * pos)392 static size_t NextWord(const std::string &str, size_t *pos) {
393 const char *whitespaces = " \t\r\n";
394 size_t start = str.find_first_not_of(whitespaces, *pos);
395 if (start == std::string::npos) {
396 return start;
397 }
398 *pos = str.find_first_of(whitespaces, start);
399 if (*pos == std::string::npos) {
400 *pos = str.size();
401 }
402 return start;
403 }
404
405 // Compares device mapper table with system hashtree descriptor.
TEST(AvbTest,SystemDescriptor)406 TEST(AvbTest, SystemDescriptor) {
407 // Get system hashtree descriptor.
408
409 android::fs_mgr::VBMetaVerifyResult verify_result;
410 std::string system_path;
411 std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor> descriptor =
412 GetSystemHashtreeDescriptor(&verify_result, &system_path);
413 ASSERT_TRUE(descriptor);
414
415 // TODO: Assert when running with compliance configuration.
416 // The SystemHashtree function asserts verify_result.
417 if (verify_result != android::fs_mgr::VBMetaVerifyResult::kSuccess) {
418 ALOGW("The system image is not an officially signed GSI.");
419 }
420
421 // Get device mapper table.
422 android::dm::DeviceMapper &device_mapper =
423 android::dm::DeviceMapper::Instance();
424 std::vector<android::dm::DeviceMapper::TargetInfo> table;
425 bool ok = device_mapper.GetTableInfo("system-verity", &table);
426 ASSERT_TRUE(ok) << "GetTableInfo fails";
427 ASSERT_EQ(table.size(), 1);
428 const android::dm::DeviceMapper::TargetInfo &target = table[0];
429 // Sample output:
430 // Device mapper table for system-verity:
431 // 0-1783288: verity, 1 253:0 253:0 4096 4096 222911 222911 sha1
432 // 6b2b46715a2d27c53cc7f91fe63ce798ff1f3df7
433 // 65bc99ca8e97379d4f7adc66664941acc0a8e682 10 restart_on_corruption
434 // ignore_zero_blocks use_fec_from_device 253:0 fec_blocks 224668 fec_start
435 // 224668 fec_roots 2
436 ALOGI("Device mapper table for system-verity:\n%llu-%llu: %s, %s",
437 target.spec.sector_start, target.spec.sector_start + target.spec.length,
438 target.spec.target_type, target.data.c_str());
439 EXPECT_EQ(strcmp(target.spec.target_type, "verity"), 0);
440
441 // Compare the target's positional parameters with the descriptor. Reference:
442 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#mapping-table-for-verity-target
443 std::array<std::string, 10> descriptor_values = {
444 std::to_string(descriptor->dm_verity_version),
445 "", // skip data_dev
446 "", // skip hash_dev
447 std::to_string(descriptor->data_block_size),
448 std::to_string(descriptor->hash_block_size),
449 std::to_string(descriptor->image_size /
450 descriptor->data_block_size), // #blocks
451 std::to_string(descriptor->image_size /
452 descriptor->data_block_size), // hash_start
453 reinterpret_cast<const char *>(descriptor->hash_algorithm),
454 descriptor->root_digest,
455 descriptor->salt,
456 };
457
458 size_t next_pos = 0;
459 for (const std::string &descriptor_value : descriptor_values) {
460 size_t begin_pos = NextWord(target.data, &next_pos);
461 ASSERT_NE(begin_pos, std::string::npos);
462 if (!descriptor_value.empty()) {
463 EXPECT_EQ(target.data.compare(begin_pos, next_pos - begin_pos,
464 descriptor_value),
465 0);
466 }
467 }
468
469 // Compare the target's optional parameters with the descriptor.
470 unsigned long opt_param_count;
471 {
472 size_t begin_pos = NextWord(target.data, &next_pos);
473 ASSERT_NE(begin_pos, std::string::npos);
474 opt_param_count =
475 std::stoul(target.data.substr(begin_pos, next_pos - begin_pos));
476 }
477 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#optional-parameters
478 std::set<std::string> opt_params = {
479 "check_at_most_once",
480 "ignore_corruption",
481 "ignore_zero_blocks",
482 "restart_on_corruption",
483 };
484 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#optional-fec-forward-error-correction-parameters
485 std::map<std::string, std::string> opt_fec_params = {
486 {"fec_blocks", ""},
487 {"fec_roots", ""},
488 {"fec_start", ""},
489 {"use_fec_from_device", ""},
490 };
491
492 for (unsigned long i = 0; i < opt_param_count; i++) {
493 size_t begin_pos = NextWord(target.data, &next_pos);
494 ASSERT_NE(begin_pos, std::string::npos);
495 const std::string param_name(target.data, begin_pos, next_pos - begin_pos);
496 if (opt_fec_params.count(param_name)) {
497 i++;
498 ASSERT_LT(i, opt_param_count);
499 begin_pos = NextWord(target.data, &next_pos);
500 ASSERT_NE(begin_pos, std::string::npos);
501 opt_fec_params[param_name] =
502 target.data.substr(begin_pos, next_pos - begin_pos);
503 } else {
504 ASSERT_NE(opt_params.count(param_name), 0)
505 << "Unknown dm-verity target parameter: " << param_name;
506 }
507 }
508
509 EXPECT_EQ(opt_fec_params["fec_roots"],
510 std::to_string(descriptor->fec_num_roots));
511 EXPECT_EQ(
512 opt_fec_params["fec_blocks"],
513 std::to_string(descriptor->fec_offset / descriptor->data_block_size));
514 EXPECT_EQ(
515 opt_fec_params["fec_start"],
516 std::to_string(descriptor->fec_offset / descriptor->data_block_size));
517 // skip use_fec_from_device
518
519 ASSERT_EQ(NextWord(target.data, &next_pos), std::string::npos);
520 }
521
VerifyHashAlgorithm(const AvbHashtreeDescriptor * descriptor)522 static void VerifyHashAlgorithm(const AvbHashtreeDescriptor* descriptor) {
523 AvbHashtreeDescriptor hashtree_descriptor;
524 ASSERT_TRUE(avb_hashtree_descriptor_validate_and_byteswap(
525 descriptor, &hashtree_descriptor))
526 << "hash tree descriptor is invalid.";
527
528 auto partition_name_ptr = reinterpret_cast<const uint8_t*>(descriptor) +
529 sizeof(AvbHashtreeDescriptor);
530 std::string partition_name(
531 partition_name_ptr,
532 partition_name_ptr + hashtree_descriptor.partition_name_len);
533
534 if (avb_strcmp(
535 reinterpret_cast<const char*>(hashtree_descriptor.hash_algorithm),
536 "sha1") == 0) {
537 FAIL() << "The hash tree algorithm cannot be SHA1 for partition "
538 << partition_name;
539 }
540 }
541
542 // In VTS, a boot-debug.img or vendor_boot-debug.img, which is not release
543 // key signed, will be used. In this case, The AvbSlotVerifyResult returned
544 // from libavb->avb_slot_verify() might be the following non-fatal errors.
545 // We should allow them in VTS because it might not be
546 // AVB_SLOT_VERIFY_RESULT_OK.
CheckAvbSlotVerifyResult(AvbSlotVerifyResult result)547 static bool CheckAvbSlotVerifyResult(AvbSlotVerifyResult result) {
548 switch (result) {
549 case AVB_SLOT_VERIFY_RESULT_OK:
550 case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
551 case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
552 case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
553 return true;
554
555 case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
556 case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
557 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
558 case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
559 case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
560 return false;
561 }
562
563 return false;
564 }
565
LoadAndVerifyAvbSlotDataForCurrentSlot(AvbSlotVerifyData ** avb_slot_data)566 static void LoadAndVerifyAvbSlotDataForCurrentSlot(
567 AvbSlotVerifyData** avb_slot_data) {
568 // Use an empty suffix string for non-A/B devices.
569 std::string suffix;
570 if (android::base::GetBoolProperty("ro.build.ab_update", false)) {
571 suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
572 ASSERT_TRUE(!suffix.empty()) << "Failed to get suffix for the current slot";
573 }
574
575 const char* requested_partitions[] = {nullptr};
576
577 // AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR is needed for boot-debug.img
578 // or vendor_boot-debug.img, which is not release key signed.
579 auto avb_ops = avb_ops_user_new();
580 auto verify_result =
581 avb_slot_verify(avb_ops, requested_partitions, suffix.c_str(),
582 AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
583 AVB_HASHTREE_ERROR_MODE_EIO, avb_slot_data);
584 ASSERT_TRUE(CheckAvbSlotVerifyResult(verify_result))
585 << "Failed to verify avb slot data " << verify_result;
586 }
587
588 // Check the correct hashtree algorithm is used.
TEST(AvbTest,HashtreeAlgorithm)589 TEST(AvbTest, HashtreeAlgorithm) {
590 constexpr auto S_API_LEVEL = 31;
591
592 uint32_t vendor_api_level = GetVendorApiLevel();
593 GTEST_LOG_(INFO) << "Vendor API level is " << vendor_api_level;
594 if (vendor_api_level < S_API_LEVEL) {
595 GTEST_LOG_(INFO)
596 << "Exempt from avb hash tree test due to old starting API level";
597 return;
598 }
599
600 // Note we don't iterate the entries in fstab; because we don't know if a
601 // partition uses hashtree or not.
602 AvbSlotVerifyData* avb_slot_data;
603 LoadAndVerifyAvbSlotDataForCurrentSlot(&avb_slot_data);
604 ASSERT_NE(nullptr, avb_slot_data) << "Failed to load avb slot verify data";
605 std::unique_ptr<AvbSlotVerifyData, decltype(&avb_slot_verify_data_free)>
606 scope_guard(avb_slot_data, avb_slot_verify_data_free);
607
608 // Iterate over the loaded vbmeta structs
609 for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {
610 std::string partition_name = avb_slot_data->vbmeta_images[i].partition_name;
611 const auto& vbmeta_image = avb_slot_data->vbmeta_images[i];
612
613 size_t num_descriptors;
614 auto descriptors = avb_descriptor_get_all(
615 vbmeta_image.vbmeta_data, vbmeta_image.vbmeta_size, &num_descriptors);
616 // Iterate over the hashtree descriptors
617 for (size_t n = 0; n < num_descriptors; n++) {
618 if (avb_be64toh(descriptors[n]->tag) != AVB_DESCRIPTOR_TAG_HASHTREE) {
619 continue;
620 }
621
622 VerifyHashAlgorithm(
623 reinterpret_cast<const AvbHashtreeDescriptor*>(descriptors[n]));
624 }
625 }
626 }
627
628 static constexpr char VBMETA_PROPERTY[] = "ro.boot.vbmeta.digest";
629
GetVbmetaDigestProperty()630 static std::optional<std::vector<uint8_t>> GetVbmetaDigestProperty() {
631 std::string default_value = "not found";
632 auto vbmeta_string =
633 ::android::base::GetProperty(VBMETA_PROPERTY, default_value);
634 if (vbmeta_string == default_value) {
635 return std::nullopt;
636 }
637
638 std::vector<uint8_t> vbmeta_digest;
639 if (HexToBytes(vbmeta_string, &vbmeta_digest)) {
640 return vbmeta_digest;
641 } else {
642 return std::nullopt;
643 }
644 }
645
646 // Check that a calculated vbmeta digest matches the Android property value.
TEST(AvbTest,CalculatedVbmetaMatchesProperty)647 TEST(AvbTest, CalculatedVbmetaMatchesProperty) {
648 // Get the vbmeta digest value from the Android property.
649 auto vbmeta_digest = GetVbmetaDigestProperty();
650 if (!vbmeta_digest.has_value()) {
651 GTEST_SKIP() << "No " << VBMETA_PROPERTY << " property value available";
652 }
653
654 AvbSlotVerifyData *avb_slot_data;
655 LoadAndVerifyAvbSlotDataForCurrentSlot(&avb_slot_data);
656
657 // Unfortunately, bootloader is not required to report the algorithm used
658 // to calculate the digest. There are only two supported options though,
659 // SHA256 and SHA512. The VBMeta digest property value must match one of
660 // these.
661 std::vector<uint8_t> digest256(AVB_SHA256_DIGEST_SIZE);
662 std::vector<uint8_t> digest512(AVB_SHA512_DIGEST_SIZE);
663
664 avb_slot_verify_data_calculate_vbmeta_digest(
665 avb_slot_data, AVB_DIGEST_TYPE_SHA256, digest256.data());
666 avb_slot_verify_data_calculate_vbmeta_digest(
667 avb_slot_data, AVB_DIGEST_TYPE_SHA512, digest512.data());
668
669 ASSERT_TRUE((vbmeta_digest == digest256) || (vbmeta_digest == digest512))
670 << "vbmeta digest from property (" << VBMETA_PROPERTY << "="
671 << BytesToHex(vbmeta_digest.value())
672 << ") does not match computed digest (sha256: " << BytesToHex(digest256)
673 << ", sha512: " << BytesToHex(digest512) << ")";
674 }
675