1 /*
2 * Copyright (C) 2020 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 #define LOG_TAG "sharedsecret_test"
18 #include <android-base/logging.h>
19
20 #include <aidl/Vintf.h>
21 #include <aidl/android/hardware/security/keymint/ErrorCode.h>
22 #include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
23 #include <android/binder_manager.h>
24 #include <gtest/gtest.h>
25 #include <vector>
26
27 namespace aidl::android::hardware::security::sharedsecret::test {
28 using ::aidl::android::hardware::security::keymint::ErrorCode;
29 using ::std::shared_ptr;
30 using ::std::vector;
31 using Status = ::ndk::ScopedAStatus;
32
33 class SharedSecretAidlTest : public ::testing::Test {
34 public:
35 struct GetParamsResult {
36 ErrorCode error;
37 SharedSecretParameters params;
tieaidl::android::hardware::security::sharedsecret::test::SharedSecretAidlTest::GetParamsResult38 auto tie() { return std::tie(error, params); }
39 };
40
41 struct ComputeResult {
42 ErrorCode error;
43 vector<uint8_t> sharing_check;
tieaidl::android::hardware::security::sharedsecret::test::SharedSecretAidlTest::ComputeResult44 auto tie() { return std::tie(error, sharing_check); }
45 };
46
getSharedSecretParameters(shared_ptr<ISharedSecret> & sharedSecret)47 GetParamsResult getSharedSecretParameters(shared_ptr<ISharedSecret>& sharedSecret) {
48 SharedSecretParameters params;
49 auto error = GetReturnErrorCode(sharedSecret->getSharedSecretParameters(¶ms));
50 EXPECT_EQ(ErrorCode::OK, error);
51 EXPECT_TRUE(params.seed.size() == 0 || params.seed.size() == 32);
52 EXPECT_TRUE(params.nonce.size() == 32);
53
54 GetParamsResult result;
55 result.tie() = std::tie(error, params);
56 return result;
57 }
58
getAllSharedSecretParameters()59 vector<SharedSecretParameters> getAllSharedSecretParameters() {
60 vector<SharedSecretParameters> paramsVec;
61 for (auto& sharedSecret : allSharedSecrets_) {
62 auto result = getSharedSecretParameters(sharedSecret);
63 EXPECT_EQ(ErrorCode::OK, result.error);
64 if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
65 }
66 return paramsVec;
67 }
68
computeSharedSecret(shared_ptr<ISharedSecret> & sharedSecret,const vector<SharedSecretParameters> & params)69 ComputeResult computeSharedSecret(shared_ptr<ISharedSecret>& sharedSecret,
70 const vector<SharedSecretParameters>& params) {
71 std::vector<uint8_t> sharingCheck;
72 auto error = GetReturnErrorCode(sharedSecret->computeSharedSecret(params, &sharingCheck));
73 ComputeResult result;
74 result.tie() = std::tie(error, sharingCheck);
75 return result;
76 }
77
computeAllSharedSecrets(const vector<SharedSecretParameters> & params)78 vector<ComputeResult> computeAllSharedSecrets(const vector<SharedSecretParameters>& params) {
79 vector<ComputeResult> result;
80 for (auto& sharedSecret : allSharedSecrets_) {
81 result.push_back(computeSharedSecret(sharedSecret, params));
82 }
83 return result;
84 }
85
copyNonces(const vector<SharedSecretParameters> & paramsVec)86 vector<vector<uint8_t>> copyNonces(const vector<SharedSecretParameters>& paramsVec) {
87 vector<vector<uint8_t>> nonces;
88 for (auto& param : paramsVec) {
89 nonces.push_back(param.nonce);
90 }
91 return nonces;
92 }
93
verifyResponses(const vector<uint8_t> & expected,const vector<ComputeResult> & responses)94 void verifyResponses(const vector<uint8_t>& expected, const vector<ComputeResult>& responses) {
95 for (auto& response : responses) {
96 EXPECT_EQ(ErrorCode::OK, response.error);
97 EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
98 }
99 }
100
GetReturnErrorCode(const Status & result)101 ErrorCode GetReturnErrorCode(const Status& result) {
102 if (result.isOk()) return ErrorCode::OK;
103 if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
104 return static_cast<ErrorCode>(result.getServiceSpecificError());
105 }
106 return ErrorCode::UNKNOWN_ERROR;
107 }
108
getSharedSecretService(const char * name)109 static shared_ptr<ISharedSecret> getSharedSecretService(const char* name) {
110 if (AServiceManager_isDeclared(name)) {
111 ::ndk::SpAIBinder binder(AServiceManager_waitForService(name));
112 return ISharedSecret::fromBinder(binder);
113 }
114 return nullptr;
115 }
116
allSharedSecrets()117 const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
118
SetUpTestCase()119 static void SetUpTestCase() {
120 ASSERT_TRUE(allSharedSecrets_.empty()) << "The Shared Secret vector is not empty.";
121 auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
122 for (const auto& name : names) {
123 auto servicePtr = getSharedSecretService(name.c_str());
124 if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
125 }
126 }
127
TearDownTestCase()128 static void TearDownTestCase() {}
SetUp()129 void SetUp() override {}
TearDown()130 void TearDown() override {}
131
132 private:
133 static vector<shared_ptr<ISharedSecret>> allSharedSecrets_;
134 };
135
136 vector<shared_ptr<ISharedSecret>> SharedSecretAidlTest::allSharedSecrets_;
137
TEST_F(SharedSecretAidlTest,GetParameters)138 TEST_F(SharedSecretAidlTest, GetParameters) {
139 auto sharedSecrets = allSharedSecrets();
140 if (sharedSecrets.empty()) {
141 GTEST_SKIP() << "Skipping the test because no shared secret service is found.";
142 }
143 for (auto sharedSecret : sharedSecrets) {
144 auto result1 = getSharedSecretParameters(sharedSecret);
145 EXPECT_EQ(ErrorCode::OK, result1.error);
146 auto result2 = getSharedSecretParameters(sharedSecret);
147 EXPECT_EQ(ErrorCode::OK, result2.error);
148 ASSERT_EQ(result1.params.seed, result2.params.seed)
149 << "A given shared secret service should always return the same seed.";
150 ASSERT_EQ(result1.params.nonce, result2.params.nonce)
151 << "A given shared secret service should always return the same nonce until "
152 "restart.";
153 }
154 }
155
TEST_F(SharedSecretAidlTest,ComputeSharedSecret)156 TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
157 auto sharedSecrets = allSharedSecrets();
158 if (sharedSecrets.empty()) {
159 GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
160 }
161 auto params = getAllSharedSecretParameters();
162 ASSERT_EQ(sharedSecrets.size(), params.size())
163 << "One or more shared secret services failed to provide parameters.";
164 auto nonces = copyNonces(params);
165 EXPECT_EQ(sharedSecrets.size(), nonces.size());
166 std::sort(nonces.begin(), nonces.end());
167 std::unique(nonces.begin(), nonces.end());
168 EXPECT_EQ(sharedSecrets.size(), nonces.size());
169
170 auto responses = computeAllSharedSecrets(params);
171 ASSERT_GT(responses.size(), 0U);
172 verifyResponses(responses[0].sharing_check, responses);
173
174 // Do it a second time. Should get the same answers.
175 params = getAllSharedSecretParameters();
176 ASSERT_EQ(sharedSecrets.size(), params.size())
177 << "One or more shared secret services failed to provide parameters.";
178
179 responses = computeAllSharedSecrets(params);
180 ASSERT_GT(responses.size(), 0U);
181 ASSERT_EQ(32U, responses[0].sharing_check.size());
182 verifyResponses(responses[0].sharing_check, responses);
183 }
184
185 template <class F>
186 class final_action {
187 public:
final_action(F f)188 explicit final_action(F f) : f_(std::move(f)) {}
~final_action()189 ~final_action() { f_(); }
190
191 private:
192 F f_;
193 };
194
195 template <class F>
finally(const F & f)196 inline final_action<F> finally(const F& f) {
197 return final_action<F>(f);
198 }
199
TEST_F(SharedSecretAidlTest,ComputeSharedSecretCorruptNonce)200 TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
201 auto sharedSecrets = allSharedSecrets();
202 if (sharedSecrets.empty()) {
203 GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
204 }
205 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
206
207 auto params = getAllSharedSecretParameters();
208 ASSERT_EQ(sharedSecrets.size(), params.size())
209 << "One or more shared secret services failed to provide parameters.";
210
211 // All should be well in the normal case
212 auto responses = computeAllSharedSecrets(params);
213
214 ASSERT_GT(responses.size(), 0U);
215 vector<uint8_t> correct_response = responses[0].sharing_check;
216 verifyResponses(correct_response, responses);
217
218 // Pick a random param, a random byte within the param's nonce, and a random bit within
219 // the byte. Flip that bit.
220 size_t param_to_tweak = rand() % params.size();
221 uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
222 uint8_t bit_to_tweak = rand() % 8;
223 params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
224
225 responses = computeAllSharedSecrets(params);
226 for (size_t i = 0; i < responses.size(); ++i) {
227 if (i == param_to_tweak) {
228 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
229 << "Shared secret service that provided tweaked param should fail to compute "
230 "shared secret";
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 shared secret, due to the tweaked "
235 "nonce.";
236 }
237 }
238 }
239
TEST_F(SharedSecretAidlTest,ComputeSharedSecretShortNonce)240 TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortNonce) {
241 auto sharedSecrets = allSharedSecrets();
242 if (sharedSecrets.empty()) {
243 GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
244 }
245 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
246
247 auto params = getAllSharedSecretParameters();
248 ASSERT_EQ(sharedSecrets.size(), params.size())
249 << "One or more shared secret services failed to provide parameters.";
250
251 // All should be well in the normal case
252 auto responses = computeAllSharedSecrets(params);
253
254 ASSERT_GT(responses.size(), 0U);
255 vector<uint8_t> correct_response = responses[0].sharing_check;
256 verifyResponses(correct_response, responses);
257
258 // Pick a random param and shorten that nonce by one.
259 size_t param_to_tweak = rand() % params.size();
260 auto& to_tweak = params[param_to_tweak].nonce;
261 ASSERT_TRUE(to_tweak.size() == 32);
262 to_tweak.resize(31);
263
264 responses = computeAllSharedSecrets(params);
265 for (size_t i = 0; i < responses.size(); ++i) {
266 if (i == param_to_tweak) {
267 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
268 << "Shared secret service that provided tweaked param should fail to compute "
269 "shared secret";
270 } else {
271 // Other services *may* succeed, or may notice the invalid size for the nonce.
272 // However, if another service completes the computation, it should get the 'wrong'
273 // answer.
274 if (responses[i].error == ErrorCode::OK) {
275 EXPECT_NE(correct_response, responses[i].sharing_check)
276 << "Others should calculate a different shared secret, due to the tweaked "
277 "nonce.";
278 } else {
279 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
280 }
281 }
282 }
283 }
284
TEST_F(SharedSecretAidlTest,ComputeSharedSecretCorruptSeed)285 TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
286 auto sharedSecrets = allSharedSecrets();
287 if (sharedSecrets.empty()) {
288 GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
289 }
290 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
291 auto params = getAllSharedSecretParameters();
292 ASSERT_EQ(sharedSecrets.size(), params.size())
293 << "One or more shared secret service failed to provide parameters.";
294
295 // All should be well in the normal case
296 auto responses = computeAllSharedSecrets(params);
297
298 ASSERT_GT(responses.size(), 0U);
299 vector<uint8_t> correct_response = responses[0].sharing_check;
300 verifyResponses(correct_response, responses);
301
302 // Pick a random param and modify the seed. We just increase the seed length by 1. It doesn't
303 // matter what value is in the additional byte; it changes the seed regardless.
304 auto param_to_tweak = rand() % params.size();
305 auto& to_tweak = params[param_to_tweak].seed;
306 ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
307 if (!to_tweak.size()) {
308 to_tweak.resize(32); // Contents don't matter; a little randomization is nice.
309 }
310 to_tweak[0]++;
311
312 responses = computeAllSharedSecrets(params);
313 for (size_t i = 0; i < responses.size(); ++i) {
314 if (i == param_to_tweak) {
315 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
316 << "Shared secret service that provided tweaked param should fail to compute "
317 "shared secret";
318 } else {
319 EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
320 EXPECT_NE(correct_response, responses[i].sharing_check)
321 << "Others should calculate a different shared secret, due to the tweaked "
322 "nonce.";
323 }
324 }
325 }
326
TEST_F(SharedSecretAidlTest,ComputeSharedSecretShortSeed)327 TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortSeed) {
328 auto sharedSecrets = allSharedSecrets();
329 if (sharedSecrets.empty()) {
330 GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
331 }
332 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
333 auto params = getAllSharedSecretParameters();
334 ASSERT_EQ(sharedSecrets.size(), params.size())
335 << "One or more shared secret service failed to provide parameters.";
336
337 // All should be well in the normal case
338 auto responses = computeAllSharedSecrets(params);
339
340 ASSERT_GT(responses.size(), 0U);
341 vector<uint8_t> correct_response = responses[0].sharing_check;
342 verifyResponses(correct_response, responses);
343
344 // Pick a random param and modify the seed to be of (invalid) length 31.
345 auto param_to_tweak = rand() % params.size();
346 auto& to_tweak = params[param_to_tweak].seed;
347 ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
348 to_tweak.resize(31);
349
350 responses = computeAllSharedSecrets(params);
351 for (size_t i = 0; i < responses.size(); ++i) {
352 if (i == param_to_tweak) {
353 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
354 << "Shared secret service that provided tweaked param should fail to compute "
355 "shared secret";
356 } else {
357 // Other services *may* succeed, or may notice the invalid size for the seed.
358 // However, if another service completes the computation, it should get the 'wrong'
359 // answer.
360 if (responses[i].error == ErrorCode::OK) {
361 EXPECT_NE(correct_response, responses[i].sharing_check)
362 << "Others should calculate a different shared secret, due to the tweaked "
363 "seed.";
364 } else {
365 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
366 }
367 }
368 }
369 }
370
371 } // namespace aidl::android::hardware::security::sharedsecret::test
372
main(int argc,char ** argv)373 int main(int argc, char** argv) {
374 ::testing::InitGoogleTest(&argc, argv);
375 return RUN_ALL_TESTS();
376 }
377