1 // Copyright 2018 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/base64.h"
6 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
7 #include "chrome/browser/sync/test/integration/encryption_helper.h"
8 #include "chrome/browser/sync/test/integration/passwords_helper.h"
9 #include "chrome/browser/sync/test/integration/sync_test.h"
10 #include "components/password_manager/core/browser/password_form.h"
11 #include "components/sync/base/passphrase_enums.h"
12 #include "components/sync/driver/profile_sync_service.h"
13 #include "components/sync/engine/sync_engine_switches.h"
14 #include "components/sync/nigori/cryptographer_impl.h"
15 #include "components/sync/nigori/nigori.h"
16 #include "components/sync/nigori/nigori_test_utils.h"
17 #include "components/sync/test/fake_server/fake_server_nigori_helper.h"
18 #include "content/public/test/browser_test.h"
19 #include "content/public/test/test_launcher.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 
22 namespace {
23 
24 using bookmarks_helper::AddURL;
25 using bookmarks_helper::BookmarksTitleChecker;
26 using bookmarks_helper::CreateBookmarkServerEntity;
27 using bookmarks_helper::ServerBookmarksEqualityChecker;
28 using fake_server::FakeServer;
29 using fake_server::GetServerNigori;
30 using fake_server::SetNigoriInFakeServer;
31 using sync_pb::EncryptedData;
32 using sync_pb::NigoriSpecifics;
33 using sync_pb::SyncEntity;
34 using syncer::CreateCustomPassphraseNigori;
35 using syncer::Cryptographer;
36 using syncer::GetEncryptedBookmarkEntitySpecifics;
37 using syncer::InitCustomPassphraseCryptographerFromNigori;
38 using syncer::KeyDerivationParams;
39 using syncer::KeyParamsForTesting;
40 using syncer::LoopbackServerEntity;
41 using syncer::ModelType;
42 using syncer::ModelTypeSet;
43 using syncer::PassphraseType;
44 using syncer::ProtoPassphraseInt32ToEnum;
45 using syncer::SyncService;
46 using testing::ElementsAre;
47 using testing::SizeIs;
48 
49 // Intercepts all bookmark entity names as committed to the FakeServer.
50 class CommittedBookmarkEntityNameObserver : public FakeServer::Observer {
51  public:
CommittedBookmarkEntityNameObserver(FakeServer * fake_server)52   explicit CommittedBookmarkEntityNameObserver(FakeServer* fake_server)
53       : fake_server_(fake_server) {
54     fake_server->AddObserver(this);
55   }
56 
~CommittedBookmarkEntityNameObserver()57   ~CommittedBookmarkEntityNameObserver() override {
58     fake_server_->RemoveObserver(this);
59   }
60 
OnCommit(const std::string & committer_invalidator_client_id,ModelTypeSet committed_model_types)61   void OnCommit(const std::string& committer_invalidator_client_id,
62                 ModelTypeSet committed_model_types) override {
63     sync_pb::ClientToServerMessage message;
64     fake_server_->GetLastCommitMessage(&message);
65     for (const sync_pb::SyncEntity& entity : message.commit().entries()) {
66       if (syncer::GetModelTypeFromSpecifics(entity.specifics()) ==
67           syncer::BOOKMARKS) {
68         committed_names_.insert(entity.name());
69       }
70     }
71   }
72 
GetCommittedEntityNames() const73   const std::set<std::string> GetCommittedEntityNames() const {
74     return committed_names_;
75   }
76 
77  private:
78   FakeServer* const fake_server_;
79   std::set<std::string> committed_names_;
80 };
81 
82 // These tests use a gray-box testing approach to verify that the data committed
83 // to the server is encrypted properly, and that properly-encrypted data from
84 // the server is successfully decrypted by the client. They also verify that the
85 // key derivation methods are set, read and handled properly. They do not,
86 // however, directly ensure that two clients syncing through the same account
87 // will be able to access each others' data in the presence of a custom
88 // passphrase. For this, a separate two-client test is be used.
89 class SingleClientCustomPassphraseSyncTest : public SyncTest {
90  public:
SingleClientCustomPassphraseSyncTest()91   SingleClientCustomPassphraseSyncTest() : SyncTest(SINGLE_CLIENT) {}
~SingleClientCustomPassphraseSyncTest()92   ~SingleClientCustomPassphraseSyncTest() override {}
93 
94   // Waits until the given set of bookmarks appears on the server, encrypted
95   // according to the server-side Nigori and with the given passphrase.
WaitForEncryptedServerBookmarks(const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark> & expected_bookmarks,const std::string & passphrase)96   bool WaitForEncryptedServerBookmarks(
97       const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
98           expected_bookmarks,
99       const std::string& passphrase) {
100     auto cryptographer = CreateCryptographerFromServerNigori(passphrase);
101     return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
102                                           expected_bookmarks,
103                                           cryptographer.get())
104         .Wait();
105   }
106 
107   // Waits until the given set of bookmarks appears on the server, encrypted
108   // with the precise KeyParamsForTesting given.
WaitForEncryptedServerBookmarks(const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark> & expected_bookmarks,const KeyParamsForTesting & key_params)109   bool WaitForEncryptedServerBookmarks(
110       const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
111           expected_bookmarks,
112       const KeyParamsForTesting& key_params) {
113     auto cryptographer = syncer::CryptographerImpl::FromSingleKeyForTesting(
114         key_params.password, key_params.derivation_params);
115     return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
116                                           expected_bookmarks,
117                                           cryptographer.get())
118         .Wait();
119   }
120 
WaitForUnencryptedServerBookmarks(const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark> & expected_bookmarks)121   bool WaitForUnencryptedServerBookmarks(
122       const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark>&
123           expected_bookmarks) {
124     return ServerBookmarksEqualityChecker(GetSyncService(), GetFakeServer(),
125                                           expected_bookmarks,
126                                           /*cryptographer=*/nullptr)
127         .Wait();
128   }
129 
WaitForNigori(PassphraseType expected_passphrase_type)130   bool WaitForNigori(PassphraseType expected_passphrase_type) {
131     return ServerNigoriChecker(GetSyncService(), GetFakeServer(),
132                                expected_passphrase_type)
133         .Wait();
134   }
135 
WaitForPassphraseRequiredState(bool desired_state)136   bool WaitForPassphraseRequiredState(bool desired_state) {
137     return PassphraseRequiredStateChecker(GetSyncService(), desired_state)
138         .Wait();
139   }
140 
WaitForClientBookmarkWithTitle(std::string title)141   bool WaitForClientBookmarkWithTitle(std::string title) {
142     return BookmarksTitleChecker(/*profile_index=*/0, title,
143                                  /*expected_count=*/1)
144         .Wait();
145   }
146 
GetSyncService()147   syncer::ProfileSyncService* GetSyncService() {
148     return SyncTest::GetSyncService(0);
149   }
150 
151   // When the cryptographer is initialized with a passphrase, it uses the key
152   // derivation method and other parameters from the server-side Nigori. Thus,
153   // checking that the server-side Nigori contains the desired key derivation
154   // method and checking that the server-side encrypted bookmarks can be
155   // decrypted using a cryptographer initialized with this function is
156   // sufficient to determine that a given key derivation method is being
157   // correctly used for encryption.
CreateCryptographerFromServerNigori(const std::string & passphrase)158   std::unique_ptr<Cryptographer> CreateCryptographerFromServerNigori(
159       const std::string& passphrase) {
160     NigoriSpecifics nigori;
161     EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
162     EXPECT_EQ(ProtoPassphraseInt32ToEnum(nigori.passphrase_type()),
163               PassphraseType::kCustomPassphrase);
164     return InitCustomPassphraseCryptographerFromNigori(nigori, passphrase);
165   }
166 
InjectEncryptedServerBookmark(const std::string & title,const GURL & url,const KeyParamsForTesting & key_params)167   void InjectEncryptedServerBookmark(const std::string& title,
168                                      const GURL& url,
169                                      const KeyParamsForTesting& key_params) {
170     std::unique_ptr<LoopbackServerEntity> server_entity =
171         CreateBookmarkServerEntity(title, url);
172     server_entity->SetSpecifics(GetEncryptedBookmarkEntitySpecifics(
173         server_entity->GetSpecifics().bookmark(), key_params));
174     GetFakeServer()->InjectEntity(std::move(server_entity));
175   }
176 
177  private:
178   DISALLOW_COPY_AND_ASSIGN(SingleClientCustomPassphraseSyncTest);
179 };
180 
181 class SingleClientCustomPassphraseDoNotUseScryptSyncTest
182     : public SingleClientCustomPassphraseSyncTest {
183  public:
SingleClientCustomPassphraseDoNotUseScryptSyncTest()184   SingleClientCustomPassphraseDoNotUseScryptSyncTest()
185       : features_(/*force_disabled=*/false, /*use_for_new_passphrases=*/false) {
186   }
187 
188  private:
189   ScopedScryptFeatureToggler features_;
190 };
191 
192 class SingleClientCustomPassphraseUseScryptSyncTest
193     : public SingleClientCustomPassphraseSyncTest {
194  public:
SingleClientCustomPassphraseUseScryptSyncTest()195   SingleClientCustomPassphraseUseScryptSyncTest()
196       : features_(/*force_disabled=*/false, /*use_for_new_passphrases=*/true) {}
197 
198  private:
199   ScopedScryptFeatureToggler features_;
200 };
201 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,CommitsEncryptedData)202 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
203                        CommitsEncryptedData) {
204   const std::string title1 = "Hello world";
205   const std::string title2 = "Bookmark #2";
206   const GURL page_url1("https://google.com/");
207   const GURL page_url2("https://example.com/");
208 
209   SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
210   ASSERT_TRUE(SetupSync());
211 
212   ASSERT_TRUE(AddURL(/*profile=*/0, title1, page_url1));
213   ASSERT_TRUE(AddURL(/*profile=*/0, title2, page_url2));
214   ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase));
215 
216   EXPECT_TRUE(WaitForEncryptedServerBookmarks(
217       {{title1, page_url1}, {title2, page_url2}},
218       /*passphrase=*/"hunter2"));
219 }
220 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,CanDecryptPbkdf2KeyEncryptedData)221 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
222                        CanDecryptPbkdf2KeyEncryptedData) {
223   KeyParamsForTesting key_params = {KeyDerivationParams::CreateForPbkdf2(),
224                                     "hunter2"};
225   InjectEncryptedServerBookmark("PBKDF2-encrypted bookmark",
226                                 GURL("http://example.com/doesnt-matter"),
227                                 key_params);
228   SetNigoriInFakeServer(CreateCustomPassphraseNigori(key_params),
229                         GetFakeServer());
230   SetDecryptionPassphraseForClient(/*index=*/0, "hunter2");
231   ASSERT_TRUE(SetupSync());
232   EXPECT_TRUE(WaitForPassphraseRequiredState(/*desired_state=*/false));
233 
234   EXPECT_TRUE(WaitForClientBookmarkWithTitle("PBKDF2-encrypted bookmark"));
235 }
236 
237 // Populates custom passphrase Nigori without keystore keys to the client.
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,PRE_CanDecryptWithKeystoreKeys)238 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
239                        PRE_CanDecryptWithKeystoreKeys) {
240   const KeyParamsForTesting key_params = {
241       KeyDerivationParams::CreateForPbkdf2(), "hunter2"};
242   SetNigoriInFakeServer(CreateCustomPassphraseNigori(key_params),
243                         GetFakeServer());
244   SetDecryptionPassphraseForClient(/*index=*/0, key_params.password);
245   ASSERT_TRUE(SetupSync());
246 }
247 
248 // Client should be able to decrypt with keystore keys, regardless whether they
249 // were stored in NigoriSpecifics. It's not a normal state, when the server
250 // stores some data encrypted with keystore keys, but client is able to
251 // reencrypt the data and recover from this state.
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,CanDecryptWithKeystoreKeys)252 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseSyncTest,
253                        CanDecryptWithKeystoreKeys) {
254   const password_manager::PasswordForm password_form =
255       passwords_helper::CreateTestPasswordForm(0);
256   passwords_helper::InjectKeystoreEncryptedServerPassword(password_form,
257                                                           GetFakeServer());
258   ASSERT_TRUE(SetupClients());
259   EXPECT_TRUE(
260       PasswordFormsChecker(/*index=*/0, /*expected_forms=*/{password_form})
261           .Wait());
262 }
263 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,CommitsEncryptedDataUsingPbkdf2WhenScryptDisabled)264 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,
265                        CommitsEncryptedDataUsingPbkdf2WhenScryptDisabled) {
266   const std::string title = "PBKDF2 encrypted";
267   const GURL page_url("https://google.com/pbkdf2-encrypted");
268 
269   SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
270   ASSERT_TRUE(SetupSync());
271   ASSERT_TRUE(AddURL(/*profile=*/0, title, page_url));
272 
273   ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase));
274   NigoriSpecifics nigori;
275   EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
276   EXPECT_EQ(nigori.custom_passphrase_key_derivation_method(),
277             sync_pb::NigoriSpecifics::PBKDF2_HMAC_SHA1_1003);
278   EXPECT_TRUE(WaitForEncryptedServerBookmarks({{title, page_url}},
279                                               /*passphrase=*/"hunter2"));
280 }
281 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseUseScryptSyncTest,CommitsEncryptedDataUsingScryptWhenScryptEnabled)282 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseUseScryptSyncTest,
283                        CommitsEncryptedDataUsingScryptWhenScryptEnabled) {
284   const std::string title = "scrypt encrypted";
285   const GURL page_url("https://google.com/scrypt-encrypted");
286 
287   SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
288   ASSERT_TRUE(SetupSync());
289 
290   ASSERT_TRUE(AddURL(/*profile=*/0, title, page_url));
291 
292   ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase));
293   NigoriSpecifics nigori;
294   EXPECT_TRUE(GetServerNigori(GetFakeServer(), &nigori));
295   EXPECT_EQ(nigori.custom_passphrase_key_derivation_method(),
296             sync_pb::NigoriSpecifics::SCRYPT_8192_8_11);
297   EXPECT_TRUE(WaitForEncryptedServerBookmarks({{title, page_url}},
298                                               /*passphrase=*/"hunter2"));
299 }
300 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,CanDecryptScryptKeyEncryptedDataWhenScryptNotDisabled)301 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,
302                        CanDecryptScryptKeyEncryptedDataWhenScryptNotDisabled) {
303   KeyParamsForTesting key_params = {
304       KeyDerivationParams::CreateForScrypt("someConstantSalt"), "hunter2"};
305   InjectEncryptedServerBookmark("scypt-encrypted bookmark",
306                                 GURL("http://example.com/doesnt-matter"),
307                                 key_params);
308   SetNigoriInFakeServer(CreateCustomPassphraseNigori(key_params),
309                         GetFakeServer());
310   SetDecryptionPassphraseForClient(/*index=*/0, "hunter2");
311 
312   ASSERT_TRUE(SetupSync());
313   EXPECT_TRUE(WaitForPassphraseRequiredState(/*desired_state=*/false));
314 
315   EXPECT_TRUE(WaitForClientBookmarkWithTitle("scypt-encrypted bookmark"));
316 }
317 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,DoesNotLeakUnencryptedData)318 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,
319                        DoesNotLeakUnencryptedData) {
320   const std::string title = "Should be encrypted";
321   const GURL page_url("https://google.com/encrypted");
322   SetEncryptionPassphraseForClient(/*index=*/0, "hunter2");
323   ASSERT_TRUE(SetupClients());
324 
325   // Create local bookmarks before sync is enabled.
326   ASSERT_TRUE(AddURL(/*profile=*/0, title, page_url));
327 
328   CommittedBookmarkEntityNameObserver observer(GetFakeServer());
329   ASSERT_TRUE(SetupSync());
330 
331   ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase));
332   // If WaitForEncryptedServerBookmarks() succeeds, that means that a
333   // cryptographer initialized with only the key params was able to decrypt the
334   // data, so the data must be encrypted using a passphrase-derived key (and not
335   // e.g. a keystore key), because that cryptographer has never seen the
336   // server-side Nigori. Furthermore, if a bookmark commit has happened only
337   // once, we are certain that no bookmarks other than those we've verified to
338   // be encrypted have been committed.
339   EXPECT_TRUE(WaitForEncryptedServerBookmarks(
340       {{title, page_url}},
341       {KeyDerivationParams::CreateForPbkdf2(), "hunter2"}));
342   EXPECT_THAT(observer.GetCommittedEntityNames(), ElementsAre("encrypted"));
343 }
344 
IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,ReencryptsDataWhenPassphraseIsSet)345 IN_PROC_BROWSER_TEST_F(SingleClientCustomPassphraseDoNotUseScryptSyncTest,
346                        ReencryptsDataWhenPassphraseIsSet) {
347   const std::string title = "Re-encryption is great";
348   const GURL page_url("https://google.com/re-encrypted");
349   ASSERT_TRUE(SetupSync());
350   ASSERT_TRUE(WaitForNigori(PassphraseType::kKeystorePassphrase));
351   ASSERT_TRUE(AddURL(/*profile=*/0, title, page_url));
352   const std::vector<ServerBookmarksEqualityChecker::ExpectedBookmark> expected =
353       {{title, page_url}};
354   ASSERT_TRUE(WaitForUnencryptedServerBookmarks(expected));
355 
356   GetSyncService()->GetUserSettings()->SetEncryptionPassphrase("hunter2");
357   ASSERT_TRUE(WaitForNigori(PassphraseType::kCustomPassphrase));
358 
359   // If WaitForEncryptedServerBookmarks() succeeds, that means that a
360   // cryptographer initialized with only the key params was able to decrypt the
361   // data, so the data must be encrypted using a passphrase-derived key (and not
362   // e.g. the previous keystore key which was stored in the Nigori keybag),
363   // because that cryptographer has never seen the server-side Nigori.
364   EXPECT_TRUE(WaitForEncryptedServerBookmarks(
365       expected, {KeyDerivationParams::CreateForPbkdf2(), "hunter2"}));
366 }
367 
368 }  // namespace
369