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>]");
-    }
-}