New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.
Starred by 35 users

Issue metadata

Status: Started
EstimatedDays: ----
NextAction: ----
OS: Linux , Android , Windows , iOS , Chrome , Mac , Fuchsia
Pri: 2
Type: Feature

Blocked on:
issue 875901

Show other hotlists

Hotlists containing this issue:

Sign in to add a comment

Issue 820976: Security: Chrome Sync passphrase is far too easy to bruteforce

Reported by, Mar 12 2018

Issue description

Documentation states the following:

> With a passphrase, you can use Google's cloud to store and sync your Chrome data without letting Google read it.

That's not currently true. The encryption key for the data is derived from the passphrase using PBKDF2-HMAC-SHA1 using a fixed salt and merely 1003 iterations. This doesn't provide significant protection against bruteforcing the passphrase. Worse yet, if somebody manages to access encrypted data from multiple user accounts, the fixed salt allows bruteforcing all of them at the same time.

The sequence in code is the following:

* SyncEncryptionHandlerImpl::SetCustomPassphrase(passphrase),
* Cryptographer::AddKey({"localhost", "dummy", passphrase}),
* Nigori::InitByDerivation("localhost", "dummy", passphrase),

InitByDerivation() will derive a salt first. Given that the input is fixed however, that salt is a constant: Suser = PBKDF2("localhostdummy", "saltsalt", 1001, 8). It will then derive the encryption key as Kenc = PBKDF2(passphrase, Suser, 1003, 16). So in order to find any Google Sync accounts using "password1" as passphrase, an attacker would only need to derive an encryption key from that passphrase once and try to decrypt the data for any accounts it can access.

Even with a salt that isn't fixed but rather based on account ID for example, 1003 iterations are a way too low hurdle today. Judging by the numbers provided in, a single Nvidia GTX 1080 GPU would be able to calculate 3.2 million PBKDF2-HMAC-SHA1 hashes with 1003 iterations per second. That's enough to go through the enormous list of passwords under in less than 8 minutes. Even the passphrase of users who didn't reuse passwords and chose a passphrase with 40 bits of entropy (that's already stronger than most of them) would be guessed within two days.

I'd consider 256,000 PBKDF2-HMAC-SHA1 iterations or 100,000 PBKDF2-HMAC-SHA256 iterations the bare minimum that is still acceptable today. And each user should have their individual salt of course.

Comment 1 by, Mar 12 2018

Components: Security Services>Sync Privacy
Labels: -Type-Bug-Security -Restrict-View-SecurityTeam Type-Bug

Comment 2 by, Mar 15 2018

On an external post is also this note that probably bears fixing at the same time, but isn't a security issue:

"The function in question manages to run PBKDF2 four times where one run would have been sufficient. First run derives the salt from host name and username (both happen to be constants in case of Chrome Sync). This is pretty pointless: a salt doesn’t have to be a secret, it merely needs to be unique. So concatenating the values or running SHA-256 on them would do just as well. The next three runs derive three different keys from identical input, using different iteration counts. A single PBKDF2 call producing the data for all three keys clearly would have been a better idea."

Comment 3 by, Mar 20 2018


Comment 4 by, Mar 23 2018

tschumann@: Mind triaging this from the sync perspective?

battre@, FYI.

Comment 5 by, Mar 26 2018

Status: Assigned (was: Unconfirmed)

Comment 6 by, Apr 19 2018

Labels: M-69 OS-Android OS-Chrome OS-Fuchsia OS-iOS OS-Linux OS-Mac OS-Windows Pri-1
Status: Started (was: Assigned)
Just to kickstart the process, I put up a CL here: It probably won't work as-is, but hopefully we can do a sort of stone soup thing ( :)

Comment 7 by, Apr 30 2018


Comment 8 by, May 7 2018

Could you please use Argon2 or scrypt instead of PBKDF2?

Comment 9 by, May 7 2018

Thank you for your suggestion! We are already looking at using something else than PBKDF2, however, the outcome is not clear yet, because there are multiple factors at play.

Comment 10 by, Jun 7 2018

Ping: Any updates on this?

Comment 11 by, Jun 7 2018

There seem to be a consensus to use scrypt instead.
The parameter selection happened to be harder than I thought (mainly because of memory). I need to do some benchmarking and check with people knowing more about memory on Android.

Comment 12 by, Jun 20 2018

Labels: -Pri-1 Pri-2
Triage ping. Does this target M69? It seems too optimistic.

Comment 13 by, Jun 20 2018

It does target M69, the BP is on 19th of July.
I am meeting with Android Chrome folks tomorrow.

Comment 14 by, Jun 25 2018

Issue 686929 has been merged into this issue.

Comment 15 by, Aug 6

sync-triage ping: any updates?

Comment 16 by, Aug 8

Labels: -M-69 M-70
I've reached an agreement with the security team, I need to polish the design doc, get more approvals and start implementing.

I hope that the first implementation will be in M70.
However, we won't enable it in M70 due to backward incompatibility and will wait for at list 2 milestones.

Comment 17 by, Aug 17


Comment 18 by, Aug 20

Blockedon: 875901
The optimistic plan for the implementation is still M70.
The launch itself can be done in M71 or M72 (depending on how much backward incompatibility can we allow).

davidovic@ is starting the implementation.
Issue 875901 is a tracking bug (sorry, Google-internal only).

Comment 19 by, Sep 25


We have the code ready (scrypt) in M70, but it is currently completely disabled, because the testing wasn't finished yet.

Please note that the change is not backward compatible (the data encrypted with the scrypt derived key cannot be decrypted in <=M69, any (even a valid) passphrase will be rejected). In order to mitigate this, we will wait until M72 before enabling the new key derivation method. Then users on >=M70 will be able to decrypt data encrypted with scrypt derived key.

Comment 20 by, Sep 25

#19: Is it possible to use a "fallback" scheme? E.g.:

key = new_key_derivation(passphrase)
error = decrypt(key, data)
if (error) {
  key = old_key_derivation(passphrase)
  error = decrypt(key, data)
  if (error) {
    return error


Comment 21 by, Sep 25

#20: We are already doing that (via a different but equivalent mechanism). The problem is that we need, at some point, to flip the switch that will cause newly-set passphrases to use the new key derivation method. Once we do that, old versions of Chrome (<M70) will not be able to access this data, simply because they know nothing about the new method. They will blindly try using the old one, see that they're unable to decrypt the data, and surface an "Incorrect passphrase" error to the user. Short of backporting the scrypt code to <M70 (which is impossible), we can do nothing about that. The best we can do is delay flipping the switch until a majority of users are using >=M70, which is the first version that is able to handle key derivation using both new and old methods.

Comment 22 by, Sep 26

To #20: Your example handles "new versions of Chrome understanding old data" case, which we do indeed already support. 

The backward incompatibility example:
1) User updates to M72 on their desktop, but their phone is still M69.
2) We enable the new key derivation method in M72. 
3) User sets a new custom passphrase on their desktop.
4) User cannot decrypt the data on their phone unless they update. The message they will see is that the passphrase is wrong.

On the other hand, if user sets a custom passphrase from their phone (M69), everything will work as expected.

Comment 23 by, Sep 26


Comment 24 by, Oct 11

Labels: Hotlist-ConOps-CrOS

Comment 25 by, Oct 30


Comment 26 by, Jan 14

Labels: -Type-Bug Type-Feature

Comment 27 by, Jan 14

Is this still on track to ship in M72?

Comment 28 by, Jan 15

We consider punting to M73 and going through Canary/Dev/Beta first.
1) The test team was busy and M70 was tested only on iOS (M71 was tested on all platforms).
2) As of this week, the ratio of users with < M70 is larger than our original estimate.

Comment 29 by, Feb 6

We are aiming for M73.
I will gradually enable the decryption part for >=M71
and then enable encryption in Canary/Dev, then in Beta, then in Stable (after getting the metrics and all the approvals).

Sign in to add a comment