1 // Copyright 2019 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 "components/password_manager/core/browser/leak_detection/leak_detection_request_utils.h"
6 
7 #include "base/test/mock_callback.h"
8 #include "base/test/task_environment.h"
9 #include "components/password_manager/core/browser/leak_detection/encryption_utils.h"
10 #include "components/password_manager/core/browser/leak_detection/single_lookup_response.h"
11 #include "crypto/sha2.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace password_manager {
16 namespace {
17 
18 using ::testing::ElementsAre;
19 using ::testing::Field;
20 
21 // Converts a string to an array for printing.
StringToArray(const std::string & s)22 std::vector<int> StringToArray(const std::string& s) {
23   return std::vector<int>(s.begin(), s.end());
24 }
25 
26 }  // namespace
27 
TEST(LeakDetectionRequestUtils,PrepareSingleLeakRequestData)28 TEST(LeakDetectionRequestUtils, PrepareSingleLeakRequestData) {
29   base::test::TaskEnvironment task_env;
30   base::MockCallback<SingleLeakRequestDataCallback> callback;
31 
32   PrepareSingleLeakRequestData("jonsnow", "1234", callback.Get());
33   EXPECT_CALL(
34       callback,
35       Run(AllOf(
36           Field(&LookupSingleLeakData::payload,
37                 AllOf(Field(&LookupSingleLeakPayload::username_hash_prefix,
38                             ElementsAre(0x3D, 0x70, 0xD3, 0x60)),
39                       Field(&LookupSingleLeakPayload::encrypted_payload,
40                             testing::Ne("")))),
41           Field(&LookupSingleLeakData::encryption_key, testing::Ne("")))));
42   task_env.RunUntilIdle();
43 }
44 
TEST(LeakDetectionRequestUtils,PrepareSingleLeakRequestDataWithKey)45 TEST(LeakDetectionRequestUtils, PrepareSingleLeakRequestDataWithKey) {
46   base::test::SingleThreadTaskEnvironment task_env;
47   auto task_runner = task_env.GetMainThreadTaskRunner();
48   base::CancelableTaskTracker task_tracker;
49   base::MockOnceCallback<void(LookupSingleLeakPayload)> callback;
50 
51   PrepareSingleLeakRequestData(task_tracker, *task_runner, "random_key",
52                                "jonsnow", "1234", callback.Get());
53   EXPECT_CALL(callback,
54               Run(AllOf(Field(&LookupSingleLeakPayload::username_hash_prefix,
55                               ElementsAre(0x3D, 0x70, 0xD3, 0x60)),
56                         Field(&LookupSingleLeakPayload::encrypted_payload,
57                               testing::Ne("")))));
58   task_env.RunUntilIdle();
59 }
60 
TEST(LeakDetectionRequestUtils,PrepareSingleLeakRequestDataCancelled)61 TEST(LeakDetectionRequestUtils, PrepareSingleLeakRequestDataCancelled) {
62   base::test::SingleThreadTaskEnvironment task_env;
63   auto task_runner = task_env.GetMainThreadTaskRunner();
64   base::MockOnceCallback<void(LookupSingleLeakPayload)> callback;
65   EXPECT_CALL(callback, Run).Times(0);
66 
67   {
68     base::CancelableTaskTracker task_tracker;
69     PrepareSingleLeakRequestData(task_tracker, *task_runner, "random_key",
70                                  "jonsnow", "1234", callback.Get());
71   }
72 
73   task_env.RunUntilIdle();
74 }
75 
TEST(LeakDetectionRequestUtils,AnalyzeResponseResult_DecryptionError)76 TEST(LeakDetectionRequestUtils, AnalyzeResponseResult_DecryptionError) {
77   base::test::TaskEnvironment task_env;
78 
79   // Force a decryption error by returning trash bytes.
80   auto response = std::make_unique<SingleLookupResponse>();
81   response->reencrypted_lookup_hash = "trash_bytes";
82 
83   base::MockCallback<SingleLeakResponseAnalysisCallback> callback;
84   AnalyzeResponse(std::move(response), "random_key", callback.Get());
85   EXPECT_CALL(callback, Run(AnalyzeResponseResult::kDecryptionError));
86   task_env.RunUntilIdle();
87 }
88 
TEST(LeakDetectionRequestUtils,AnalyzeResponseResult_NoLeak)89 TEST(LeakDetectionRequestUtils, AnalyzeResponseResult_NoLeak) {
90   base::test::TaskEnvironment task_env;
91 
92   constexpr char kUsernamePasswordHash[] = "abcdefg";
93   auto response = std::make_unique<SingleLookupResponse>();
94   std::string key_client;
95   std::string encrypted_username_password =
96       CipherEncrypt(kUsernamePasswordHash, &key_client);
97   std::string key_server;
98   response->reencrypted_lookup_hash =
99       CipherReEncrypt(encrypted_username_password, &key_server);
100   SCOPED_TRACE(testing::Message()
101                << "key_client="
102                << testing::PrintToString(StringToArray(key_client))
103                << ", key_server="
104                << testing::PrintToString(StringToArray(key_server)));
105 
106   response->encrypted_leak_match_prefixes.push_back(crypto::SHA256HashString(
107       CipherEncryptWithKey("unrelated_trash", key_server)));
108 
109   base::MockCallback<SingleLeakResponseAnalysisCallback> callback;
110   AnalyzeResponse(std::move(response), key_client, callback.Get());
111   EXPECT_CALL(callback, Run(AnalyzeResponseResult::kNotLeaked));
112   task_env.RunUntilIdle();
113 }
114 
TEST(LeakDetectionRequestUtils,AnalyzeResponseResult_Leak)115 TEST(LeakDetectionRequestUtils, AnalyzeResponseResult_Leak) {
116   base::test::TaskEnvironment task_env;
117 
118   constexpr char kUsernamePasswordHash[] = "abcdefg";
119   auto response = std::make_unique<SingleLookupResponse>();
120   std::string key_client;
121   std::string encrypted_username_password =
122       CipherEncrypt(kUsernamePasswordHash, &key_client);
123   std::string key_server;
124   response->reencrypted_lookup_hash =
125       CipherReEncrypt(encrypted_username_password, &key_server);
126   SCOPED_TRACE(testing::Message()
127                << "key_client="
128                << testing::PrintToString(StringToArray(key_client))
129                << ", key_server="
130                << testing::PrintToString(StringToArray(key_server)));
131 
132   // Random length of the prefix for values in |encrypted_leak_match_prefixes|.
133   // The server can pick any value.
134   constexpr int kPrefixLength = 30;
135   response->encrypted_leak_match_prefixes.push_back(crypto::SHA256HashString(
136       CipherEncryptWithKey("unrelated_trash", key_server)));
137   response->encrypted_leak_match_prefixes.push_back(
138       crypto::SHA256HashString(
139           CipherEncryptWithKey(kUsernamePasswordHash, key_server))
140           .substr(0, kPrefixLength));
141 
142   base::MockCallback<SingleLeakResponseAnalysisCallback> callback;
143 
144   AnalyzeResponse(std::move(response), key_client, callback.Get());
145   EXPECT_CALL(callback, Run(AnalyzeResponseResult::kLeaked));
146   task_env.RunUntilIdle();
147 }
148 
149 }  // namespace password_manager
150