Merge "Add keystore API for metrics re-routing." into sc-dev
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index f6f8bfe..64849c1 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -304,33 +304,36 @@
static std::vector<KeyCharacteristics>
processLegacyCharacteristics(KeyMintSecurityLevel securityLevel,
const std::vector<KeyParameter>& genParams,
- const V4_0_KeyCharacteristics& legacyKc, bool hwEnforcedOnly = false) {
+ const V4_0_KeyCharacteristics& legacyKc, bool kmEnforcedOnly = false) {
- KeyCharacteristics hwEnforced{securityLevel,
- convertKeyParametersFromLegacy(legacyKc.hardwareEnforced)};
+ KeyCharacteristics kmEnforced{securityLevel, convertKeyParametersFromLegacy(
+ securityLevel == KeyMintSecurityLevel::SOFTWARE
+ ? legacyKc.softwareEnforced
+ : legacyKc.hardwareEnforced)};
- if (hwEnforcedOnly) {
- return {hwEnforced};
+ if (securityLevel == KeyMintSecurityLevel::SOFTWARE && legacyKc.hardwareEnforced.size() > 0) {
+ LOG(WARNING) << "Unexpected hardware enforced parameters.";
}
- KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE,
- convertKeyParametersFromLegacy(legacyKc.softwareEnforced)};
+ if (kmEnforcedOnly) {
+ return {kmEnforced};
+ }
+
+ KeyCharacteristics keystoreEnforced{KeyMintSecurityLevel::KEYSTORE, {}};
+
+ if (securityLevel != KeyMintSecurityLevel::SOFTWARE) {
+ // Don't include these tags on software backends, else they'd end up duplicated
+ // across both the keystore-enforced and software keymaster-enforced tags.
+ keystoreEnforced.authorizations = convertKeyParametersFromLegacy(legacyKc.softwareEnforced);
+ }
// Add all parameters that we know can be enforced by keystore but not by the legacy backend.
auto unsupported_requested = extractNewAndKeystoreEnforceableParams(genParams);
- std::copy(unsupported_requested.begin(), unsupported_requested.end(),
- std::back_insert_iterator(keystoreEnforced.authorizations));
+ keystoreEnforced.authorizations.insert(keystoreEnforced.authorizations.end(),
+ std::begin(unsupported_requested),
+ std::end(unsupported_requested));
- if (securityLevel == KeyMintSecurityLevel::SOFTWARE) {
- // If the security level of the backend is `software` we expect the hardware enforced list
- // to be empty. Log a warning otherwise.
- if (legacyKc.hardwareEnforced.size() != 0) {
- LOG(WARNING) << "Unexpected hardware enforced parameters.";
- }
- return {keystoreEnforced};
- }
-
- return {hwEnforced, keystoreEnforced};
+ return {kmEnforced, keystoreEnforced};
}
static V4_0_KeyFormat convertKeyFormatToLegacy(const KeyFormat& kf) {
@@ -722,7 +725,7 @@
km_error = convert(errorCode);
*keyCharacteristics =
processLegacyCharacteristics(securityLevel_, {} /* getParams */,
- v40KeyCharacteristics, true /* hwEnforcedOnly */);
+ v40KeyCharacteristics, true /* kmEnforcedOnly */);
});
if (!ret.isOk()) {
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 1f3f8e8..40ffd0c 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -302,9 +302,17 @@
(mac.len() as u8),
];
cose_mac_0.append(&mut mac);
+ // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
+ // the COSE_Key struct to denote that.
+ let test_mode_entry_shift = if test_mode { 0 } else { 6 };
+ let byte_dist_mac0_payload = 8;
+ let cose_key_size = 83 - test_mode_entry_shift;
for maced_public_key in keys_to_sign {
- if maced_public_key.macedKey.len() > 83 + 8 {
- cose_mac_0.extend_from_slice(&maced_public_key.macedKey[8..83 + 8]);
+ if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
+ cose_mac_0.extend_from_slice(
+ &maced_public_key.macedKey
+ [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
+ );
}
}
Ok(cose_mac_0)
diff --git a/keystore2/vpnprofilestore/lib.rs b/keystore2/vpnprofilestore/lib.rs
index baa632f..548bec5 100644
--- a/keystore2/vpnprofilestore/lib.rs
+++ b/keystore2/vpnprofilestore/lib.rs
@@ -467,9 +467,6 @@
const PROFILE_COUNT: u32 = 5000u32;
const PROFILE_DB_COUNT: u32 = 5000u32;
- let mode: String = db.conn.pragma_query_value(None, "journal_mode", |row| row.get(0))?;
- assert_eq!(mode, "wal");
-
let mut actual_profile_count = PROFILE_COUNT;
// First insert PROFILE_COUNT profiles.
for count in 0..PROFILE_COUNT {
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index cab92e2..3d5243a 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -227,13 +227,19 @@
while (!ec && it != end) {
if (it->is_regular_file()) {
- // Verify
+ // Verify the file is in fs-verity
auto result = isFileInVerity(it->path());
if (!result.ok()) {
return result.error();
}
digests[it->path()] = *result;
- } // TODO reject other types besides dirs?
+ } else if (it->is_directory()) {
+ // These are fine to ignore
+ } else if (it->is_symlink()) {
+ return Error() << "Rejecting artifacts, symlink at " << it->path();
+ } else {
+ return Error() << "Rejecting artifacts, unexpected file type for " << it->path();
+ }
++it;
}
if (ec) {
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index 12a21d1..86f0f12 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -43,15 +43,6 @@
},
}
-java_binary {
- name: "provisioner_cli",
- wrapper: "provisioner_cli",
- srcs: ["src/com/android/commands/provisioner/**/*.java"],
- static_libs: [
- "android.security.provisioner-java",
- ],
-}
-
cc_binary {
name: "rkp_factory_extraction_tool",
srcs: ["rkp_factory_extraction_tool.cpp"],
@@ -62,8 +53,9 @@
"libcppbor_external",
"libcppcose_rkp",
"libcrypto",
+ "libgflags",
"liblog",
+ "libkeymint_remote_prov_support",
"libvintf",
],
- //export_include_dirs: ["include"],
}
diff --git a/provisioner/provisioner_cli b/provisioner/provisioner_cli
deleted file mode 100755
index 7b53d6e..0000000
--- a/provisioner/provisioner_cli
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/system/bin/sh
-#
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# Script to start "provisioner_cli" on the device.
-#
-base=/system
-export CLASSPATH=$base/framework/provisioner_cli.jar
-exec app_process $base/bin com.android.commands.provisioner.Cli "$@"
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index d4842b1..a6c7d72 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -20,8 +20,11 @@
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <android/binder_manager.h>
#include <cppbor.h>
+#include <gflags/gflags.h>
#include <keymaster/cppcose/cppcose.h>
#include <log/log.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <sys/random.h>
#include <vintf/VintfObject.h>
using std::set;
@@ -32,6 +35,8 @@
using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
using aidl::android::hardware::security::keymint::MacedPublicKey;
using aidl::android::hardware::security::keymint::ProtectedData;
+using aidl::android::hardware::security::keymint::remote_prov::generateEekChain;
+using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
using android::vintf::HalManifest;
using android::vintf::VintfObject;
@@ -39,66 +44,36 @@
using namespace cppbor;
using namespace cppcose;
+DEFINE_bool(test_mode, false, "If enabled, a fake EEK key/cert are used.");
+
namespace {
const string kPackage = "android.hardware.security.keymint";
const string kInterface = "IRemotelyProvisionedComponent";
const string kFormattedName = kPackage + "." + kInterface + "/";
-ErrMsgOr<vector<uint8_t>> generateEekChain(size_t length, const vector<uint8_t>& eekId) {
- auto eekChain = cppbor::Array();
+constexpr size_t kChallengeSize = 16;
- vector<uint8_t> prevPrivKey;
- for (size_t i = 0; i < length - 1; ++i) {
- vector<uint8_t> pubKey(ED25519_PUBLIC_KEY_LEN);
- vector<uint8_t> privKey(ED25519_PRIVATE_KEY_LEN);
+std::vector<uint8_t> generateChallenge() {
+ std::vector<uint8_t> challenge(kChallengeSize);
- ED25519_keypair(pubKey.data(), privKey.data());
-
- // The first signing key is self-signed.
- if (prevPrivKey.empty()) prevPrivKey = privKey;
-
- auto coseSign1 = constructCoseSign1(prevPrivKey,
- cppbor::Map() /* payload CoseKey */
- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
- .add(CoseKey::ALGORITHM, EDDSA)
- .add(CoseKey::CURVE, ED25519)
- .add(CoseKey::PUBKEY_X, pubKey)
- .canonicalize()
- .encode(),
- {} /* AAD */);
- if (!coseSign1) return coseSign1.moveMessage();
- eekChain.add(coseSign1.moveValue());
-
- prevPrivKey = privKey;
+ ssize_t bytesRemaining = static_cast<ssize_t>(challenge.size());
+ uint8_t* writePtr = challenge.data();
+ while (bytesRemaining > 0) {
+ int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
+ if (bytesRead < 0) {
+ LOG_FATAL_IF(errno != EINTR, "%d - %s", errno, strerror(errno));
+ }
+ bytesRemaining -= bytesRead;
+ writePtr += bytesRead;
}
- vector<uint8_t> pubKey(X25519_PUBLIC_VALUE_LEN);
- vector<uint8_t> privKey(X25519_PRIVATE_KEY_LEN);
- X25519_keypair(pubKey.data(), privKey.data());
-
- auto coseSign1 = constructCoseSign1(prevPrivKey,
- cppbor::Map() /* payload CoseKey */
- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
- .add(CoseKey::KEY_ID, eekId)
- .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
- .add(CoseKey::CURVE, cppcose::X25519)
- .add(CoseKey::PUBKEY_X, pubKey)
- .canonicalize()
- .encode(),
- {} /* AAD */);
- if (!coseSign1) return coseSign1.moveMessage();
- eekChain.add(coseSign1.moveValue());
-
- return eekChain.encode();
-}
-
-std::vector<uint8_t> getChallenge() {
- return std::vector<uint8_t>(0);
+ return challenge;
}
std::vector<uint8_t> composeCertificateRequest(ProtectedData&& protectedData,
- DeviceInfo&& deviceInfo) {
+ DeviceInfo&& deviceInfo,
+ const std::vector<uint8_t>& challenge) {
Array emptyMacedKeysToSign;
emptyMacedKeysToSign
.add(std::vector<uint8_t>(0)) // empty protected headers as bstr
@@ -107,7 +82,7 @@
.add(std::vector<uint8_t>(0)); // empty tag as bstr
Array certificateRequest;
certificateRequest.add(EncodedItem(std::move(deviceInfo.deviceInfo)))
- .add(getChallenge()) // fake challenge
+ .add(challenge)
.add(EncodedItem(std::move(protectedData.protectedData)))
.add(std::move(emptyMacedKeysToSign));
return certificateRequest.encode();
@@ -118,9 +93,27 @@
return -1;
}
+std::vector<uint8_t> getEekChain() {
+ if (FLAGS_test_mode) {
+ const std::vector<uint8_t> kFakeEekId = {'f', 'a', 'k', 'e', 0};
+ auto eekOrErr = generateEekChain(3 /* chainlength */, kFakeEekId);
+ LOG_FATAL_IF(!eekOrErr, "Failed to generate test EEK somehow: %s",
+ eekOrErr.message().c_str());
+ auto [eek, ignored_pubkey, ignored_privkey] = eekOrErr.moveValue();
+ return eek;
+ }
+
+ return getProdEekChain();
+}
+
} // namespace
-int main() {
+int main(int argc, char** argv) {
+ gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags=*/true);
+
+ const std::vector<uint8_t> eek_chain = getEekChain();
+ const std::vector<uint8_t> challenge = generateChallenge();
+
std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
set<string> rkpNames = manifest->getAidlInstances(kPackage, kInterface);
for (auto name : rkpNames) {
@@ -136,29 +129,19 @@
std::vector<uint8_t> keysToSignMac;
std::vector<MacedPublicKey> emptyKeys;
- // Replace this eek chain generation with the actual production GEEK
- std::vector<uint8_t> eekId(10); // replace with real KID later (EEK fingerprint)
- auto eekOrErr = generateEekChain(3 /* chainlength */, eekId);
- if (!eekOrErr) {
- ALOGE("Failed to generate test EEK somehow: %s", eekOrErr.message().c_str());
- return errorMsg(name);
- }
-
- std::vector<uint8_t> eek = eekOrErr.moveValue();
DeviceInfo deviceInfo;
ProtectedData protectedData;
if (rkp_service) {
ALOGE("extracting bundle");
::ndk::ScopedAStatus status = rkp_service->generateCertificateRequest(
- true /* testMode */, emptyKeys, eek, getChallenge(), &deviceInfo, &protectedData,
+ FLAGS_test_mode, emptyKeys, eek_chain, challenge, &deviceInfo, &protectedData,
&keysToSignMac);
if (!status.isOk()) {
ALOGE("Bundle extraction failed. Error code: %d", status.getServiceSpecificError());
return errorMsg(name);
}
- std::cout << "\n";
- std::vector<uint8_t> certificateRequest =
- composeCertificateRequest(std::move(protectedData), std::move(deviceInfo));
+ std::vector<uint8_t> certificateRequest = composeCertificateRequest(
+ std::move(protectedData), std::move(deviceInfo), challenge);
std::copy(certificateRequest.begin(), certificateRequest.end(),
std::ostream_iterator<char>(std::cout));
}
diff --git a/provisioner/src/com/android/commands/provisioner/Cli.java b/provisioner/src/com/android/commands/provisioner/Cli.java
deleted file mode 100644
index 62afdac..0000000
--- a/provisioner/src/com/android/commands/provisioner/Cli.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.commands.provisioner;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.security.provisioner.IProvisionerService;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.IllegalArgumentException;
-
-/**
- * Contains the implementation of the remote provisioning command-line interface.
- */
-public class Cli extends BaseCommand {
- /**
- * Creates an instance of the command-line interface and runs it. This is the entry point of
- * the tool.
- */
- public static void main(String[] args) {
- new Cli().run(args);
- }
-
- /**
- * Runs the command requested by the invoker. It parses the very first required argument, which
- * is the command, and calls the appropriate handler.
- */
- @Override
- public void onRun() throws Exception {
- String cmd = nextArgRequired();
- switch (cmd) {
- case "get-req":
- getRequest();
- break;
-
- case "help":
- onShowUsage(System.out);
- break;
-
- default:
- throw new IllegalArgumentException("unknown command: " + cmd);
- }
- }
-
- /**
- * Retrieves a 'certificate request' from the provisioning service. The COSE-encoded
- * 'certificate chain' describing the endpoint encryption key (EEK) to use for encryption is
- * read from the standard input. The retrieved request is written to the standard output.
- */
- private void getRequest() throws Exception {
- // Process options.
- boolean test = false;
- byte[] challenge = null;
- int count = 0;
- String arg;
- while ((arg = nextArg()) != null) {
- switch (arg) {
- case "--test":
- test = true;
- break;
-
- case "--challenge":
- // TODO: We may need a different encoding of the challenge.
- challenge = nextArgRequired().getBytes();
- break;
-
- case "--count":
- count = Integer.parseInt(nextArgRequired());
- if (count < 0) {
- throw new IllegalArgumentException(
- "--count must be followed by non-negative number");
- }
- break;
-
- default:
- throw new IllegalArgumentException("unknown argument: " + arg);
- }
- }
-
- // Send the request over to the provisioning service and write the result to stdout.
- byte[] res = getService().getCertificateRequest(test, count, readAll(System.in), challenge);
- if (res != null) {
- System.out.write(res);
- }
- }
-
- /**
- * Retrieves an implementation of the IProvisionerService interface. It allows the caller to
- * call into the service via binder.
- */
- private static IProvisionerService getService() throws RemoteException {
- IBinder binder = ServiceManager.getService("remote-provisioner");
- if (binder == null) {
- throw new RemoteException("Provisioning service is inaccessible");
- }
- return IProvisionerService.Stub.asInterface(binder);
- }
-
- /** Reads all data from the provided input stream and returns it as a byte array. */
- private static byte[] readAll(InputStream in) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- byte[] buf = new byte[1024];
- int read;
- while ((read = in.read(buf)) != -1) {
- out.write(buf, 0, read);
- }
- return out.toByteArray();
- }
-
- /**
- * Writes the usage information to the given stream. This is displayed to users of the tool when
- * they ask for help or when they pass incorrect arguments to the tool.
- */
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "Usage: provisioner_cli <command> [options]\n" +
- "Commands: help\n" +
- " get-req [--count <n>] [--test] [--challenge <v>]");
- }
-}