Use the bundled public key for debuggable builds In debuggable builds, when it has failed to find a matching public key in the built-in partitions (e.g. /system/etc/security/apex), the public key bundled in the APEX is used as a fallback, if it exists. Bug: 122047804 Test: add 'installable: false' to the apex_key 'com.android.apex.test_package.key'. Install the APEX 'com.android.apex.test_package'. The APEX is activated. Change-Id: I7824a69478ef55e91d75e1c76d2cfda5e23e8926
diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp index 063e96f..419a9dd 100644 --- a/apexd/apex_file.cpp +++ b/apexd/apex_file.cpp
@@ -41,6 +41,12 @@ constexpr const char* kImageFilename = "apex_payload.img"; constexpr const char* kManifestFilename = "apex_manifest.json"; +constexpr const char* kBundledPublicKeyFilename = "apex_pubkey"; +#ifdef DEBUG_ALLOW_BUNDLED_KEY +constexpr const bool kDebugAllowBundledKey = true; +#else +constexpr const bool kDebugAllowBundledKey = false; +#endif // Tests if <path>/manifest.json file exists. bool isFlattenedApex(const std::string& path) { @@ -72,6 +78,7 @@ int32_t image_offset; size_t image_size; std::string manifest_content; + std::string pubkey; if (isFlattenedApex(path)) { flattened = true; @@ -127,6 +134,23 @@ << ": " << ErrorCodeString(ret); return StatusOr<ApexFile>::MakeError(err); } + + if (kDebugAllowBundledKey) { + ret = FindEntry(handle, ZipString(kBundledPublicKeyFilename), &entry); + if (ret >= 0) { + LOG(VERBOSE) << "Found bundled key in package " << path; + length = entry.uncompressed_length; + pubkey.resize(length, '\0'); + ret = ExtractToMemory(handle, &entry, + reinterpret_cast<uint8_t*>(&(pubkey)[0]), length); + if (ret != 0) { + std::string err = StringLog() + << "Failed to extract public key from package " + << path << ": " << ErrorCodeString(ret); + return StatusOr<ApexFile>::MakeError(err); + } + } + } } StatusOr<ApexManifest> manifest = ApexManifest::Parse(manifest_content); @@ -134,7 +158,8 @@ return StatusOr<ApexFile>::MakeError(manifest.ErrorMessage()); } - ApexFile apexFile(path, flattened, image_offset, image_size, *manifest); + ApexFile apexFile(path, flattened, image_offset, image_size, *manifest, + pubkey); return StatusOr<ApexFile>(std::move(apexFile)); } @@ -230,12 +255,25 @@ return Status::Fail(StringLog() << "Can't read from " << acceptedKeyFile); } - if (memcmp(&verifiedKey[0], key, length) != 0) { + if (verifiedKey.length() != length || + memcmp(&verifiedKey[0], key, length) != 0) { return Status::Fail("Failed to compare verified key with key"); } return Status::Success(); } +Status verifyBundledPublicKey(const uint8_t* key, size_t length, + std::string bundledPublicKey) { + if (!kDebugAllowBundledKey) { + return Status::Fail("Bundled key must not be used in production builds"); + } + if (bundledPublicKey.length() != length || + memcmp(&bundledPublicKey[0], key, length) != 0) { + return Status::Fail("Failed to compare the bundled public key with key"); + } + return Status::Success(); +} + StatusOr<std::string> getPublicKeyFilePath(const ApexFile& apex, const uint8_t* data, size_t length, const std::string& apex_key_dir) { @@ -309,18 +347,27 @@ break; } } - if (!keyFilePath.Ok()) { + + Status st; + if (keyFilePath.Ok()) { + // TODO(b/115718846) + // We need to decide whether we need rollback protection, and whether + // we can use the rollback protection provided by libavb. + st = verifyPublicKey(pk, pk_len, *keyFilePath); + } else if (kDebugAllowBundledKey) { + // Failing to find the matching public key in the built-in partitions + // is a hard error for non-debuggable build. For debuggable builds, + // the public key bundled in the APEX is used as a fallback. + st = verifyBundledPublicKey(pk, pk_len, apex.GetBundledPublicKey()); + } else { return keyFilePath.ErrorStatus(); } - // TODO(b/115718846) - // We need to decide whether we need rollback protection, and whether - // we can use the rollback protection provided by libavb. - Status st = verifyPublicKey(pk, pk_len, *keyFilePath); if (st.Ok()) { LOG(VERBOSE) << apex.GetPath() << ": public key matches."; return st; } + return Status::Fail(StringLog() << "Error verifying " << apex.GetPath() << ": " << "couldn't verify public key: " << st.ErrorMessage());