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
Exemple #4
0
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))