platform_build/tools/releasetools/test_ota_from_target_files.py
Tianjie Xu 21e6deb647 Call delta_generator to get the signature size
The signature size is needed during payload hashing and signing.
We used to sign the update_engine's payload with RSA keys only. In
this case, the signature size always equals the key size. But the
assumption is no longer true for EC keys, whose DER-encoded signature
size is a variant with a maximum size.

Therefore, we always give the maximum signature size to the delta
generator, who then add paddings to the real signature if necessary.
The maximum signature size is calculated by calling the delta_generator
with the new option '--calculate_signature_size'. For custom payload
signers, we also deprecate the '--payload_signer_key_size' and replace
it with '--payload_signer_maximum_signature_size'.

The EC key in the test is generated with:
The EC key in the unittest is generated with the command:
openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
openssl pkey -in prime256v1-key.pem -out testkey_EC.key

Bug: 141244025
Test: sign and verify a payload
Change-Id: Ife6e269d8aa3d870405aca20086330f1795e167f
2019-10-12 00:36:54 +00:00

1317 lines
49 KiB
Python

#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import copy
import os
import os.path
import zipfile
import common
import test_utils
from ota_from_target_files import (
_LoadOemDicts, AbOtaPropertyFiles, FinalizeMetadata,
GetPackageMetadata, GetTargetFilesZipForSecondaryImages,
GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles,
Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
StreamingPropertyFiles, WriteFingerprintAssertion)
def construct_target_files(secondary=False):
"""Returns a target-files.zip file for generating OTA packages."""
target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
with zipfile.ZipFile(target_files, 'w') as target_files_zip:
# META/update_engine_config.txt
target_files_zip.writestr(
'META/update_engine_config.txt',
"PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
# META/postinstall_config.txt
target_files_zip.writestr(
POSTINSTALL_CONFIG,
'\n'.join([
"RUN_POSTINSTALL_system=true",
"POSTINSTALL_PATH_system=system/bin/otapreopt_script",
"FILESYSTEM_TYPE_system=ext4",
"POSTINSTALL_OPTIONAL_system=true",
]))
ab_partitions = [
('IMAGES', 'boot'),
('IMAGES', 'system'),
('IMAGES', 'vendor'),
('RADIO', 'bootloader'),
('RADIO', 'modem'),
]
# META/ab_partitions.txt
target_files_zip.writestr(
'META/ab_partitions.txt',
'\n'.join([partition[1] for partition in ab_partitions]))
# Create dummy images for each of them.
for path, partition in ab_partitions:
target_files_zip.writestr(
'{}/{}.img'.format(path, partition),
os.urandom(len(partition)))
# system_other shouldn't appear in META/ab_partitions.txt.
if secondary:
target_files_zip.writestr('IMAGES/system_other.img',
os.urandom(len("system_other")))
return target_files
class LoadOemDictsTest(test_utils.ReleaseToolsTestCase):
def test_NoneDict(self):
self.assertIsNone(_LoadOemDicts(None))
def test_SingleDict(self):
dict_file = common.MakeTempFile()
with open(dict_file, 'w') as dict_fp:
dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
oem_dicts = _LoadOemDicts([dict_file])
self.assertEqual(1, len(oem_dicts))
self.assertEqual('foo', oem_dicts[0]['xyz'])
self.assertEqual('bar', oem_dicts[0]['a.b.c'])
def test_MultipleDicts(self):
oem_source = []
for i in range(3):
dict_file = common.MakeTempFile()
with open(dict_file, 'w') as dict_fp:
dict_fp.write(
'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
oem_source.append(dict_file)
oem_dicts = _LoadOemDicts(oem_source)
self.assertEqual(3, len(oem_dicts))
for i, oem_dict in enumerate(oem_dicts):
self.assertEqual('2', oem_dict['def'])
self.assertEqual('foo', oem_dict['xyz'])
self.assertEqual('bar', oem_dict['a.b.c'])
self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
TEST_TARGET_INFO_DICT = {
'build.prop' : {
'ro.product.device' : 'product-device',
'ro.build.fingerprint' : 'build-fingerprint-target',
'ro.build.version.incremental' : 'build-version-incremental-target',
'ro.build.version.sdk' : '27',
'ro.build.version.security_patch' : '2017-12-01',
'ro.build.date.utc' : '1500000000',
},
}
TEST_SOURCE_INFO_DICT = {
'build.prop' : {
'ro.product.device' : 'product-device',
'ro.build.fingerprint' : 'build-fingerprint-source',
'ro.build.version.incremental' : 'build-version-incremental-source',
'ro.build.version.sdk' : '25',
'ro.build.version.security_patch' : '2016-12-01',
'ro.build.date.utc' : '1400000000',
},
}
TEST_INFO_DICT_USES_OEM_PROPS = {
'build.prop' : {
'ro.product.name' : 'product-name',
'ro.build.thumbprint' : 'build-thumbprint',
'ro.build.bar' : 'build-bar',
},
'vendor.build.prop' : {
'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
},
'property1' : 'value1',
'property2' : 4096,
'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
}
TEST_OEM_DICTS = [
{
'ro.product.brand' : 'brand1',
'ro.product.device' : 'device1',
},
{
'ro.product.brand' : 'brand2',
'ro.product.device' : 'device2',
},
{
'ro.product.brand' : 'brand3',
'ro.product.device' : 'device3',
},
]
def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
# Reset the global options as in ota_from_target_files.py.
common.OPTIONS.incremental_source = None
common.OPTIONS.downgrade = False
common.OPTIONS.retrofit_dynamic_partitions = False
common.OPTIONS.timestamp = False
common.OPTIONS.wipe_user_data = False
common.OPTIONS.no_signing = False
common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
common.OPTIONS.key_passwords = {
common.OPTIONS.package_key : None,
}
common.OPTIONS.search_path = test_utils.get_search_path()
def test_GetPackageMetadata_abOta_full(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
target_info_dict['ab_update'] = 'true'
target_info = common.BuildInfo(target_info_dict, None)
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
'ota-type' : 'AB',
'ota-required-cache' : '0',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
metadata)
def test_GetPackageMetadata_abOta_incremental(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
target_info_dict['ab_update'] = 'true'
target_info = common.BuildInfo(target_info_dict, None)
source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
common.OPTIONS.incremental_source = ''
metadata = GetPackageMetadata(target_info, source_info)
self.assertDictEqual(
{
'ota-type' : 'AB',
'ota-required-cache' : '0',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
'pre-build-incremental' : 'build-version-incremental-source',
},
metadata)
def test_GetPackageMetadata_nonAbOta_full(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
metadata)
def test_GetPackageMetadata_nonAbOta_incremental(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
common.OPTIONS.incremental_source = ''
metadata = GetPackageMetadata(target_info, source_info)
self.assertDictEqual(
{
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
'pre-build-incremental' : 'build-version-incremental-source',
},
metadata)
def test_GetPackageMetadata_wipe(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
common.OPTIONS.wipe_user_data = True
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
'ota-type' : 'BLOCK',
'ota-wipe' : 'yes',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
metadata)
def test_GetPackageMetadata_retrofitDynamicPartitions(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
common.OPTIONS.retrofit_dynamic_partitions = True
metadata = GetPackageMetadata(target_info)
self.assertDictEqual(
{
'ota-retrofit-dynamic-partitions' : 'yes',
'ota-type' : 'BLOCK',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1500000000',
'pre-device' : 'product-device',
},
metadata)
@staticmethod
def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
(target_info['build.prop']['ro.build.date.utc'],
source_info['build.prop']['ro.build.date.utc']) = (
source_info['build.prop']['ro.build.date.utc'],
target_info['build.prop']['ro.build.date.utc'])
def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
self._test_GetPackageMetadata_swapBuildTimestamps(
target_info_dict, source_info_dict)
target_info = common.BuildInfo(target_info_dict, None)
source_info = common.BuildInfo(source_info_dict, None)
common.OPTIONS.incremental_source = ''
self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
source_info)
def test_GetPackageMetadata_downgrade(self):
target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
self._test_GetPackageMetadata_swapBuildTimestamps(
target_info_dict, source_info_dict)
target_info = common.BuildInfo(target_info_dict, None)
source_info = common.BuildInfo(source_info_dict, None)
common.OPTIONS.incremental_source = ''
common.OPTIONS.downgrade = True
common.OPTIONS.wipe_user_data = True
metadata = GetPackageMetadata(target_info, source_info)
self.assertDictEqual(
{
'ota-downgrade' : 'yes',
'ota-type' : 'BLOCK',
'ota-wipe' : 'yes',
'post-build' : 'build-fingerprint-target',
'post-build-incremental' : 'build-version-incremental-target',
'post-sdk-level' : '27',
'post-security-patch-level' : '2017-12-01',
'post-timestamp' : '1400000000',
'pre-device' : 'product-device',
'pre-build' : 'build-fingerprint-source',
'pre-build-incremental' : 'build-version-incremental-source',
},
metadata)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipForSecondaryImages(self):
input_file = construct_target_files(secondary=True)
target_file = GetTargetFilesZipForSecondaryImages(input_file)
with zipfile.ZipFile(target_file) as verify_zip:
namelist = verify_zip.namelist()
ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()
self.assertIn('META/ab_partitions.txt', namelist)
self.assertIn('IMAGES/system.img', namelist)
self.assertIn('RADIO/bootloader.img', namelist)
self.assertIn(POSTINSTALL_CONFIG, namelist)
self.assertNotIn('IMAGES/boot.img', namelist)
self.assertNotIn('IMAGES/system_other.img', namelist)
self.assertNotIn('IMAGES/system.map', namelist)
self.assertNotIn('RADIO/modem.img', namelist)
expected_ab_partitions = ['system', 'bootloader']
self.assertEqual('\n'.join(expected_ab_partitions), ab_partitions)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):
input_file = construct_target_files(secondary=True)
target_file = GetTargetFilesZipForSecondaryImages(
input_file, skip_postinstall=True)
with zipfile.ZipFile(target_file) as verify_zip:
namelist = verify_zip.namelist()
self.assertIn('META/ab_partitions.txt', namelist)
self.assertIn('IMAGES/system.img', namelist)
self.assertIn('RADIO/bootloader.img', namelist)
self.assertNotIn('IMAGES/boot.img', namelist)
self.assertNotIn('IMAGES/system_other.img', namelist)
self.assertNotIn('IMAGES/system.map', namelist)
self.assertNotIn('RADIO/modem.img', namelist)
self.assertNotIn(POSTINSTALL_CONFIG, namelist)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipForSecondaryImages_withoutRadioImages(self):
input_file = construct_target_files(secondary=True)
common.ZipDelete(input_file, 'RADIO/bootloader.img')
common.ZipDelete(input_file, 'RADIO/modem.img')
target_file = GetTargetFilesZipForSecondaryImages(input_file)
with zipfile.ZipFile(target_file) as verify_zip:
namelist = verify_zip.namelist()
self.assertIn('META/ab_partitions.txt', namelist)
self.assertIn('IMAGES/system.img', namelist)
self.assertIn(POSTINSTALL_CONFIG, namelist)
self.assertNotIn('IMAGES/boot.img', namelist)
self.assertNotIn('IMAGES/system_other.img', namelist)
self.assertNotIn('IMAGES/system.map', namelist)
self.assertNotIn('RADIO/bootloader.img', namelist)
self.assertNotIn('RADIO/modem.img', namelist)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipForSecondaryImages_dynamicPartitions(self):
input_file = construct_target_files(secondary=True)
misc_info = '\n'.join([
'use_dynamic_partition_size=true',
'use_dynamic_partitions=true',
'dynamic_partition_list=system vendor product',
'super_partition_groups=google_dynamic_partitions',
'super_google_dynamic_partitions_group_size=4873781248',
'super_google_dynamic_partitions_partition_list=system vendor product',
])
dynamic_partitions_info = '\n'.join([
'super_partition_groups=google_dynamic_partitions',
'super_google_dynamic_partitions_group_size=4873781248',
'super_google_dynamic_partitions_partition_list=system vendor product',
])
with zipfile.ZipFile(input_file, 'a') as append_zip:
common.ZipWriteStr(append_zip, 'META/misc_info.txt', misc_info)
common.ZipWriteStr(append_zip, 'META/dynamic_partitions_info.txt',
dynamic_partitions_info)
target_file = GetTargetFilesZipForSecondaryImages(input_file)
with zipfile.ZipFile(target_file) as verify_zip:
namelist = verify_zip.namelist()
updated_misc_info = verify_zip.read('META/misc_info.txt').decode()
updated_dynamic_partitions_info = verify_zip.read(
'META/dynamic_partitions_info.txt').decode()
self.assertIn('META/ab_partitions.txt', namelist)
self.assertIn('IMAGES/system.img', namelist)
self.assertIn(POSTINSTALL_CONFIG, namelist)
self.assertIn('META/misc_info.txt', namelist)
self.assertIn('META/dynamic_partitions_info.txt', namelist)
self.assertNotIn('IMAGES/boot.img', namelist)
self.assertNotIn('IMAGES/system_other.img', namelist)
self.assertNotIn('IMAGES/system.map', namelist)
# Check the vendor & product are removed from the partitions list.
expected_misc_info = misc_info.replace('system vendor product',
'system')
expected_dynamic_partitions_info = dynamic_partitions_info.replace(
'system vendor product', 'system')
self.assertEqual(expected_misc_info, updated_misc_info)
self.assertEqual(expected_dynamic_partitions_info,
updated_dynamic_partitions_info)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipWithoutPostinstallConfig(self):
input_file = construct_target_files()
target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
with zipfile.ZipFile(target_file) as verify_zip:
self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):
input_file = construct_target_files()
common.ZipDelete(input_file, POSTINSTALL_CONFIG)
target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
with zipfile.ZipFile(target_file) as verify_zip:
self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
def _test_FinalizeMetadata(self, large_entry=False):
entries = [
'required-entry1',
'required-entry2',
]
zip_file = PropertyFilesTest.construct_zip_package(entries)
# Add a large entry of 1 GiB if requested.
if large_entry:
with zipfile.ZipFile(zip_file, 'a') as zip_fp:
zip_fp.writestr(
# Using 'zoo' so that the entry stays behind others after signing.
'zoo',
'A' * 1024 * 1024 * 1024,
zipfile.ZIP_STORED)
metadata = {}
output_file = common.MakeTempFile(suffix='.zip')
needed_property_files = (
TestPropertyFiles(),
)
FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
self.assertIn('ota-test-property-files', metadata)
@test_utils.SkipIfExternalToolsUnavailable()
def test_FinalizeMetadata(self):
self._test_FinalizeMetadata()
@test_utils.SkipIfExternalToolsUnavailable()
def test_FinalizeMetadata_withNoSigning(self):
common.OPTIONS.no_signing = True
self._test_FinalizeMetadata()
@test_utils.SkipIfExternalToolsUnavailable()
def test_FinalizeMetadata_largeEntry(self):
self._test_FinalizeMetadata(large_entry=True)
@test_utils.SkipIfExternalToolsUnavailable()
def test_FinalizeMetadata_largeEntry_withNoSigning(self):
common.OPTIONS.no_signing = True
self._test_FinalizeMetadata(large_entry=True)
@test_utils.SkipIfExternalToolsUnavailable()
def test_FinalizeMetadata_insufficientSpace(self):
entries = [
'required-entry1',
'required-entry2',
'optional-entry1',
'optional-entry2',
]
zip_file = PropertyFilesTest.construct_zip_package(entries)
with zipfile.ZipFile(zip_file, 'a') as zip_fp:
zip_fp.writestr(
# 'foo-entry1' will appear ahead of all other entries (in alphabetical
# order) after the signing, which will in turn trigger the
# InsufficientSpaceException and an automatic retry.
'foo-entry1',
'A' * 1024 * 1024,
zipfile.ZIP_STORED)
metadata = {}
needed_property_files = (
TestPropertyFiles(),
)
output_file = common.MakeTempFile(suffix='.zip')
FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
self.assertIn('ota-test-property-files', metadata)
def test_WriteFingerprintAssertion_without_oem_props(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
source_info_dict['build.prop']['ro.build.fingerprint'] = (
'source-build-fingerprint')
source_info = common.BuildInfo(source_info_dict, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeFingerprint', 'source-build-fingerprint',
'build-fingerprint-target')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_source_oem_props(self):
target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
source_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_target_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
source_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
'build-thumbprint')],
script_writer.lines)
def test_WriteFingerprintAssertion_with_both_oem_props(self):
target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
self.TEST_OEM_DICTS)
source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
source_info_dict['build.prop']['ro.build.thumbprint'] = (
'source-build-thumbprint')
source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
script_writer = test_utils.MockScriptWriter()
WriteFingerprintAssertion(script_writer, target_info, source_info)
self.assertEqual(
[('AssertSomeThumbprint', 'build-thumbprint',
'source-build-thumbprint')],
script_writer.lines)
class TestPropertyFiles(PropertyFiles):
"""A class that extends PropertyFiles for testing purpose."""
def __init__(self):
super(TestPropertyFiles, self).__init__()
self.name = 'ota-test-property-files'
self.required = (
'required-entry1',
'required-entry2',
)
self.optional = (
'optional-entry1',
'optional-entry2',
)
class PropertyFilesTest(test_utils.ReleaseToolsTestCase):
def setUp(self):
common.OPTIONS.no_signing = False
@staticmethod
def construct_zip_package(entries):
zip_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(zip_file, 'w') as zip_fp:
for entry in entries:
zip_fp.writestr(
entry,
entry.replace('.', '-').upper(),
zipfile.ZIP_STORED)
return zip_file
@staticmethod
def _parse_property_files_string(data):
result = {}
for token in data.split(','):
name, info = token.split(':', 1)
result[name] = info
return result
def _verify_entries(self, input_file, tokens, entries):
for entry in entries:
offset, size = map(int, tokens[entry].split(':'))
with open(input_file, 'rb') as input_fp:
input_fp.seek(offset)
if entry == 'metadata':
expected = b'META-INF/COM/ANDROID/METADATA'
else:
expected = entry.replace('.', '-').upper().encode()
self.assertEqual(expected, input_fp.read(size))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Compute(self):
entries = (
'required-entry1',
'required-entry2',
)
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
property_files_string = property_files.Compute(zip_fp)
tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(3, len(tokens))
self._verify_entries(zip_file, tokens, entries)
def test_Compute_withOptionalEntries(self):
entries = (
'required-entry1',
'required-entry2',
'optional-entry1',
'optional-entry2',
)
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
property_files_string = property_files.Compute(zip_fp)
tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(5, len(tokens))
self._verify_entries(zip_file, tokens, entries)
def test_Compute_missingRequiredEntry(self):
entries = (
'required-entry2',
)
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
self.assertRaises(KeyError, property_files.Compute, zip_fp)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Finalize(self):
entries = [
'required-entry1',
'required-entry2',
'META-INF/com/android/metadata',
]
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
tokens = self._parse_property_files_string(streaming_metadata)
self.assertEqual(3, len(tokens))
# 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
# streaming metadata.
entries[2] = 'metadata'
self._verify_entries(zip_file, tokens, entries)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Finalize_assertReservedLength(self):
entries = (
'required-entry1',
'required-entry2',
'optional-entry1',
'optional-entry2',
'META-INF/com/android/metadata',
)
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
# First get the raw metadata string (i.e. without padding space).
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
raw_length = len(raw_metadata)
# Now pass in the exact expected length.
streaming_metadata = property_files.Finalize(zip_fp, raw_length)
self.assertEqual(raw_length, len(streaming_metadata))
# Or pass in insufficient length.
self.assertRaises(
PropertyFiles.InsufficientSpaceException,
property_files.Finalize,
zip_fp,
raw_length - 1)
# Or pass in a much larger size.
streaming_metadata = property_files.Finalize(
zip_fp,
raw_length + 20)
self.assertEqual(raw_length + 20, len(streaming_metadata))
self.assertEqual(' ' * 20, streaming_metadata[raw_length:])
def test_Verify(self):
entries = (
'required-entry1',
'required-entry2',
'optional-entry1',
'optional-entry2',
'META-INF/com/android/metadata',
)
zip_file = self.construct_zip_package(entries)
property_files = TestPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
# First get the raw metadata string (i.e. without padding space).
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
# Should pass the test if verification passes.
property_files.Verify(zip_fp, raw_metadata)
# Or raise on verification failure.
self.assertRaises(
AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
class StreamingPropertyFilesTest(PropertyFilesTest):
"""Additional sanity checks specialized for StreamingPropertyFiles."""
def test_init(self):
property_files = StreamingPropertyFiles()
self.assertEqual('ota-streaming-property-files', property_files.name)
self.assertEqual(
(
'payload.bin',
'payload_properties.txt',
),
property_files.required)
self.assertEqual(
(
'care_map.pb',
'care_map.txt',
'compatibility.zip',
),
property_files.optional)
def test_Compute(self):
entries = (
'payload.bin',
'payload_properties.txt',
'care_map.txt',
'compatibility.zip',
)
zip_file = self.construct_zip_package(entries)
property_files = StreamingPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
property_files_string = property_files.Compute(zip_fp)
tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(5, len(tokens))
self._verify_entries(zip_file, tokens, entries)
def test_Finalize(self):
entries = [
'payload.bin',
'payload_properties.txt',
'care_map.txt',
'compatibility.zip',
'META-INF/com/android/metadata',
]
zip_file = self.construct_zip_package(entries)
property_files = StreamingPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
tokens = self._parse_property_files_string(streaming_metadata)
self.assertEqual(5, len(tokens))
# 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
# streaming metadata.
entries[4] = 'metadata'
self._verify_entries(zip_file, tokens, entries)
def test_Verify(self):
entries = (
'payload.bin',
'payload_properties.txt',
'care_map.txt',
'compatibility.zip',
'META-INF/com/android/metadata',
)
zip_file = self.construct_zip_package(entries)
property_files = StreamingPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
# First get the raw metadata string (i.e. without padding space).
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
# Should pass the test if verification passes.
property_files.Verify(zip_fp, raw_metadata)
# Or raise on verification failure.
self.assertRaises(
AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
class AbOtaPropertyFilesTest(PropertyFilesTest):
"""Additional sanity checks specialized for AbOtaPropertyFiles."""
# The size for payload and metadata signature size.
SIGNATURE_SIZE = 256
def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
common.OPTIONS.wipe_user_data = False
common.OPTIONS.payload_signer = None
common.OPTIONS.payload_signer_args = None
common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
common.OPTIONS.key_passwords = {
common.OPTIONS.package_key : None,
}
def test_init(self):
property_files = AbOtaPropertyFiles()
self.assertEqual('ota-property-files', property_files.name)
self.assertEqual(
(
'payload.bin',
'payload_properties.txt',
),
property_files.required)
self.assertEqual(
(
'care_map.pb',
'care_map.txt',
'compatibility.zip',
),
property_files.optional)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetPayloadMetadataOffsetAndSize(self):
target_file = construct_target_files()
payload = Payload()
payload.Generate(target_file)
payload_signer = PayloadSigner()
payload.Sign(payload_signer)
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
payload.WriteToZip(output_zip)
# Find out the payload metadata offset and size.
property_files = AbOtaPropertyFiles()
with zipfile.ZipFile(output_file) as input_zip:
# pylint: disable=protected-access
payload_offset, metadata_total = (
property_files._GetPayloadMetadataOffsetAndSize(input_zip))
# The signature proto has the following format (details in
# /platform/system/update_engine/update_metadata.proto):
# message Signature {
# optional uint32 version = 1;
# optional bytes data = 2;
# optional fixed32 unpadded_signature_size = 3;
# }
#
# According to the protobuf encoding, the tail of the signature message will
# be [signature string(256 bytes) + encoding of the fixed32 number 256]. And
# 256 is encoded as 'x1d\x00\x01\x00\x00':
# [3 (field number) << 3 | 5 (type) + byte reverse of 0x100 (256)].
# Details in (https://developers.google.com/protocol-buffers/docs/encoding)
signature_tail_length = self.SIGNATURE_SIZE + 5
self.assertGreater(metadata_total, signature_tail_length)
with open(output_file, 'rb') as verify_fp:
verify_fp.seek(payload_offset + metadata_total - signature_tail_length)
metadata_signature_proto_tail = verify_fp.read(signature_tail_length)
self.assertEqual(b'\x1d\x00\x01\x00\x00',
metadata_signature_proto_tail[-5:])
metadata_signature = metadata_signature_proto_tail[:-5]
# Now we extract the metadata hash via brillo_update_payload script, which
# will serve as the oracle result.
payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
cmd = ['brillo_update_payload', 'hash',
'--unsigned_payload', payload.payload_file,
'--signature_size', str(self.SIGNATURE_SIZE),
'--metadata_hash_file', metadata_sig_file,
'--payload_hash_file', payload_sig_file]
proc = common.Run(cmd)
stdoutdata, _ = proc.communicate()
self.assertEqual(
0, proc.returncode,
'Failed to run brillo_update_payload:\n{}'.format(stdoutdata))
signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
# Finally we can compare the two signatures.
with open(signed_metadata_sig_file, 'rb') as verify_fp:
self.assertEqual(verify_fp.read(), metadata_signature)
@staticmethod
def construct_zip_package_withValidPayload(with_metadata=False):
# Cannot use construct_zip_package() since we need a "valid" payload.bin.
target_file = construct_target_files()
payload = Payload()
payload.Generate(target_file)
payload_signer = PayloadSigner()
payload.Sign(payload_signer)
zip_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(zip_file, 'w') as zip_fp:
# 'payload.bin',
payload.WriteToZip(zip_fp)
# Other entries.
entries = ['care_map.txt', 'compatibility.zip']
# Put META-INF/com/android/metadata if needed.
if with_metadata:
entries.append('META-INF/com/android/metadata')
for entry in entries:
zip_fp.writestr(
entry, entry.replace('.', '-').upper(), zipfile.ZIP_STORED)
return zip_file
@test_utils.SkipIfExternalToolsUnavailable()
def test_Compute(self):
zip_file = self.construct_zip_package_withValidPayload()
property_files = AbOtaPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
property_files_string = property_files.Compute(zip_fp)
tokens = self._parse_property_files_string(property_files_string)
# "6" indcludes the four entries above, one metadata entry, and one entry
# for payload-metadata.bin.
self.assertEqual(6, len(tokens))
self._verify_entries(
zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Finalize(self):
zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)
property_files = AbOtaPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
tokens = self._parse_property_files_string(property_files_string)
# "6" indcludes the four entries above, one metadata entry, and one entry
# for payload-metadata.bin.
self.assertEqual(6, len(tokens))
self._verify_entries(
zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Verify(self):
zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)
property_files = AbOtaPropertyFiles()
with zipfile.ZipFile(zip_file, 'r') as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
property_files.Verify(zip_fp, raw_metadata)
class NonAbOtaPropertyFilesTest(PropertyFilesTest):
"""Additional sanity checks specialized for NonAbOtaPropertyFiles."""
def test_init(self):
property_files = NonAbOtaPropertyFiles()
self.assertEqual('ota-property-files', property_files.name)
self.assertEqual((), property_files.required)
self.assertEqual((), property_files.optional)
def test_Compute(self):
entries = ()
zip_file = self.construct_zip_package(entries)
property_files = NonAbOtaPropertyFiles()
with zipfile.ZipFile(zip_file) as zip_fp:
property_files_string = property_files.Compute(zip_fp)
tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(1, len(tokens))
self._verify_entries(zip_file, tokens, entries)
def test_Finalize(self):
entries = [
'META-INF/com/android/metadata',
]
zip_file = self.construct_zip_package(entries)
property_files = NonAbOtaPropertyFiles()
with zipfile.ZipFile(zip_file) as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
tokens = self._parse_property_files_string(property_files_string)
self.assertEqual(1, len(tokens))
# 'META-INF/com/android/metadata' will be key'd as 'metadata'.
entries[0] = 'metadata'
self._verify_entries(zip_file, tokens, entries)
def test_Verify(self):
entries = (
'META-INF/com/android/metadata',
)
zip_file = self.construct_zip_package(entries)
property_files = NonAbOtaPropertyFiles()
with zipfile.ZipFile(zip_file) as zip_fp:
raw_metadata = property_files.GetPropertyFilesString(
zip_fp, reserve_space=False)
property_files.Verify(zip_fp, raw_metadata)
class PayloadSignerTest(test_utils.ReleaseToolsTestCase):
SIGFILE = 'sigfile.bin'
SIGNED_SIGFILE = 'signed-sigfile.bin'
def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
common.OPTIONS.payload_signer = None
common.OPTIONS.payload_signer_args = []
common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
common.OPTIONS.key_passwords = {
common.OPTIONS.package_key : None,
}
def _assertFilesEqual(self, file1, file2):
with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
self.assertEqual(fp1.read(), fp2.read())
@test_utils.SkipIfExternalToolsUnavailable()
def test_init(self):
payload_signer = PayloadSigner()
self.assertEqual('openssl', payload_signer.signer)
self.assertEqual(256, payload_signer.maximum_signature_size)
@test_utils.SkipIfExternalToolsUnavailable()
def test_init_withPassword(self):
common.OPTIONS.package_key = os.path.join(
self.testdata_dir, 'testkey_with_passwd')
common.OPTIONS.key_passwords = {
common.OPTIONS.package_key : 'foo',
}
payload_signer = PayloadSigner()
self.assertEqual('openssl', payload_signer.signer)
def test_init_withExternalSigner(self):
common.OPTIONS.payload_signer = 'abc'
common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
common.OPTIONS.payload_signer_maximum_signature_size = '512'
payload_signer = PayloadSigner()
self.assertEqual('abc', payload_signer.signer)
self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
self.assertEqual(512, payload_signer.maximum_signature_size)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetMaximumSignatureSizeInBytes_512Bytes(self):
signing_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
# pylint: disable=protected-access
signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)
self.assertEqual(512, signature_size)
@test_utils.SkipIfExternalToolsUnavailable()
def test_GetMaximumSignatureSizeInBytes_ECKey(self):
signing_key = os.path.join(self.testdata_dir, 'testkey_EC.key')
# pylint: disable=protected-access
signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)
self.assertEqual(72, signature_size)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign(self):
payload_signer = PayloadSigner()
input_file = os.path.join(self.testdata_dir, self.SIGFILE)
signed_file = payload_signer.Sign(input_file)
verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
self._assertFilesEqual(verify_file, signed_file)
def test_Sign_withExternalSigner_openssl(self):
"""Uses openssl as the external payload signer."""
common.OPTIONS.payload_signer = 'openssl'
common.OPTIONS.payload_signer_args = [
'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
os.path.join(self.testdata_dir, 'testkey.pk8'),
'-pkeyopt', 'digest:sha256']
payload_signer = PayloadSigner()
input_file = os.path.join(self.testdata_dir, self.SIGFILE)
signed_file = payload_signer.Sign(input_file)
verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
self._assertFilesEqual(verify_file, signed_file)
def test_Sign_withExternalSigner_script(self):
"""Uses testdata/payload_signer.sh as the external payload signer."""
common.OPTIONS.payload_signer = os.path.join(
self.testdata_dir, 'payload_signer.sh')
os.chmod(common.OPTIONS.payload_signer, 0o700)
common.OPTIONS.payload_signer_args = [
os.path.join(self.testdata_dir, 'testkey.pk8')]
payload_signer = PayloadSigner()
input_file = os.path.join(self.testdata_dir, self.SIGFILE)
signed_file = payload_signer.Sign(input_file)
verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
self._assertFilesEqual(verify_file, signed_file)
class PayloadTest(test_utils.ReleaseToolsTestCase):
def setUp(self):
self.testdata_dir = test_utils.get_testdata_dir()
self.assertTrue(os.path.exists(self.testdata_dir))
common.OPTIONS.wipe_user_data = False
common.OPTIONS.payload_signer = None
common.OPTIONS.payload_signer_args = None
common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
common.OPTIONS.key_passwords = {
common.OPTIONS.package_key : None,
}
@staticmethod
def _create_payload_full(secondary=False):
target_file = construct_target_files(secondary)
payload = Payload(secondary)
payload.Generate(target_file)
return payload
@staticmethod
def _create_payload_incremental():
target_file = construct_target_files()
source_file = construct_target_files()
payload = Payload()
payload.Generate(target_file, source_file)
return payload
@test_utils.SkipIfExternalToolsUnavailable()
def test_Generate_full(self):
payload = self._create_payload_full()
self.assertTrue(os.path.exists(payload.payload_file))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Generate_incremental(self):
payload = self._create_payload_incremental()
self.assertTrue(os.path.exists(payload.payload_file))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Generate_additionalArgs(self):
target_file = construct_target_files()
source_file = construct_target_files()
payload = Payload()
# This should work the same as calling payload.Generate(target_file,
# source_file).
payload.Generate(
target_file, additional_args=["--source_image", source_file])
self.assertTrue(os.path.exists(payload.payload_file))
@test_utils.SkipIfExternalToolsUnavailable()
def test_Generate_invalidInput(self):
target_file = construct_target_files()
common.ZipDelete(target_file, 'IMAGES/vendor.img')
payload = Payload()
self.assertRaises(common.ExternalError, payload.Generate, target_file)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign_full(self):
payload = self._create_payload_full()
payload.Sign(PayloadSigner())
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
payload.WriteToZip(output_zip)
import check_ota_package_signature
check_ota_package_signature.VerifyAbOtaPayload(
os.path.join(self.testdata_dir, 'testkey.x509.pem'),
output_file)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign_incremental(self):
payload = self._create_payload_incremental()
payload.Sign(PayloadSigner())
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
payload.WriteToZip(output_zip)
import check_ota_package_signature
check_ota_package_signature.VerifyAbOtaPayload(
os.path.join(self.testdata_dir, 'testkey.x509.pem'),
output_file)
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign_withDataWipe(self):
common.OPTIONS.wipe_user_data = True
payload = self._create_payload_full()
payload.Sign(PayloadSigner())
with open(payload.payload_properties) as properties_fp:
self.assertIn("POWERWASH=1", properties_fp.read())
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign_secondary(self):
payload = self._create_payload_full(secondary=True)
payload.Sign(PayloadSigner())
with open(payload.payload_properties) as properties_fp:
self.assertIn("SWITCH_SLOT_ON_REBOOT=0", properties_fp.read())
@test_utils.SkipIfExternalToolsUnavailable()
def test_Sign_badSigner(self):
"""Tests that signing failure can be captured."""
payload = self._create_payload_full()
payload_signer = PayloadSigner()
payload_signer.signer_args.append('bad-option')
self.assertRaises(common.ExternalError, payload.Sign, payload_signer)
@test_utils.SkipIfExternalToolsUnavailable()
def test_WriteToZip(self):
payload = self._create_payload_full()
payload.Sign(PayloadSigner())
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
payload.WriteToZip(output_zip)
with zipfile.ZipFile(output_file) as verify_zip:
# First make sure we have the essential entries.
namelist = verify_zip.namelist()
self.assertIn(Payload.PAYLOAD_BIN, namelist)
self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
# Then assert these entries are stored.
for entry_info in verify_zip.infolist():
if entry_info.filename not in (Payload.PAYLOAD_BIN,
Payload.PAYLOAD_PROPERTIES_TXT):
continue
self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
@test_utils.SkipIfExternalToolsUnavailable()
def test_WriteToZip_unsignedPayload(self):
"""Unsigned payloads should not be allowed to be written to zip."""
payload = self._create_payload_full()
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
# Also test with incremental payload.
payload = self._create_payload_incremental()
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
@test_utils.SkipIfExternalToolsUnavailable()
def test_WriteToZip_secondary(self):
payload = self._create_payload_full(secondary=True)
payload.Sign(PayloadSigner())
output_file = common.MakeTempFile(suffix='.zip')
with zipfile.ZipFile(output_file, 'w') as output_zip:
payload.WriteToZip(output_zip)
with zipfile.ZipFile(output_file) as verify_zip:
# First make sure we have the essential entries.
namelist = verify_zip.namelist()
self.assertIn(Payload.SECONDARY_PAYLOAD_BIN, namelist)
self.assertIn(Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)
# Then assert these entries are stored.
for entry_info in verify_zip.infolist():
if entry_info.filename not in (
Payload.SECONDARY_PAYLOAD_BIN,
Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
continue
self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)