Add HMAC key sharing tests

Bug: 70409878
Test: VtsHalKeymasterV4_0TargetTest
Change-Id: I9da12a70ce04f606980b5c8bec8deaeaa318bf81
diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp
index fc6941f..e705d66 100644
--- a/keymaster/4.0/vts/functional/Android.bp
+++ b/keymaster/4.0/vts/functional/Android.bp
@@ -18,6 +18,7 @@
     name: "VtsHalKeymasterV4_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
+        "HmacKeySharingTest.cpp",
         "KeymasterHidlTest.cpp",
         "keymaster_hidl_hal_test.cpp",
     ],
diff --git a/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
new file mode 100644
index 0000000..96c47a8
--- /dev/null
+++ b/keymaster/4.0/vts/functional/HmacKeySharingTest.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "KeymasterHidlTest.h"
+
+namespace android {
+namespace hardware {
+namespace keymaster {
+namespace V4_0 {
+namespace test {
+
+/**
+ * HmacKeySharingTest extends KeymasterHidlTest with some utilities that make writing HMAC sharing
+ * tests easier.
+ */
+class HmacKeySharingTest : public KeymasterHidlTest {
+   protected:
+    struct GetParamsResult {
+        ErrorCode error;
+        HmacSharingParameters params;
+        auto tie() { return std::tie(error, params); }
+    };
+
+    struct ComputeHmacResult {
+        ErrorCode error;
+        HidlBuf sharing_check;
+        auto tie() { return std::tie(error, sharing_check); }
+    };
+
+    using KeymasterVec = std::vector<sp<IKeymasterDevice>>;
+    using ByteString = std::basic_string<uint8_t>;
+    // using NonceVec = std::vector<HidlBuf>;
+
+    GetParamsResult getHmacSharingParameters(IKeymasterDevice& keymaster) {
+        GetParamsResult result;
+        EXPECT_TRUE(keymaster
+                        .getHmacSharingParameters([&](auto error, auto params) {
+                            result.tie() = std::tie(error, params);
+                        })
+                        .isOk());
+        return result;
+    }
+
+    hidl_vec<HmacSharingParameters> getHmacSharingParameters(const KeymasterVec& keymasters) {
+        std::vector<HmacSharingParameters> paramsVec;
+        for (auto& keymaster : keymasters) {
+            auto result = getHmacSharingParameters(*keymaster);
+            EXPECT_EQ(ErrorCode::OK, result.error);
+            if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
+        }
+        return paramsVec;
+    }
+
+    ComputeHmacResult computeSharedHmac(IKeymasterDevice& keymaster,
+                                        const hidl_vec<HmacSharingParameters>& params) {
+        ComputeHmacResult result;
+        EXPECT_TRUE(keymaster
+                        .computeSharedHmac(params,
+                                           [&](auto error, auto params) {
+                                               result.tie() = std::tie(error, params);
+                                           })
+                        .isOk());
+        return result;
+    }
+
+    std::vector<ComputeHmacResult> computeSharedHmac(
+        const KeymasterVec& keymasters, const hidl_vec<HmacSharingParameters>& paramsVec) {
+        std::vector<ComputeHmacResult> resultVec;
+        for (auto& keymaster : keymasters) {
+            resultVec.push_back(computeSharedHmac(*keymaster, paramsVec));
+        }
+        return resultVec;
+    }
+
+    std::vector<ByteString> copyNonces(const hidl_vec<HmacSharingParameters>& paramsVec) {
+        std::vector<ByteString> nonces;
+        for (auto& param : paramsVec) {
+            nonces.emplace_back(param.nonce.data(), param.nonce.size());
+        }
+        return nonces;
+    }
+
+    void verifyResponses(const HidlBuf& expected, const std::vector<ComputeHmacResult>& responses) {
+        for (auto& response : responses) {
+            EXPECT_EQ(ErrorCode::OK, response.error);
+            EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
+        }
+    }
+};
+
+TEST_F(HmacKeySharingTest, GetParameters) {
+    auto result1 = getHmacSharingParameters(keymaster());
+    EXPECT_EQ(ErrorCode::OK, result1.error);
+
+    auto result2 = getHmacSharingParameters(keymaster());
+    EXPECT_EQ(ErrorCode::OK, result2.error);
+
+    ASSERT_EQ(result1.params.seed, result2.params.seed)
+        << "A given keymaster should always return the same seed.";
+    ASSERT_EQ(result1.params.nonce, result2.params.nonce)
+        << "A given keymaster should always return the same nonce until restart.";
+}
+
+TEST_F(HmacKeySharingTest, ComputeSharedHmac) {
+    auto params = getHmacSharingParameters(all_keymasters());
+    ASSERT_EQ(all_keymasters().size(), params.size())
+        << "One or more keymasters failed to provide parameters.";
+
+    auto nonces = copyNonces(params);
+    EXPECT_EQ(all_keymasters().size(), nonces.size());
+    std::sort(nonces.begin(), nonces.end());
+    std::unique(nonces.begin(), nonces.end());
+    EXPECT_EQ(all_keymasters().size(), nonces.size());
+
+    auto responses = computeSharedHmac(all_keymasters(), params);
+    ASSERT_GT(responses.size(), 0U);
+    verifyResponses(responses[0].sharing_check, responses);
+
+    // Do it a second time.  Should get the same answers.
+    params = getHmacSharingParameters(all_keymasters());
+    ASSERT_EQ(all_keymasters().size(), params.size())
+        << "One or more keymasters failed to provide parameters.";
+
+    responses = computeSharedHmac(all_keymasters(), params);
+    ASSERT_GT(responses.size(), 0U);
+    verifyResponses(responses[0].sharing_check, responses);
+}
+
+template <class F>
+class final_action {
+   public:
+    explicit final_action(F f) : f_(move(f)) {}
+    ~final_action() { f_(); }
+
+   private:
+    F f_;
+};
+
+template <class F>
+inline final_action<F> finally(const F& f) {
+    return final_action<F>(f);
+}
+
+TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptNonce) {
+    // Important: The execution of this test gets the keymaster implementations on the device out of
+    // sync with respect to the HMAC key.  Granted that VTS tests aren't run on in-use production
+    // devices, this still has the potential to cause confusion.  To mitigate that, we always
+    // (barring crashes :-/) re-run the unmodified agreement process on our way out.
+    auto fixup_hmac = finally(
+        [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+
+    auto params = getHmacSharingParameters(all_keymasters());
+    ASSERT_EQ(all_keymasters().size(), params.size())
+        << "One or more keymasters failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeSharedHmac(all_keymasters(), params);
+
+    ASSERT_GT(responses.size(), 0U);
+    HidlBuf correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param, a random byte within the param's nonce, and a random bit within
+    // the byte.  Flip that bit.
+    size_t param_to_tweak = rand() % params.size();
+    uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
+    uint8_t bit_to_tweak = rand() % 8;
+    params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
+
+    responses = computeSharedHmac(all_keymasters(), params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                << "Keymaster that provided tweaked param should fail to compute HMAC key";
+        } else {
+            EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+            EXPECT_NE(correct_response, responses[i].sharing_check)
+                << "Others should calculate a different HMAC key, due to the tweaked nonce.";
+        }
+    }
+}
+
+TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptSeed) {
+    // Important: The execution of this test gets the keymaster implementations on the device out of
+    // sync with respect to the HMAC key.  Granted that VTS tests aren't run on in-use production
+    // devices, this still has the potential to cause confusion.  To mitigate that, we always
+    // (barring crashes :-/) re-run the unmodified agreement process on our way out.
+    auto fixup_hmac = finally(
+        [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); });
+
+    auto params = getHmacSharingParameters(all_keymasters());
+    ASSERT_EQ(all_keymasters().size(), params.size())
+        << "One or more keymasters failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeSharedHmac(all_keymasters(), params);
+
+    ASSERT_GT(responses.size(), 0U);
+    HidlBuf correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param and modify the seed.  We just increase the seed length by 1.  It doesn't
+    // matter what value is in the additional byte; it changes the seed regardless.
+    auto param_to_tweak = rand() % params.size();
+    auto& to_tweak = params[param_to_tweak].seed;
+    to_tweak.resize(to_tweak.size() + 1);
+
+    responses = computeSharedHmac(all_keymasters(), params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                << "Keymaster that provided tweaked param should fail to compute HMAC key ";
+        } else {
+            EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+            EXPECT_NE(correct_response, responses[i].sharing_check)
+                << "Others should calculate a different HMAC key, due to the tweaked nonce.";
+        }
+    }
+}
+
+}  // namespace test
+}  // namespace V4_0
+}  // namespace keymaster
+}  // namespace hardware
+}  // namespace android
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index a490341..0c73f05 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -101,6 +101,7 @@
     }
 
     static IKeymasterDevice& keymaster() { return *keymaster_; }
+    static const std::vector<sp<IKeymasterDevice>>& all_keymasters() { return all_keymasters_; }
     static uint32_t os_version() { return os_version_; }
     static uint32_t os_patch_level() { return os_patch_level_; }