1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "KeymasterHidlTest.h" 18 19 namespace android { 20 namespace hardware { 21 namespace keymaster { 22 namespace V4_0 { 23 namespace test { 24 25 /** 26 * HmacKeySharingTest extends KeymasterHidlTest with some utilities that make writing HMAC sharing 27 * tests easier. 28 */ 29 class HmacKeySharingTest : public KeymasterHidlTest { 30 protected: 31 struct GetParamsResult { 32 ErrorCode error; 33 HmacSharingParameters params; 34 auto tie() { return std::tie(error, params); } 35 }; 36 37 struct ComputeHmacResult { 38 ErrorCode error; 39 HidlBuf sharing_check; 40 auto tie() { return std::tie(error, sharing_check); } 41 }; 42 43 using KeymasterVec = std::vector<sp<IKeymasterDevice>>; 44 using ByteString = std::basic_string<uint8_t>; 45 // using NonceVec = std::vector<HidlBuf>; 46 47 GetParamsResult getHmacSharingParameters(IKeymasterDevice& keymaster) { 48 GetParamsResult result; 49 EXPECT_TRUE(keymaster 50 .getHmacSharingParameters([&](auto error, auto params) { 51 result.tie() = std::tie(error, params); 52 }) 53 .isOk()); 54 return result; 55 } 56 57 hidl_vec<HmacSharingParameters> getHmacSharingParameters(const KeymasterVec& keymasters) { 58 std::vector<HmacSharingParameters> paramsVec; 59 for (auto& keymaster : keymasters) { 60 auto result = getHmacSharingParameters(*keymaster); 61 EXPECT_EQ(ErrorCode::OK, result.error); 62 if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params)); 63 } 64 return paramsVec; 65 } 66 67 ComputeHmacResult computeSharedHmac(IKeymasterDevice& keymaster, 68 const hidl_vec<HmacSharingParameters>& params) { 69 ComputeHmacResult result; 70 EXPECT_TRUE(keymaster 71 .computeSharedHmac(params, 72 [&](auto error, auto params) { 73 result.tie() = std::tie(error, params); 74 }) 75 .isOk()); 76 return result; 77 } 78 79 std::vector<ComputeHmacResult> computeSharedHmac( 80 const KeymasterVec& keymasters, const hidl_vec<HmacSharingParameters>& paramsVec) { 81 std::vector<ComputeHmacResult> resultVec; 82 for (auto& keymaster : keymasters) { 83 resultVec.push_back(computeSharedHmac(*keymaster, paramsVec)); 84 } 85 return resultVec; 86 } 87 88 std::vector<ByteString> copyNonces(const hidl_vec<HmacSharingParameters>& paramsVec) { 89 std::vector<ByteString> nonces; 90 for (auto& param : paramsVec) { 91 nonces.emplace_back(param.nonce.data(), param.nonce.size()); 92 } 93 return nonces; 94 } 95 96 void verifyResponses(const HidlBuf& expected, const std::vector<ComputeHmacResult>& responses) { 97 for (auto& response : responses) { 98 EXPECT_EQ(ErrorCode::OK, response.error); 99 EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match."; 100 } 101 } 102 }; 103 104 TEST_F(HmacKeySharingTest, GetParameters) { 105 auto result1 = getHmacSharingParameters(keymaster()); 106 EXPECT_EQ(ErrorCode::OK, result1.error); 107 108 auto result2 = getHmacSharingParameters(keymaster()); 109 EXPECT_EQ(ErrorCode::OK, result2.error); 110 111 ASSERT_EQ(result1.params.seed, result2.params.seed) 112 << "A given keymaster should always return the same seed."; 113 ASSERT_EQ(result1.params.nonce, result2.params.nonce) 114 << "A given keymaster should always return the same nonce until restart."; 115 } 116 117 TEST_F(HmacKeySharingTest, ComputeSharedHmac) { 118 auto params = getHmacSharingParameters(all_keymasters()); 119 ASSERT_EQ(all_keymasters().size(), params.size()) 120 << "One or more keymasters failed to provide parameters."; 121 122 auto nonces = copyNonces(params); 123 EXPECT_EQ(all_keymasters().size(), nonces.size()); 124 std::sort(nonces.begin(), nonces.end()); 125 std::unique(nonces.begin(), nonces.end()); 126 EXPECT_EQ(all_keymasters().size(), nonces.size()); 127 128 auto responses = computeSharedHmac(all_keymasters(), params); 129 ASSERT_GT(responses.size(), 0U); 130 verifyResponses(responses[0].sharing_check, responses); 131 132 // Do it a second time. Should get the same answers. 133 params = getHmacSharingParameters(all_keymasters()); 134 ASSERT_EQ(all_keymasters().size(), params.size()) 135 << "One or more keymasters failed to provide parameters."; 136 137 responses = computeSharedHmac(all_keymasters(), params); 138 ASSERT_GT(responses.size(), 0U); 139 ASSERT_EQ(32U, responses[0].sharing_check.size()); 140 verifyResponses(responses[0].sharing_check, responses); 141 } 142 143 template <class F> 144 class final_action { 145 public: 146 explicit final_action(F f) : f_(move(f)) {} 147 ~final_action() { f_(); } 148 149 private: 150 F f_; 151 }; 152 153 template <class F> 154 inline final_action<F> finally(const F& f) { 155 return final_action<F>(f); 156 } 157 158 TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptNonce) { 159 // Important: The execution of this test gets the keymaster implementations on the device out of 160 // sync with respect to the HMAC key. Granted that VTS tests aren't run on in-use production 161 // devices, this still has the potential to cause confusion. To mitigate that, we always 162 // (barring crashes :-/) re-run the unmodified agreement process on our way out. 163 auto fixup_hmac = finally( 164 [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); }); 165 166 auto params = getHmacSharingParameters(all_keymasters()); 167 ASSERT_EQ(all_keymasters().size(), params.size()) 168 << "One or more keymasters failed to provide parameters."; 169 170 // All should be well in the normal case 171 auto responses = computeSharedHmac(all_keymasters(), params); 172 173 ASSERT_GT(responses.size(), 0U); 174 HidlBuf correct_response = responses[0].sharing_check; 175 verifyResponses(correct_response, responses); 176 177 // Pick a random param, a random byte within the param's nonce, and a random bit within 178 // the byte. Flip that bit. 179 size_t param_to_tweak = rand() % params.size(); 180 uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce); 181 uint8_t bit_to_tweak = rand() % 8; 182 params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak); 183 184 responses = computeSharedHmac(all_keymasters(), params); 185 for (size_t i = 0; i < responses.size(); ++i) { 186 if (i == param_to_tweak) { 187 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error) 188 << "Keymaster that provided tweaked param should fail to compute HMAC key"; 189 } else { 190 EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed"; 191 EXPECT_NE(correct_response, responses[i].sharing_check) 192 << "Others should calculate a different HMAC key, due to the tweaked nonce."; 193 } 194 } 195 } 196 197 TEST_F(HmacKeySharingTest, ComputeSharedHmacCorruptSeed) { 198 // Important: The execution of this test gets the keymaster implementations on the device out of 199 // sync with respect to the HMAC key. Granted that VTS tests aren't run on in-use production 200 // devices, this still has the potential to cause confusion. To mitigate that, we always 201 // (barring crashes :-/) re-run the unmodified agreement process on our way out. 202 auto fixup_hmac = finally( 203 [&]() { computeSharedHmac(all_keymasters(), getHmacSharingParameters(all_keymasters())); }); 204 205 auto params = getHmacSharingParameters(all_keymasters()); 206 ASSERT_EQ(all_keymasters().size(), params.size()) 207 << "One or more keymasters failed to provide parameters."; 208 209 // All should be well in the normal case 210 auto responses = computeSharedHmac(all_keymasters(), params); 211 212 ASSERT_GT(responses.size(), 0U); 213 HidlBuf correct_response = responses[0].sharing_check; 214 verifyResponses(correct_response, responses); 215 216 // Pick a random param and modify the seed. We just increase the seed length by 1. It doesn't 217 // matter what value is in the additional byte; it changes the seed regardless. 218 auto param_to_tweak = rand() % params.size(); 219 auto& to_tweak = params[param_to_tweak].seed; 220 ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0); 221 if (!to_tweak.size()) { 222 to_tweak.resize(32); // Contents don't matter; a little randomization is nice. 223 } 224 to_tweak[0]++; 225 226 responses = computeSharedHmac(all_keymasters(), params); 227 for (size_t i = 0; i < responses.size(); ++i) { 228 if (i == param_to_tweak) { 229 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error) 230 << "Keymaster that provided tweaked param should fail to compute HMAC key "; 231 } else { 232 EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed"; 233 EXPECT_NE(correct_response, responses[i].sharing_check) 234 << "Others should calculate a different HMAC key, due to the tweaked nonce."; 235 } 236 } 237 } 238 239 } // namespace test 240 } // namespace V4_0 241 } // namespace keymaster 242 } // namespace hardware 243 } // namespace android 244