From b345651e6c85ac7a7a8d637ab38fee61397d6996 Mon Sep 17 00:00:00 2001 From: Seungjae Yoo Date: Mon, 11 Mar 2024 14:41:22 +0900 Subject: [PATCH] Add unit test for parsing avb info Bug: 328195652 Test: atest --host releasetools_test Test: sign_target_files_apks Change-Id: Ie38c3883907bc70c794606b20caf55a70dbcdf7c --- tools/releasetools/sign_target_files_apks.py | 92 ++++++++++--------- .../test_sign_target_files_apks.py | 88 +++++++++++++++++- 2 files changed, 133 insertions(+), 47 deletions(-) diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py index 52d95d88c6..5d92eded43 100755 --- a/tools/releasetools/sign_target_files_apks.py +++ b/tools/releasetools/sign_target_files_apks.py @@ -822,6 +822,52 @@ def ProcessTargetFiles(input_tf_zip: zipfile.ZipFile, output_tf_zip, misc_info, # Write back misc_info with the latest values. ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info) +# Parse string output of `avbtool info_image`. +def ParseAvbInfo(info_raw): + # line_matcher is for parsing each output line of `avbtool info_image`. + # example string input: " Hash Algorithm: sha1" + # example matched input: (" ", "Hash Algorithm", "sha1") + line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$') + # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`. + # example string input: "example_prop_key -> 'example_prop_value'" + # example matched output: ("example_prop_key", "example_prop_value") + prop_matcher = re.compile(r"(.+)\s->\s'(.+)'") + info = {} + indent_stack = [[-1, info]] + for line_info_raw in info_raw.split('\n'): + # Parse the line + line_info_parsed = line_matcher.match(line_info_raw) + if not line_info_parsed: + continue + indent = len(line_info_parsed.group(1)) + key = line_info_parsed.group(2).strip() + value = line_info_parsed.group(3).strip() + + # Pop indentation stack + while indent <= indent_stack[-1][0]: + del indent_stack[-1] + + # Insert information into 'info'. + cur_info = indent_stack[-1][1] + if value == "": + if key == "Descriptors": + empty_list = [] + cur_info[key] = empty_list + indent_stack.append([indent, empty_list]) + else: + empty_dict = {} + cur_info.append({key:empty_dict}) + indent_stack.append([indent, empty_dict]) + elif key == "Prop": + prop_parsed = prop_matcher.match(value) + if not prop_parsed: + raise ValueError( + "Failed to parse prop while getting avb information.") + cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}}) + else: + cur_info[key] = value + return info + def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info): # Get avb information about the image by parsing avbtool info_image. def GetAvbInfo(avbtool, image_name): @@ -830,51 +876,7 @@ def ReplaceKeyInAvbHashtreeFooter(image, new_key, new_algorithm, misc_info): avbtool, 'info_image', '--image', image_name ]) - - # line_matcher is for parsing each output line of `avbtool info_image`. - # example string input: " Hash Algorithm: sha1" - # example matched input: (" ", "Hash Algorithm", "sha1") - line_matcher = re.compile(r'^(\s*)([^:]+):\s*(.*)$') - # prop_matcher is for parsing value part of 'Prop' in `avbtool info_image`. - # example string input: "example_prop_key -> 'example_prop_value'" - # example matched output: ("example_prop_key", "example_prop_value") - prop_matcher = re.compile(r"(.+)\s->\s'(.+)'") - info = {} - indent_stack = [[-1, info]] - for line_info_raw in info_raw.split('\n'): - # Parse the line - line_info_parsed = line_matcher.match(line_info_raw) - if not line_info_parsed: - continue - indent = len(line_info_parsed.group(1)) - key = line_info_parsed.group(2).strip() - value = line_info_parsed.group(3).strip() - - # Pop indentation stack - while indent <= indent_stack[-1][0]: - del indent_stack[-1] - - # Insert information into 'info'. - cur_info = indent_stack[-1][1] - if value == "": - if key == "Descriptors": - empty_list = [] - cur_info[key] = empty_list - indent_stack.append([indent, empty_list]) - else: - empty_dict = {} - cur_info.append({key:empty_dict}) - indent_stack.append([indent, empty_dict]) - elif key == "Prop": - prop_parsed = prop_matcher.match(value) - if not prop_parsed: - raise ValueError( - "Failed to parse prop while getting avb information.") - cur_info.append({key:{prop_parsed.group(1):prop_parsed.group(2)}}) - else: - cur_info[key] = value - - return info + return ParseAvbInfo(info_raw) # Get hashtree descriptor from info def GetAvbHashtreeDescriptor(avb_info): diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py index 9cc6df428c..7ac1cff0bb 100644 --- a/tools/releasetools/test_sign_target_files_apks.py +++ b/tools/releasetools/test_sign_target_files_apks.py @@ -22,8 +22,9 @@ import zipfile import common import test_utils from sign_target_files_apks import ( - CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo, - ReplaceCerts, RewriteAvbProps, RewriteProps, WriteOtacerts) + CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ParseAvbInfo, + ReadApexKeysInfo, ReplaceCerts, RewriteAvbProps, RewriteProps, + WriteOtacerts) class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase): @@ -535,3 +536,86 @@ name="apex.apexd_test_different_app.apex" public_key="system/apex/apexd/apexd_te 'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem', 'build/make/target/product/security/testkey', None), }, keys_info) + + def test_ParseAvbInfo(self): + avb_info_string = """ + Footer version: 1.0 + Image size: 9999999 bytes + Original image size: 8888888 bytes + VBMeta offset: 7777777 + VBMeta size: 1111 bytes + -- + Minimum libavb version: 1.0 + Header Block: 222 bytes + Authentication Block: 333 bytes + Auxiliary Block: 888 bytes + Public key (sha1): abababababababababababababababababababab + Algorithm: SHA256_RSA2048 + Rollback Index: 0 + Flags: 0 + Rollback Index Location: 0 + Release String: 'avbtool 1.3.0' + Descriptors: + Hashtree descriptor: + Version of dm-verity: 1 + Image Size: 8888888 bytes + Tree Offset: 8888888 + Tree Size: 44444 bytes + Data Block Size: 4444 bytes + Hash Block Size: 4444 bytes + FEC num roots: 0 + FEC offset: 0 + FEC size: 0 bytes + Hash Algorithm: sha1 + Partition Name: partition-name + Salt: cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd + Root Digest: efefefefefefefefefefefefefefefefefef + Flags: 0 + Prop: prop.key -> 'prop.value' + """ + + self.assertEqual( + { + 'Footer version': '1.0', + 'Image size': '9999999 bytes', + 'Original image size': '8888888 bytes', + 'VBMeta offset': '7777777', + 'VBMeta size': '1111 bytes', + 'Minimum libavb version': '1.0', + 'Header Block': '222 bytes', + 'Authentication Block': '333 bytes', + 'Auxiliary Block': '888 bytes', + 'Public key (sha1)': 'abababababababababababababababababababab', + 'Algorithm': 'SHA256_RSA2048', + 'Rollback Index': '0', + 'Flags': '0', + 'Rollback Index Location': '0', + 'Release String': "'avbtool 1.3.0'", + 'Descriptors': [ + { + 'Hashtree descriptor': { + 'Version of dm-verity': '1', + 'Image Size': '8888888 bytes', + 'Tree Offset': '8888888', + 'Tree Size': '44444 bytes', + 'Data Block Size': '4444 bytes', + 'Hash Block Size': '4444 bytes', + 'FEC num roots': '0', + 'FEC offset': '0', + 'FEC size': '0 bytes', + 'Hash Algorithm': 'sha1', + 'Partition Name': 'partition-name', + 'Salt': 'cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd', + 'Root Digest': 'efefefefefefefefefefefefefefefefefef', + 'Flags': '0', + } + }, + { + 'Prop': { + 'prop.key': 'prop.value', + } + }, + ], + }, + ParseAvbInfo(avb_info_string), + ) \ No newline at end of file