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