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(&params));
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