blob: 83f6ef39dbeb79e6ba8d582a158ba5c4a5952e5c [file] [log] [blame]
Chirag Pathak8960aae2021-01-25 21:37:06 +00001/*
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
27namespace aidl::android::hardware::security::sharedsecret::test {
28using ::aidl::android::hardware::security::keymint::ErrorCode;
29using ::std::shared_ptr;
30using ::std::vector;
31using Status = ::ndk::ScopedAStatus;
32
33class SharedSecretAidlTest : public ::testing::Test {
34 public:
35 struct GetParamsResult {
36 ErrorCode error;
37 SharedSecretParameters params;
38 auto tie() { return std::tie(error, params); }
39 };
40
41 struct ComputeResult {
42 ErrorCode error;
43 vector<uint8_t> sharing_check;
44 auto tie() { return std::tie(error, sharing_check); }
45 };
46
47 GetParamsResult getSharedSecretParameters(shared_ptr<ISharedSecret>& sharedSecret) {
48 SharedSecretParameters params;
49 auto error = GetReturnErrorCode(sharedSecret->getSharedSecretParameters(&params));
50 EXPECT_EQ(ErrorCode::OK, error);
51 GetParamsResult result;
52 result.tie() = std::tie(error, params);
53 return result;
54 }
55
56 vector<SharedSecretParameters> getAllSharedSecretParameters() {
57 vector<SharedSecretParameters> paramsVec;
58 for (auto& sharedSecret : allSharedSecrets_) {
59 auto result = getSharedSecretParameters(sharedSecret);
60 EXPECT_EQ(ErrorCode::OK, result.error);
61 if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
62 }
63 return paramsVec;
64 }
65
66 ComputeResult computeSharedSecret(shared_ptr<ISharedSecret>& sharedSecret,
67 const vector<SharedSecretParameters>& params) {
68 std::vector<uint8_t> sharingCheck;
69 auto error = GetReturnErrorCode(sharedSecret->computeSharedSecret(params, &sharingCheck));
70 ComputeResult result;
71 result.tie() = std::tie(error, sharingCheck);
72 return result;
73 }
74
75 vector<ComputeResult> computeAllSharedSecrets(const vector<SharedSecretParameters>& params) {
76 vector<ComputeResult> result;
77 for (auto& sharedSecret : allSharedSecrets_) {
78 result.push_back(computeSharedSecret(sharedSecret, params));
79 }
80 return result;
81 }
82
83 vector<vector<uint8_t>> copyNonces(const vector<SharedSecretParameters>& paramsVec) {
84 vector<vector<uint8_t>> nonces;
85 for (auto& param : paramsVec) {
86 nonces.push_back(param.nonce);
87 }
88 return nonces;
89 }
90
91 void verifyResponses(const vector<uint8_t>& expected, const vector<ComputeResult>& responses) {
92 for (auto& response : responses) {
93 EXPECT_EQ(ErrorCode::OK, response.error);
94 EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
95 }
96 }
97
98 ErrorCode GetReturnErrorCode(const Status& result) {
99 if (result.isOk()) return ErrorCode::OK;
100 if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
101 return static_cast<ErrorCode>(result.getServiceSpecificError());
102 }
103 return ErrorCode::UNKNOWN_ERROR;
104 }
105
106 static shared_ptr<ISharedSecret> getSharedSecretService(const char* name) {
107 if (AServiceManager_isDeclared(name)) {
108 ::ndk::SpAIBinder binder(AServiceManager_waitForService(name));
109 return ISharedSecret::fromBinder(binder);
110 }
111 return nullptr;
112 }
113
114 const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
115
116 static void SetUpTestCase() {
117 if (allSharedSecrets_.empty()) {
118 auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
119 for (const auto& name : names) {
120 auto servicePtr = getSharedSecretService(name.c_str());
121 if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
122 }
123 }
124 }
125 static void TearDownTestCase() {}
126 void SetUp() override {}
127 void TearDown() override {}
128
129 private:
130 static vector<shared_ptr<ISharedSecret>> allSharedSecrets_;
131};
132
133vector<shared_ptr<ISharedSecret>> SharedSecretAidlTest::allSharedSecrets_;
134
135TEST_F(SharedSecretAidlTest, GetParameters) {
136 auto sharedSecrets = allSharedSecrets();
137 for (auto sharedSecret : sharedSecrets) {
138 auto result1 = getSharedSecretParameters(sharedSecret);
139 EXPECT_EQ(ErrorCode::OK, result1.error);
140 auto result2 = getSharedSecretParameters(sharedSecret);
141 EXPECT_EQ(ErrorCode::OK, result2.error);
142 ASSERT_EQ(result1.params.seed, result2.params.seed)
143 << "A given shared secret service should always return the same seed.";
144 ASSERT_EQ(result1.params.nonce, result2.params.nonce)
145 << "A given shared secret service should always return the same nonce until "
146 "restart.";
147 }
148}
149
150TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
151 auto params = getAllSharedSecretParameters();
152 ASSERT_EQ(allSharedSecrets().size(), params.size())
153 << "One or more shared secret services failed to provide parameters.";
154 auto nonces = copyNonces(params);
155 EXPECT_EQ(allSharedSecrets().size(), nonces.size());
156 std::sort(nonces.begin(), nonces.end());
157 std::unique(nonces.begin(), nonces.end());
158 EXPECT_EQ(allSharedSecrets().size(), nonces.size());
159
160 auto responses = computeAllSharedSecrets(params);
161 ASSERT_GT(responses.size(), 0U);
162 verifyResponses(responses[0].sharing_check, responses);
163
164 // Do it a second time. Should get the same answers.
165 params = getAllSharedSecretParameters();
166 ASSERT_EQ(allSharedSecrets().size(), params.size())
167 << "One or more shared secret services failed to provide parameters.";
168
169 responses = computeAllSharedSecrets(params);
170 ASSERT_GT(responses.size(), 0U);
171 ASSERT_EQ(32U, responses[0].sharing_check.size());
172 verifyResponses(responses[0].sharing_check, responses);
173}
174
175template <class F>
176class final_action {
177 public:
178 explicit final_action(F f) : f_(std::move(f)) {}
179 ~final_action() { f_(); }
180
181 private:
182 F f_;
183};
184
185template <class F>
186inline final_action<F> finally(const F& f) {
187 return final_action<F>(f);
188}
189
190TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
191 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
192
193 auto params = getAllSharedSecretParameters();
194 ASSERT_EQ(allSharedSecrets().size(), params.size())
195 << "One or more shared secret services failed to provide parameters.";
196
197 // All should be well in the normal case
198 auto responses = computeAllSharedSecrets(params);
199
200 ASSERT_GT(responses.size(), 0U);
201 vector<uint8_t> correct_response = responses[0].sharing_check;
202 verifyResponses(correct_response, responses);
203
204 // Pick a random param, a random byte within the param's nonce, and a random bit within
205 // the byte. Flip that bit.
206 size_t param_to_tweak = rand() % params.size();
207 uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
208 uint8_t bit_to_tweak = rand() % 8;
209 params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
210
211 responses = computeAllSharedSecrets(params);
212 for (size_t i = 0; i < responses.size(); ++i) {
213 if (i == param_to_tweak) {
214 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
215 << "Shared secret service that provided tweaked param should fail to compute "
216 "shared secret";
217 } else {
218 EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
219 EXPECT_NE(correct_response, responses[i].sharing_check)
220 << "Others should calculate a different shared secret, due to the tweaked "
221 "nonce.";
222 }
223 }
224}
225
226TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
227 auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
228 auto params = getAllSharedSecretParameters();
229 ASSERT_EQ(allSharedSecrets().size(), params.size())
230 << "One or more shared secret service failed to provide parameters.";
231
232 // All should be well in the normal case
233 auto responses = computeAllSharedSecrets(params);
234
235 ASSERT_GT(responses.size(), 0U);
236 vector<uint8_t> correct_response = responses[0].sharing_check;
237 verifyResponses(correct_response, responses);
238
239 // Pick a random param and modify the seed. We just increase the seed length by 1. It doesn't
240 // matter what value is in the additional byte; it changes the seed regardless.
241 auto param_to_tweak = rand() % params.size();
242 auto& to_tweak = params[param_to_tweak].seed;
243 ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
244 if (!to_tweak.size()) {
245 to_tweak.resize(32); // Contents don't matter; a little randomization is nice.
246 }
247 to_tweak[0]++;
248
249 responses = computeAllSharedSecrets(params);
250 for (size_t i = 0; i < responses.size(); ++i) {
251 if (i == param_to_tweak) {
252 EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
253 << "Shared secret service that provided tweaked param should fail to compute "
254 "shared secret";
255 } else {
256 EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
257 EXPECT_NE(correct_response, responses[i].sharing_check)
258 << "Others should calculate a different shared secret, due to the tweaked "
259 "nonce.";
260 }
261 }
262}
263} // namespace aidl::android::hardware::security::sharedsecret::test
264
265int main(int argc, char** argv) {
266 ::testing::InitGoogleTest(&argc, argv);
267 return RUN_ALL_TESTS();
268}