Merge "Use soong_java_prebuilt.mk to export soong jars to java"
diff --git a/tools/checkowners.py b/tools/checkowners.py
index b874955..1190d30 100755
--- a/tools/checkowners.py
+++ b/tools/checkowners.py
@@ -34,8 +34,8 @@
+ urllib.quote(address))
echo('Checking email address: ' + address)
result = urllib2.urlopen(request).read()
- expected = '"email": "' + address + '"'
- checked_addresses[address] = (result.find(expected) >= 0)
+ checked_addresses[address] = (
+ result.find('"email":') >= 0 and result.find('"_account_id":') >= 0)
return checked_addresses[address]
diff --git a/tools/releasetools/check_ota_package_signature.py b/tools/releasetools/check_ota_package_signature.py
index 548b619..1f8b7bb 100755
--- a/tools/releasetools/check_ota_package_signature.py
+++ b/tools/releasetools/check_ota_package_signature.py
@@ -25,12 +25,19 @@
import re
import subprocess
import sys
+import tempfile
+import zipfile
from hashlib import sha1
from hashlib import sha256
+# 'update_payload' package is under 'system/update_engine/scripts/', which
+# should to be included in PYTHONPATH.
+from update_payload.payload import Payload
+from update_payload.update_metadata_pb2 import Signatures
-def cert_uses_sha256(cert):
+
+def CertUsesSha256(cert):
"""Check if the cert uses SHA-256 hashing algorithm."""
cmd = ['openssl', 'x509', '-text', '-noout', '-in', cert]
@@ -46,7 +53,7 @@
return algorithm.group(1).startswith('sha256')
-def verify_package(cert, package):
+def VerifyPackage(cert, package):
"""Verify the given package with the certificate.
(Comments from bootable/recovery/verifier.cpp:)
@@ -90,7 +97,7 @@
print('Signed data length: %d' % (signed_len,))
print('Signature start: %d' % (signature_start,))
- use_sha256 = cert_uses_sha256(cert)
+ use_sha256 = CertUsesSha256(cert)
print('Use SHA-256: %s' % (use_sha256,))
if use_sha256:
@@ -100,7 +107,7 @@
h.update(package_bytes[:signed_len])
package_digest = h.hexdigest().lower()
- print('Digest: %s\n' % (package_digest,))
+ print('Digest: %s' % (package_digest,))
# Get the signature from the input package.
signature = package_bytes[signature_start:-6]
@@ -141,7 +148,87 @@
assert package_digest == digest_string, "Verification failed."
# Verified successfully upon reaching here.
- print('VERIFIED\n')
+ print('\nWhole package signature VERIFIED\n')
+
+
+def VerifyAbOtaPayload(cert, package):
+ """Verifies the payload and metadata signatures in an A/B OTA payload."""
+
+ def VerifySignatureBlob(hash_file, blob):
+ """Verifies the input hash_file against the signature blob."""
+ signatures = Signatures()
+ signatures.ParseFromString(blob)
+
+ extracted_sig_file = common.MakeTempFile(
+ prefix='extracted-sig-', suffix='.bin')
+ # In Android, we only expect one signature.
+ assert len(signatures.signatures) == 1, \
+ 'Invalid number of signatures: %d' % len(signatures.signatures)
+ signature = signatures.signatures[0]
+ length = len(signature.data)
+ assert length == 256, 'Invalid signature length %d' % (length,)
+ with open(extracted_sig_file, 'w') as f:
+ f.write(signature.data)
+
+ # Verify the signature file extracted from the payload, by reversing the
+ # signing operation. Alternatively, this can be done by calling 'openssl
+ # rsautl -verify -certin -inkey <cert.pem> -in <extracted_sig_file> -out
+ # <output>', then to assert that
+ # <output> == SHA-256 DigestInfo prefix || <hash_file>.
+ cmd = ['openssl', 'pkeyutl', '-verify', '-certin', '-inkey', cert,
+ '-pkeyopt', 'digest:sha256', '-in', hash_file,
+ '-sigfile', extracted_sig_file]
+ p = common.Run(cmd, stdout=subprocess.PIPE)
+ result, _ = p.communicate()
+
+ # https://github.com/openssl/openssl/pull/3213
+ # 'openssl pkeyutl -verify' (prior to 1.1.0) returns non-zero return code,
+ # even on successful verification. To avoid the false alarm with older
+ # openssl, check the output directly.
+ assert result.strip() == 'Signature Verified Successfully', result.strip()
+
+ package_zip = zipfile.ZipFile(package, 'r')
+ if 'payload.bin' not in package_zip.namelist():
+ common.ZipClose(package_zip)
+ return
+
+ print('Verifying A/B OTA payload signatures...')
+
+ package_dir = tempfile.mkdtemp(prefix='package-')
+ common.OPTIONS.tempfiles.append(package_dir)
+
+ payload_file = package_zip.extract('payload.bin', package_dir)
+ payload = Payload(open(payload_file, 'rb'))
+ payload.Init()
+
+ # Extract the payload hash and metadata hash from the payload.bin.
+ payload_hash_file = common.MakeTempFile(prefix='hash-', suffix='.bin')
+ metadata_hash_file = common.MakeTempFile(prefix='hash-', suffix='.bin')
+ cmd = ['brillo_update_payload', 'hash',
+ '--unsigned_payload', payload_file,
+ '--signature_size', '256',
+ '--metadata_hash_file', metadata_hash_file,
+ '--payload_hash_file', payload_hash_file]
+ p = common.Run(cmd, stdout=subprocess.PIPE)
+ p.communicate()
+ assert p.returncode == 0, 'brillo_update_payload hash failed'
+
+ # Payload signature verification.
+ assert payload.manifest.HasField('signatures_offset')
+ payload_signature = payload.ReadDataBlob(
+ payload.manifest.signatures_offset, payload.manifest.signatures_size)
+ VerifySignatureBlob(payload_hash_file, payload_signature)
+
+ # Metadata signature verification.
+ metadata_signature = payload.ReadDataBlob(
+ -payload.header.metadata_signature_len,
+ payload.header.metadata_signature_len)
+ VerifySignatureBlob(metadata_hash_file, metadata_signature)
+
+ common.ZipClose(package_zip)
+
+ # Verified successfully upon reaching here.
+ print('\nPayload signatures VERIFIED\n\n')
def main():
@@ -150,7 +237,8 @@
parser.add_argument('package', help='The OTA package to be verified.')
args = parser.parse_args()
- verify_package(args.certificate, args.package)
+ VerifyPackage(args.certificate, args.package)
+ VerifyAbOtaPayload(args.certificate, args.package)
if __name__ == '__main__':
@@ -159,3 +247,5 @@
except AssertionError as err:
print('\n ERROR: %s\n' % (err,))
sys.exit(1)
+ finally:
+ common.Cleanup()
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 8ac3322..4b34820 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -44,8 +44,8 @@
return sparse_img.SparseImage(path, mappath, clobbered_blocks)
-def _CalculateFileSha1(file_name, unpacked_name, round_up=False):
- """Calculate the SHA-1 for a given file. Round up its size to 4K if needed."""
+def _ReadFile(file_name, unpacked_name, round_up=False):
+ """Constructs and returns a File object. Rounds up its size if needed."""
def RoundUpTo4K(value):
rounded_up = value + 4095
@@ -58,7 +58,7 @@
if round_up:
file_size_rounded_up = RoundUpTo4K(file_size)
file_data += '\0' * (file_size_rounded_up - file_size)
- return common.File(file_name, file_data).sha1
+ return common.File(file_name, file_data)
def ValidateFileAgainstSha1(input_tmp, file_name, file_path, expected_sha1):
@@ -67,7 +67,7 @@
logging.info('Validating the SHA-1 of {}'.format(file_name))
unpacked_name = os.path.join(input_tmp, file_path)
assert os.path.exists(unpacked_name)
- actual_sha1 = _CalculateFileSha1(file_name, unpacked_name, False)
+ actual_sha1 = _ReadFile(file_name, unpacked_name, False).sha1
assert actual_sha1 == expected_sha1, \
'SHA-1 mismatches for {}. actual {}, expected {}'.format(
file_name, actual_sha1, expected_sha1)
@@ -92,8 +92,20 @@
# The filename under unpacked directory, such as SYSTEM/bin/sh.
unpacked_name = os.path.join(
input_tmp, which.upper(), entry[(len(prefix) + 1):])
- file_sha1 = _CalculateFileSha1(entry, unpacked_name, True)
+ unpacked_file = _ReadFile(entry, unpacked_name, True)
+ file_size = unpacked_file.size
+ # block.map may contain less blocks, because mke2fs may skip allocating
+ # blocks if they contain all zeros. We can't reconstruct such a file from
+ # its block list. (Bug: 65213616)
+ if file_size > ranges.size() * 4096:
+ logging.warning(
+ 'Skipping %s that has less blocks: file size %d-byte,'
+ ' ranges %s (%d-byte)', entry, file_size, ranges,
+ ranges.size() * 4096)
+ continue
+
+ file_sha1 = unpacked_file.sha1
assert blocks_sha1 == file_sha1, \
'file: %s, range: %s, blocks_sha1: %s, file_sha1: %s' % (
entry, ranges, blocks_sha1, file_sha1)