def VerifyAbOtaPayload(cert, package): """Verifies the payload and metadata signatures in an A/B OTA payload.""" package_zip = zipfile.ZipFile(package, 'r', allowZip64=True) if 'payload.bin' not in package_zip.namelist(): common.ZipClose(package_zip) return print('Verifying A/B OTA payload signatures...') # Dump pubkey from the certificate. pubkey = common.MakeTempFile(prefix="key-", suffix=".pem") with open(pubkey, 'w') as pubkey_fp: pubkey_fp.write(common.ExtractPublicKey(cert)) package_dir = common.MakeTempDir(prefix='package-') # Signature verification with delta_generator. payload_file = package_zip.extract('payload.bin', package_dir) cmd = [ 'delta_generator', '--in_file=' + payload_file, '--public_key=' + pubkey ] common.RunAndCheckOutput(cmd) common.ZipClose(package_zip) # Verified successfully upon reaching here. print('\nPayload signatures VERIFIED\n\n')
def VerifyAbOtaPayload(cert, package): """Verifies the payload and metadata signatures in an A/B OTA payload.""" 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...') # Dump pubkey from the certificate. pubkey = common.MakeTempFile(prefix="key-", suffix=".pem") with open(pubkey, 'wb') as pubkey_fp: pubkey_fp.write(common.ExtractPublicKey(cert)) package_dir = common.MakeTempDir(prefix='package-') # Signature verification with delta_generator. payload_file = package_zip.extract('payload.bin', package_dir) cmd = ['delta_generator', '--in_file=' + payload_file, '--public_key=' + pubkey] proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdoutdata, _ = proc.communicate() assert proc.returncode == 0, \ 'Failed to verify payload with delta_generator: %s\n%s' % (package, stdoutdata) common.ZipClose(package_zip) # Verified successfully upon reaching here. print('\nPayload signatures VERIFIED\n\n')
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): try: keylist = input_tf_zip.read("META/otakeys.txt").split() except KeyError: raise common.ExternalError("can't read META/otakeys.txt from input") extra_recovery_keys = misc_info.get("extra_recovery_keys") if extra_recovery_keys: extra_recovery_keys = [ OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split() ] if extra_recovery_keys: print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) else: extra_recovery_keys = [] mapped_keys = [] for k in keylist: m = re.match(r"^(.*)\.x509\.pem$", k) if not m: raise common.ExternalError( "can't parse \"%s\" from META/otakeys.txt" % (k, )) k = m.group(1) mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: print("using:\n ", "\n ".join(mapped_keys)) print("for OTA package verification") else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_devkey = OPTIONS.key_map.get(devkey, devkey) if mapped_devkey != devkey: misc_info["default_system_dev_certificate"] = mapped_devkey mapped_keys.append(mapped_devkey + ".x509.pem") print("META/otakeys.txt has no keys; using %s for OTA package" " verification." % (mapped_keys[0], )) # recovery uses a version of the key that has been slightly # predigested (by DumpPublicKey.java) and put in res/keys. # extra_recovery_keys are used only in recovery. cmd = ([OPTIONS.java_path] + OPTIONS.java_args + [ "-jar", os.path.join(OPTIONS.search_path, "framework", "dumpkey.jar") ] + mapped_keys + extra_recovery_keys) p = common.Run(cmd, stdout=subprocess.PIPE) new_recovery_keys, _ = p.communicate() if p.returncode != 0: raise common.ExternalError("failed to run dumpkeys") # system_root_image puts the recovery keys at BOOT/RAMDISK. if misc_info.get("system_root_image") == "true": recovery_keys_location = "BOOT/RAMDISK/res/keys" else: recovery_keys_location = "RECOVERY/RAMDISK/res/keys" common.ZipWriteStr(output_tf_zip, recovery_keys_location, new_recovery_keys) # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. try: from StringIO import StringIO except ImportError: from io import StringIO temp_file = StringIO() certs_zip = zipfile.ZipFile(temp_file, "w") for k in mapped_keys: common.ZipWrite(certs_zip, k) common.ZipClose(certs_zip) common.ZipWriteStr(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", temp_file.getvalue()) # For A/B devices, update the payload verification key. if misc_info.get("ab_update") == "true": # Unlike otacerts.zip that may contain multiple keys, we can only specify # ONE payload verification key. if len(mapped_keys) > 1: print( "\n WARNING: Found more than one OTA keys; Using the first one" " as payload verification key.\n\n") print("Using %s for payload verification." % (mapped_keys[0], )) pubkey = common.ExtractPublicKey(mapped_keys[0]) common.ZipWriteStr( output_tf_zip, "SYSTEM/etc/update_engine/update-payload-key.pub.pem", pubkey) common.ZipWriteStr( output_tf_zip, "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem", pubkey) return new_recovery_keys
def ReplaceOtaKeys(input_tf_zip, output_tf_zip, misc_info): try: keylist = input_tf_zip.read("META/otakeys.txt").split() except KeyError: raise common.ExternalError("can't read META/otakeys.txt from input") extra_recovery_keys = misc_info.get("extra_recovery_keys") if extra_recovery_keys: extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem" for k in extra_recovery_keys.split()] if extra_recovery_keys: print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys)) else: extra_recovery_keys = [] mapped_keys = [] for k in keylist: m = re.match(r"^(.*)\.x509\.pem$", k) if not m: raise common.ExternalError( "can't parse \"%s\" from META/otakeys.txt" % (k,)) k = m.group(1) mapped_keys.append(OPTIONS.key_map.get(k, k) + ".x509.pem") if mapped_keys: print("using:\n ", "\n ".join(mapped_keys)) print("for OTA package verification") else: devkey = misc_info.get("default_system_dev_certificate", "build/target/product/security/testkey") mapped_devkey = OPTIONS.key_map.get(devkey, devkey) if mapped_devkey != devkey: misc_info["default_system_dev_certificate"] = mapped_devkey mapped_keys.append(mapped_devkey + ".x509.pem") print("META/otakeys.txt has no keys; using %s for OTA package" " verification." % (mapped_keys[0],)) # recovery now uses the same x509.pem version of the keys. # extra_recovery_keys are used only in recovery. if misc_info.get("recovery_as_boot") == "true": recovery_keys_location = "BOOT/RAMDISK/system/etc/security/otacerts.zip" else: recovery_keys_location = "RECOVERY/RAMDISK/system/etc/security/otacerts.zip" WriteOtacerts(output_tf_zip, recovery_keys_location, mapped_keys + extra_recovery_keys) # SystemUpdateActivity uses the x509.pem version of the keys, but # put into a zipfile system/etc/security/otacerts.zip. # We DO NOT include the extra_recovery_keys (if any) here. WriteOtacerts(output_tf_zip, "SYSTEM/etc/security/otacerts.zip", mapped_keys) # For A/B devices, update the payload verification key. if misc_info.get("ab_update") == "true": # Unlike otacerts.zip that may contain multiple keys, we can only specify # ONE payload verification key. if len(mapped_keys) > 1: print("\n WARNING: Found more than one OTA keys; Using the first one" " as payload verification key.\n\n") print("Using %s for payload verification." % (mapped_keys[0],)) pubkey = common.ExtractPublicKey(mapped_keys[0]) common.ZipWriteStr( output_tf_zip, "SYSTEM/etc/update_engine/update-payload-key.pub.pem", pubkey) common.ZipWriteStr( output_tf_zip, "BOOT/RAMDISK/system/etc/update_engine/update-payload-key.pub.pem", pubkey)
def test_ExtractPublicKey(self): testdata_dir = test_utils.get_testdata_dir() cert = os.path.join(testdata_dir, 'testkey.x509.pem') pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem') with open(pubkey, 'rb') as pubkey_fp: self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))