1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/strings/stringprintf.h"
7#include "base/values.h"
8#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h"
9#include "chrome/browser/extensions/extension_api_unittest.h"
10#include "chrome/browser/extensions/extension_function_test_utils.h"
11#include "chrome/common/extensions/api/easy_unlock_private.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/fake_easy_unlock_client.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "third_party/cros_system_api/dbus/service_constants.h"
16
17namespace {
18
19namespace api = extensions::api::easy_unlock_private;
20
21using extensions::api::EasyUnlockPrivateGenerateEcP256KeyPairFunction;
22using extensions::api::EasyUnlockPrivatePerformECDHKeyAgreementFunction;
23using extensions::api::EasyUnlockPrivateCreateSecureMessageFunction;
24using extensions::api::EasyUnlockPrivateUnwrapSecureMessageFunction;
25
26// Converts a string to a base::BinaryValue value whose buffer contains the
27// string data without the trailing '\0'.
28base::BinaryValue* StringToBinaryValue(const std::string value) {
29  return base::BinaryValue::CreateWithCopiedBuffer(value.data(),
30                                                   value.length());
31}
32
33// Copies |private_key_source| and |public_key_source| to |private_key_target|
34// and |public_key_target|. It is used as a callback for
35// |EasyUnlockClient::GenerateEcP256KeyPair| to save the values returned by the
36// method.
37void CopyKeyPair(std::string* private_key_target,
38                 std::string* public_key_target,
39                 const std::string& private_key_source,
40                 const std::string& public_key_source) {
41  *private_key_target = private_key_source;
42  *public_key_target = public_key_source;
43}
44
45// Copies |data_source| to |data_target|. Used as a callback to EasyUnlockClient
46// methods to save the data returned by the method.
47void CopyData(std::string* data_target, const std::string& data_source) {
48  *data_target = data_source;
49}
50
51class EasyUnlockPrivateApiTest : public extensions::ExtensionApiUnittest {
52 public:
53  EasyUnlockPrivateApiTest() {}
54  virtual ~EasyUnlockPrivateApiTest() {}
55
56 protected:
57  virtual void SetUp() OVERRIDE {
58    chromeos::DBusThreadManager::Initialize();
59    client_ = chromeos::DBusThreadManager::Get()->GetEasyUnlockClient();
60
61    extensions::ExtensionApiUnittest::SetUp();
62  }
63
64  virtual void TearDown() OVERRIDE {
65    extensions::ExtensionApiUnittest::TearDown();
66
67    chromeos::DBusThreadManager::Shutdown();
68  }
69
70  // Extracts a single binary value result from a run extension function
71  // |function| and converts it to string.
72  // It will fail if the extension doesn't have exactly one binary value result.
73  // On failure, an empty string is returned.
74  std::string GetSingleBinaryResultAsString(
75        UIThreadExtensionFunction* function) {
76    const base::ListValue* result_list = function->GetResultList();
77    if (!result_list) {
78      LOG(ERROR) << "Function has no result list.";
79      return "";
80    }
81
82    if (result_list->GetSize() != 1u) {
83      LOG(ERROR) << "Invalid number of results.";
84      return "";
85    }
86
87    const base::BinaryValue* result_binary_value;
88    if (!result_list->GetBinary(0, &result_binary_value) ||
89        !result_binary_value) {
90      LOG(ERROR) << "Result not a binary value.";
91      return "";
92    }
93
94    return std::string(result_binary_value->GetBuffer(),
95                       result_binary_value->GetSize());
96  }
97
98  chromeos::EasyUnlockClient* client_;
99};
100
101TEST_F(EasyUnlockPrivateApiTest, GenerateEcP256KeyPair) {
102  scoped_refptr<EasyUnlockPrivateGenerateEcP256KeyPairFunction> function(
103      new EasyUnlockPrivateGenerateEcP256KeyPairFunction());
104  function->set_has_callback(true);
105
106  ASSERT_TRUE(extension_function_test_utils::RunFunction(
107      function.get(),
108      "[]",
109      browser(),
110      extension_function_test_utils::NONE));
111
112  const base::ListValue* result_list = function->GetResultList();
113  ASSERT_TRUE(result_list);
114  ASSERT_EQ(2u, result_list->GetSize());
115
116  const base::BinaryValue* public_key;
117  ASSERT_TRUE(result_list->GetBinary(0, &public_key));
118  ASSERT_TRUE(public_key);
119
120  const base::BinaryValue* private_key;
121  ASSERT_TRUE(result_list->GetBinary(1, &private_key));
122  ASSERT_TRUE(private_key);
123
124  EXPECT_TRUE(chromeos::FakeEasyUnlockClient::IsEcP256KeyPair(
125      std::string(private_key->GetBuffer(), private_key->GetSize()),
126      std::string(public_key->GetBuffer(), public_key->GetSize())));
127}
128
129TEST_F(EasyUnlockPrivateApiTest, PerformECDHKeyAgreement) {
130  scoped_refptr<EasyUnlockPrivatePerformECDHKeyAgreementFunction> function(
131      new EasyUnlockPrivatePerformECDHKeyAgreementFunction());
132  function->set_has_callback(true);
133
134  std::string private_key_1;
135  std::string public_key_1_unused;
136  client_->GenerateEcP256KeyPair(
137      base::Bind(&CopyKeyPair, &private_key_1, &public_key_1_unused));
138
139  std::string private_key_2_unused;
140  std::string public_key_2;
141  client_->GenerateEcP256KeyPair(
142      base::Bind(&CopyKeyPair, &private_key_2_unused, &public_key_2));
143
144  std::string expected_result;
145  client_->PerformECDHKeyAgreement(
146      private_key_1,
147      public_key_2,
148      base::Bind(&CopyData, &expected_result));
149  ASSERT_GT(expected_result.length(), 0u);
150
151  scoped_ptr<base::ListValue> args(new base::ListValue);
152  args->Append(StringToBinaryValue(private_key_1));
153  args->Append(StringToBinaryValue(public_key_2));
154
155  ASSERT_TRUE(extension_function_test_utils::RunFunction(
156      function.get(),
157      args.Pass(),
158      browser(),
159      extension_function_test_utils::NONE));
160
161  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
162}
163
164TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage) {
165  scoped_refptr<EasyUnlockPrivateCreateSecureMessageFunction> function(
166      new EasyUnlockPrivateCreateSecureMessageFunction());
167  function->set_has_callback(true);
168
169  chromeos::EasyUnlockClient::CreateSecureMessageOptions create_options;
170  create_options.key = "KEY";
171  create_options.associated_data = "ASSOCIATED_DATA";
172  create_options.public_metadata = "PUBLIC_METADATA";
173  create_options.verification_key_id = "VERIFICATION_KEY_ID";
174  create_options.decryption_key_id = "DECRYPTION_KEY_ID";
175  create_options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
176  create_options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
177
178  std::string expected_result;
179  client_->CreateSecureMessage(
180      "PAYLOAD",
181      create_options,
182      base::Bind(&CopyData, &expected_result));
183  ASSERT_GT(expected_result.length(), 0u);
184
185  scoped_ptr<base::ListValue> args(new base::ListValue);
186  args->Append(StringToBinaryValue("PAYLOAD"));
187  args->Append(StringToBinaryValue("KEY"));
188  base::DictionaryValue* options = new base::DictionaryValue();
189  args->Append(options);
190  options->Set("associatedData", StringToBinaryValue("ASSOCIATED_DATA"));
191  options->Set("publicMetadata", StringToBinaryValue("PUBLIC_METADATA"));
192  options->Set("verificationKeyId",
193               StringToBinaryValue("VERIFICATION_KEY_ID"));
194  options->Set("decryptionKeyId",
195               StringToBinaryValue("DECRYPTION_KEY_ID"));
196  options->SetString(
197      "encryptType",
198      api::ToString(api::ENCRYPTION_TYPE_AES_256_CBC));
199  options->SetString(
200      "signType",
201      api::ToString(api::SIGNATURE_TYPE_HMAC_SHA256));
202
203  ASSERT_TRUE(extension_function_test_utils::RunFunction(
204      function.get(),
205      args.Pass(),
206      browser(),
207      extension_function_test_utils::NONE));
208
209  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
210}
211
212TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage_EmptyOptions) {
213  scoped_refptr<EasyUnlockPrivateCreateSecureMessageFunction> function(
214      new EasyUnlockPrivateCreateSecureMessageFunction());
215  function->set_has_callback(true);
216
217  chromeos::EasyUnlockClient::CreateSecureMessageOptions create_options;
218  create_options.key = "KEY";
219  create_options.encryption_type = easy_unlock::kEncryptionTypeNone;
220  create_options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
221
222  std::string expected_result;
223  client_->CreateSecureMessage(
224      "PAYLOAD",
225      create_options,
226      base::Bind(&CopyData, &expected_result));
227  ASSERT_GT(expected_result.length(), 0u);
228
229  scoped_ptr<base::ListValue> args(new base::ListValue);
230  args->Append(StringToBinaryValue("PAYLOAD"));
231  args->Append(StringToBinaryValue("KEY"));
232  base::DictionaryValue* options = new base::DictionaryValue();
233  args->Append(options);
234
235  ASSERT_TRUE(extension_function_test_utils::RunFunction(
236      function.get(),
237      args.Pass(),
238      browser(),
239      extension_function_test_utils::NONE));
240
241  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
242}
243
244TEST_F(EasyUnlockPrivateApiTest, CreateSecureMessage_AsymmetricSign) {
245  scoped_refptr<EasyUnlockPrivateCreateSecureMessageFunction> function(
246      new EasyUnlockPrivateCreateSecureMessageFunction());
247  function->set_has_callback(true);
248
249  chromeos::EasyUnlockClient::CreateSecureMessageOptions create_options;
250  create_options.key = "KEY";
251  create_options.associated_data = "ASSOCIATED_DATA";
252  create_options.verification_key_id = "VERIFICATION_KEY_ID";
253  create_options.encryption_type = easy_unlock::kEncryptionTypeNone;
254  create_options.signature_type = easy_unlock::kSignatureTypeECDSAP256SHA256;
255
256  std::string expected_result;
257  client_->CreateSecureMessage(
258      "PAYLOAD",
259      create_options,
260      base::Bind(&CopyData, &expected_result));
261  ASSERT_GT(expected_result.length(), 0u);
262
263  scoped_ptr<base::ListValue> args(new base::ListValue);
264  args->Append(StringToBinaryValue("PAYLOAD"));
265  args->Append(StringToBinaryValue("KEY"));
266  base::DictionaryValue* options = new base::DictionaryValue();
267  args->Append(options);
268  options->Set("associatedData",
269               StringToBinaryValue("ASSOCIATED_DATA"));
270  options->Set("verificationKeyId",
271               StringToBinaryValue("VERIFICATION_KEY_ID"));
272  options->SetString(
273      "signType",
274      api::ToString(api::SIGNATURE_TYPE_ECDSA_P256_SHA256));
275
276  ASSERT_TRUE(extension_function_test_utils::RunFunction(
277      function.get(),
278      args.Pass(),
279      browser(),
280      extension_function_test_utils::NONE));
281
282  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
283}
284
285TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage) {
286  scoped_refptr<EasyUnlockPrivateUnwrapSecureMessageFunction> function(
287      new EasyUnlockPrivateUnwrapSecureMessageFunction());
288  function->set_has_callback(true);
289
290  chromeos::EasyUnlockClient::UnwrapSecureMessageOptions unwrap_options;
291  unwrap_options.key = "KEY";
292  unwrap_options.associated_data = "ASSOCIATED_DATA";
293  unwrap_options.encryption_type = easy_unlock::kEncryptionTypeAES256CBC;
294  unwrap_options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
295
296  std::string expected_result;
297  client_->UnwrapSecureMessage(
298      "MESSAGE",
299      unwrap_options,
300      base::Bind(&CopyData, &expected_result));
301  ASSERT_GT(expected_result.length(), 0u);
302
303  scoped_ptr<base::ListValue> args(new base::ListValue);
304  args->Append(StringToBinaryValue("MESSAGE"));
305  args->Append(StringToBinaryValue("KEY"));
306  base::DictionaryValue* options = new base::DictionaryValue();
307  args->Append(options);
308  options->Set("associatedData", StringToBinaryValue("ASSOCIATED_DATA"));
309  options->SetString(
310      "encryptType",
311      api::ToString(api::ENCRYPTION_TYPE_AES_256_CBC));
312  options->SetString(
313      "signType",
314      api::ToString(api::SIGNATURE_TYPE_HMAC_SHA256));
315
316  ASSERT_TRUE(extension_function_test_utils::RunFunction(
317      function.get(),
318      args.Pass(),
319      browser(),
320      extension_function_test_utils::NONE));
321
322  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
323}
324
325TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage_EmptyOptions) {
326  scoped_refptr<EasyUnlockPrivateUnwrapSecureMessageFunction> function(
327      new EasyUnlockPrivateUnwrapSecureMessageFunction());
328  function->set_has_callback(true);
329
330  chromeos::EasyUnlockClient::UnwrapSecureMessageOptions unwrap_options;
331  unwrap_options.key = "KEY";
332  unwrap_options.encryption_type = easy_unlock::kEncryptionTypeNone;
333  unwrap_options.signature_type = easy_unlock::kSignatureTypeHMACSHA256;
334
335  std::string expected_result;
336  client_->UnwrapSecureMessage(
337      "MESSAGE",
338      unwrap_options,
339      base::Bind(&CopyData, &expected_result));
340  ASSERT_GT(expected_result.length(), 0u);
341
342  scoped_ptr<base::ListValue> args(new base::ListValue);
343  args->Append(StringToBinaryValue("MESSAGE"));
344  args->Append(StringToBinaryValue("KEY"));
345  base::DictionaryValue* options = new base::DictionaryValue();
346  args->Append(options);
347
348  ASSERT_TRUE(extension_function_test_utils::RunFunction(
349      function.get(),
350      args.Pass(),
351      browser(),
352      extension_function_test_utils::NONE));
353
354  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
355}
356
357TEST_F(EasyUnlockPrivateApiTest, UnwrapSecureMessage_AsymmetricSign) {
358  scoped_refptr<EasyUnlockPrivateUnwrapSecureMessageFunction> function(
359      new EasyUnlockPrivateUnwrapSecureMessageFunction());
360  function->set_has_callback(true);
361
362  chromeos::EasyUnlockClient::UnwrapSecureMessageOptions unwrap_options;
363  unwrap_options.key = "KEY";
364  unwrap_options.associated_data = "ASSOCIATED_DATA";
365  unwrap_options.encryption_type = easy_unlock::kEncryptionTypeNone;
366  unwrap_options.signature_type = easy_unlock::kSignatureTypeECDSAP256SHA256;
367
368  std::string expected_result;
369  client_->UnwrapSecureMessage(
370      "MESSAGE",
371      unwrap_options,
372      base::Bind(&CopyData, &expected_result));
373  ASSERT_GT(expected_result.length(), 0u);
374
375  scoped_ptr<base::ListValue> args(new base::ListValue);
376  args->Append(StringToBinaryValue("MESSAGE"));
377  args->Append(StringToBinaryValue("KEY"));
378  base::DictionaryValue* options = new base::DictionaryValue();
379  args->Append(options);
380  options->Set("associatedData",
381               StringToBinaryValue("ASSOCIATED_DATA"));
382  options->SetString(
383      "signType",
384      api::ToString(api::SIGNATURE_TYPE_ECDSA_P256_SHA256));
385
386  ASSERT_TRUE(extension_function_test_utils::RunFunction(
387      function.get(),
388      args.Pass(),
389      browser(),
390      extension_function_test_utils::NONE));
391
392  EXPECT_EQ(expected_result, GetSingleBinaryResultAsString(function.get()));
393}
394
395}  // namespace
396
397