Merge "releasetools: Add tests for common.ReadApkCerts()." am: e3692095a1
am: 05d63dc7b0
Change-Id: I9e849a806db2accc02c26c803f2054382eb18603
This commit is contained in:
commit
19fc696154
2 changed files with 182 additions and 39 deletions
|
@ -792,11 +792,22 @@ def CheckSize(data, target, info_dict):
|
||||||
|
|
||||||
|
|
||||||
def ReadApkCerts(tf_zip):
|
def ReadApkCerts(tf_zip):
|
||||||
"""Given a target_files ZipFile, parse the META/apkcerts.txt file
|
"""Parses the APK certs info from a given target-files zip.
|
||||||
and return a tuple with the following elements: (1) a dictionary that maps
|
|
||||||
packages to certs (based on the "certificate" and "private_key" attributes
|
Given a target-files ZipFile, parses the META/apkcerts.txt entry and returns a
|
||||||
in the file. (2) A string representing the extension of compressed APKs in
|
tuple with the following elements: (1) a dictionary that maps packages to
|
||||||
the target files (e.g ".gz" ".bro")."""
|
certs (based on the "certificate" and "private_key" attributes in the file;
|
||||||
|
(2) a string representing the extension of compressed APKs in the target files
|
||||||
|
(e.g ".gz", ".bro").
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tf_zip: The input target_files ZipFile (already open).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(certmap, ext): certmap is a dictionary that maps packages to certs; ext is
|
||||||
|
the extension string of compressed APKs (e.g. ".gz"), or None if there's
|
||||||
|
no compressed APKs.
|
||||||
|
"""
|
||||||
certmap = {}
|
certmap = {}
|
||||||
compressed_extension = None
|
compressed_extension = None
|
||||||
|
|
||||||
|
@ -812,41 +823,51 @@ def ReadApkCerts(tf_zip):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
m = re.match(r'^name="(?P<NAME>.*)"\s+certificate="(?P<CERT>.*)"\s+'
|
m = re.match(
|
||||||
r'private_key="(?P<PRIVKEY>.*?)"(\s+compressed="(?P<COMPRESSED>.*)")?$',
|
r'^name="(?P<NAME>.*)"\s+certificate="(?P<CERT>.*)"\s+'
|
||||||
line)
|
r'private_key="(?P<PRIVKEY>.*?)"(\s+compressed="(?P<COMPRESSED>.*)")?$',
|
||||||
if m:
|
line)
|
||||||
matches = m.groupdict()
|
if not m:
|
||||||
cert = matches["CERT"]
|
continue
|
||||||
privkey = matches["PRIVKEY"]
|
|
||||||
name = matches["NAME"]
|
|
||||||
this_compressed_extension = matches["COMPRESSED"]
|
|
||||||
public_key_suffix_len = len(OPTIONS.public_key_suffix)
|
|
||||||
private_key_suffix_len = len(OPTIONS.private_key_suffix)
|
|
||||||
if cert in SPECIAL_CERT_STRINGS and not privkey:
|
|
||||||
certmap[name] = cert
|
|
||||||
elif (cert.endswith(OPTIONS.public_key_suffix) and
|
|
||||||
privkey.endswith(OPTIONS.private_key_suffix) and
|
|
||||||
cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
|
|
||||||
certmap[name] = cert[:-public_key_suffix_len]
|
|
||||||
else:
|
|
||||||
raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
|
|
||||||
if this_compressed_extension:
|
|
||||||
# Only count the installed files.
|
|
||||||
filename = name + '.' + this_compressed_extension
|
|
||||||
if filename not in installed_files:
|
|
||||||
continue
|
|
||||||
# Make sure that all the values in the compression map have the same
|
|
||||||
# extension. We don't support multiple compression methods in the same
|
|
||||||
# system image.
|
|
||||||
if compressed_extension:
|
|
||||||
if this_compressed_extension != compressed_extension:
|
|
||||||
raise ValueError("multiple compressed extensions : %s vs %s",
|
|
||||||
(compressed_extension, this_compressed_extension))
|
|
||||||
else:
|
|
||||||
compressed_extension = this_compressed_extension
|
|
||||||
|
|
||||||
return (certmap, ("." + compressed_extension) if compressed_extension else None)
|
matches = m.groupdict()
|
||||||
|
cert = matches["CERT"]
|
||||||
|
privkey = matches["PRIVKEY"]
|
||||||
|
name = matches["NAME"]
|
||||||
|
this_compressed_extension = matches["COMPRESSED"]
|
||||||
|
|
||||||
|
public_key_suffix_len = len(OPTIONS.public_key_suffix)
|
||||||
|
private_key_suffix_len = len(OPTIONS.private_key_suffix)
|
||||||
|
if cert in SPECIAL_CERT_STRINGS and not privkey:
|
||||||
|
certmap[name] = cert
|
||||||
|
elif (cert.endswith(OPTIONS.public_key_suffix) and
|
||||||
|
privkey.endswith(OPTIONS.private_key_suffix) and
|
||||||
|
cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
|
||||||
|
certmap[name] = cert[:-public_key_suffix_len]
|
||||||
|
else:
|
||||||
|
raise ValueError("Failed to parse line from apkcerts.txt:\n" + line)
|
||||||
|
|
||||||
|
if not this_compressed_extension:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Only count the installed files.
|
||||||
|
filename = name + '.' + this_compressed_extension
|
||||||
|
if filename not in installed_files:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Make sure that all the values in the compression map have the same
|
||||||
|
# extension. We don't support multiple compression methods in the same
|
||||||
|
# system image.
|
||||||
|
if compressed_extension:
|
||||||
|
if this_compressed_extension != compressed_extension:
|
||||||
|
raise ValueError(
|
||||||
|
"Multiple compressed extensions: {} vs {}".format(
|
||||||
|
compressed_extension, this_compressed_extension))
|
||||||
|
else:
|
||||||
|
compressed_extension = this_compressed_extension
|
||||||
|
|
||||||
|
return (certmap,
|
||||||
|
("." + compressed_extension) if compressed_extension else None)
|
||||||
|
|
||||||
|
|
||||||
COMMON_DOCSTRING = """
|
COMMON_DOCSTRING = """
|
||||||
|
|
|
@ -353,6 +353,128 @@ class CommonZipTest(unittest.TestCase):
|
||||||
os.remove(zip_file.name)
|
os.remove(zip_file.name)
|
||||||
|
|
||||||
|
|
||||||
|
class CommonApkUtilsTest(unittest.TestCase):
|
||||||
|
"""Tests the APK utils related functions."""
|
||||||
|
|
||||||
|
APKCERTS_TXT1 = (
|
||||||
|
'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
|
||||||
|
' private_key="certs/devkey.pk8"\n'
|
||||||
|
'name="Settings.apk"'
|
||||||
|
' certificate="build/target/product/security/platform.x509.pem"'
|
||||||
|
' private_key="build/target/product/security/platform.pk8"\n'
|
||||||
|
'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
APKCERTS_CERTMAP1 = {
|
||||||
|
'RecoveryLocalizer.apk' : 'certs/devkey',
|
||||||
|
'Settings.apk' : 'build/target/product/security/platform',
|
||||||
|
'TV.apk' : 'PRESIGNED',
|
||||||
|
}
|
||||||
|
|
||||||
|
APKCERTS_TXT2 = (
|
||||||
|
'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
|
||||||
|
' private_key="certs/compressed1.pk8" compressed="gz"\n'
|
||||||
|
'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
|
||||||
|
' private_key="certs/compressed2.pk8" compressed="gz"\n'
|
||||||
|
'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
|
||||||
|
' private_key="certs/compressed2.pk8" compressed="gz"\n'
|
||||||
|
'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
|
||||||
|
' private_key="certs/compressed3.pk8" compressed="gz"\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
APKCERTS_CERTMAP2 = {
|
||||||
|
'Compressed1.apk' : 'certs/compressed1',
|
||||||
|
'Compressed2a.apk' : 'certs/compressed2',
|
||||||
|
'Compressed2b.apk' : 'certs/compressed2',
|
||||||
|
'Compressed3.apk' : 'certs/compressed3',
|
||||||
|
}
|
||||||
|
|
||||||
|
APKCERTS_TXT3 = (
|
||||||
|
'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
|
||||||
|
' private_key="certs/compressed4.pk8" compressed="xz"\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
APKCERTS_CERTMAP3 = {
|
||||||
|
'Compressed4.apk' : 'certs/compressed4',
|
||||||
|
}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
common.Cleanup()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _write_apkcerts_txt(apkcerts_txt, additional=None):
|
||||||
|
if additional is None:
|
||||||
|
additional = []
|
||||||
|
target_files = common.MakeTempFile(suffix='.zip')
|
||||||
|
with zipfile.ZipFile(target_files, 'w') as target_files_zip:
|
||||||
|
target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
|
||||||
|
for entry in additional:
|
||||||
|
target_files_zip.writestr(entry, '')
|
||||||
|
return target_files
|
||||||
|
|
||||||
|
def test_ReadApkCerts_NoncompressedApks(self):
|
||||||
|
target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
certmap, ext = common.ReadApkCerts(input_zip)
|
||||||
|
|
||||||
|
self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
|
||||||
|
self.assertIsNone(ext)
|
||||||
|
|
||||||
|
def test_ReadApkCerts_CompressedApks(self):
|
||||||
|
# We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
|
||||||
|
# not stored in '.gz' format, so it shouldn't be considered as installed.
|
||||||
|
target_files = self._write_apkcerts_txt(
|
||||||
|
self.APKCERTS_TXT2,
|
||||||
|
['Compressed1.apk.gz', 'Compressed3.apk'])
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
certmap, ext = common.ReadApkCerts(input_zip)
|
||||||
|
|
||||||
|
self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
|
||||||
|
self.assertEqual('.gz', ext)
|
||||||
|
|
||||||
|
# Alternative case with '.xz'.
|
||||||
|
target_files = self._write_apkcerts_txt(
|
||||||
|
self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
certmap, ext = common.ReadApkCerts(input_zip)
|
||||||
|
|
||||||
|
self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
|
||||||
|
self.assertEqual('.xz', ext)
|
||||||
|
|
||||||
|
def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
|
||||||
|
target_files = self._write_apkcerts_txt(
|
||||||
|
self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
|
||||||
|
['Compressed1.apk.gz', 'Compressed3.apk'])
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
certmap, ext = common.ReadApkCerts(input_zip)
|
||||||
|
|
||||||
|
certmap_merged = self.APKCERTS_CERTMAP1.copy()
|
||||||
|
certmap_merged.update(self.APKCERTS_CERTMAP2)
|
||||||
|
self.assertDictEqual(certmap_merged, certmap)
|
||||||
|
self.assertEqual('.gz', ext)
|
||||||
|
|
||||||
|
def test_ReadApkCerts_MultipleCompressionMethods(self):
|
||||||
|
target_files = self._write_apkcerts_txt(
|
||||||
|
self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
|
||||||
|
['Compressed1.apk.gz', 'Compressed4.apk.xz'])
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
|
||||||
|
|
||||||
|
def test_ReadApkCerts_MismatchingKeys(self):
|
||||||
|
malformed_apkcerts_txt = (
|
||||||
|
'name="App1.apk" certificate="certs/cert1.x509.pem"'
|
||||||
|
' private_key="certs/cert2.pk8"\n'
|
||||||
|
)
|
||||||
|
target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_files, 'r') as input_zip:
|
||||||
|
self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
|
||||||
|
|
||||||
|
|
||||||
class InstallRecoveryScriptFormatTest(unittest.TestCase):
|
class InstallRecoveryScriptFormatTest(unittest.TestCase):
|
||||||
"""Checks the format of install-recovery.sh.
|
"""Checks the format of install-recovery.sh.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue