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());