Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC3265: Login and SSSS with a Single Password #3265

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions proposals/3265-login-and-ssss-with-single-password.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Login and SSSS with a Single Password

The current approach for secure server-side storage (SSSS) requires that users
create and remember two different passwords for each account. Unfortunately,
humans are not very good at creating and remembering even one strong password
per account. Requiring the use of two passwords increases the risk that users
will choose at least one weak password, or that they will re-use the same
password for both purposes.

The case where a user uses a single password for both login and SSSS is
particularly dangerous. Even though the homeserver should not store the
password in long-term storage, it does learn the password whenever a user
logs in or authenticates through the user-interactive authentication API.
At that point, a malicious or compromised homeserver could easily decrypt
all of the lazy user's server-side storage.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
all of the lazy user's server-side storage.
all of the user's server-side storage.


This proposal describes a new scheme that clients can use to better protect
their users' secrets. It does not require any changes on the server side.

The general idea is to use hash functions with the user's human-memorable
("raw") password to generate two different secrets. We use the first secret
as the new login password, and we use the second secret as the symmetric
encryption/decryption key for SSSS.

## Security Requirements

The scheme should satisfy the following requirements:

1. Given the login password, it should be computationally infeasible to
recover the user's original "raw" password.

2. Given the login password, it should be computationally infeasible to
recover the user's SSSS key.

3. Given no special information about the user's "raw" password, it should
be computationally impractical for an adversary to recover either the login
password or the SSSS key. This requirement applies even to adversaries
who can build or buy specialized hardware, including GPUs, FPGAs, and
ASICs in order to accelerate a brute-force search for the password.

## High-Level Description of the Scheme

The proposed construction is very simple. It relies on two cryptographic hash
functions.

1. `H()` - a standard cryptographic hash function, such as SHA256.

2. `PHF()` - a password hashing function, such as bcrypt, scrypt, argon2, or
(if necessary) PBKDF2.

First we pass the user's "raw" password through the password hashing function
to generate a "root" secret known only to the client. We use a salt derived
from the user's ID to thwart precomputation attacks using rainbow tables or
other time-memory tradeoffs.

```
salt = H(user_part)
root_secret = PHF(raw_password, salt)
```

This protects against adversaries that want to recover the raw password.
The PHF parameters should be chosen such that brute force password guessing
attacks are not practical. For example, the beta version of Circles uses
Bcrypt with work factor 14. With this setting, computing a single hash is
Comment on lines +63 to +64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the client know which parameters to use? How would an upgrade be handled?

I see that in another thread you said:

The upgrade path is relatively straightforward if your users only ever use a single app to access their account. At some point in time, the app upgrades everyone's passwords, and after that point it never falls back to sending raw passwords ever again.

Can you explain how this happens? How will the app know if a particular user has been upgraded or not? What if the user doesn't log in on the flag day?

very fast -- probably under 500ms on mid-range Apple hardware like the
iPhone X, iPhone XR, and the 2018 iPad. However, a brute-force search for
a non-trivial password would be extremely expensive.

Second, we hash the root secret with the hash function, using two different
prefixes to produce two hash digests, very similar to the way a chain key is
hashed in the double ratchet to produce a message key and a new chain key.
This gives us the two seemingly independent secrets that we require.

We use the first derived secret to create the user's new login password, by
encoding it as an ASCII/UTF8 hex string and taking the first 32 characters.

We take the second derived secret as the symmetric key for SSSS.

```
login_password = H('LoginPassword'|root_secret)[:16].utf8()
ssss_key = H('S4Key'|root_secret)
```

## Security Analysis

An adversary who can temporarily take over the homeserver can observe the
login password submitted in requests for password login or user-interactive
authentication. Our security requirements above state that this adversary
must not be able to learn the SSSS key or the user's human-memorable "raw"
password.

### Protecting the SSSS key
If the attacker can recover the root secret, then it can easily compute the
SSSS key. The proposed construction protects against this line of attack.
If the hash function is preimage resistant, then the login password gives
the attacker no real information about the root secret.

### Protecting the user's "raw" password
As noted above, the preimage resistance property of the hash function
prevents the attacker working backwards to obtain the root secret.
Furthermore, even if the attacker did know the root secret, the password
hashing function should also offer preimage resistance. Therefore the
attacker cannot invert the password hash to recover the password.

The attacker's best strategy then is to attempt a brute-force search for
the password, using the latest password dictionaries and guessing rules,
plus possibly specialized hardware to accelerate the computation of the
hash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the best strategy be to ask the user to log in with an old version of a matrix client? Those send the password in plain text to the server, so it is trivial to steal the users login "raw" password that way for a server admin and as such compromise e2ee security. I'm not sure, if there is a way to solve that...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the best strategy be to ask the user to log in with an old version of a matrix client?

Oh, that is sneaky. 😈

The easiest target for the attack would be users of a web app, like Element Web. The server simply serves them the old version of the app that sends raw passwords. But then again, if the server is serving up arbitrary code every time a user logs in, I'm not sure there's much hope against this adversary in the first place.

If the users are on a mobile app, then it may be very difficult to get an old version of the software, even if they want to.

So then the most interesting targets are (1) developers, who may run lots of different clients, but who (hopefully) make up a small fraction of the users, and (2) users of desktop clients, who have more freedom over which version they install and run.

The upgrade path is relatively straightforward if your users only ever use a single app to access their account. At some point in time, the app upgrades everyone's passwords, and after that point it never falls back to sending raw passwords ever again. (This is the path I took with Circles. It doesn't even know how to log in with raw passwords. At some point, this will be re-added as an "advanced" option for compatibility with existing accounts.)

If any sort of backwards compatibility is required, where it's ambiguous whether an account is using this proposal or it's using raw passwords, then this will be very tricky indeed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case that I was mostly thinking about, was people pulling old versions of clients from Ubuntu repos or so. By default you currently get a 3 year old version on Ubuntu 20.04, iirc. I don't think this is really solveable. Maybe this can be solved by only allowing the password login first and in a year or so also allow using this for SSSS unlock. Maybe you can add a suggestion of how such a migration could work or why this is not necessarily a concern. One could maybe force users to not use old clients, by disallowing logins with old clients, by adding an extra flag, in which case this would not be backwards compatible after some period and people may learn to not use old clients.

At least this really needs control over the server to abuse this, but on the other hand... that's what E2EE should protect you against. :D

If the user uses a reasonable password (ie, not in the password dictionaries,
and not a simple derivation from a word that is), and the password hashing
function resists acceleration, then the brute force guessing attack is also
not practically feasible.

Regardless of the user's choice of password, the proposed scheme is no
weaker than the current approach, where the user has separate passwords
for login and for SSSS. Under the current approach, the adversary who
temporarily compromises the homeserver learns the login password immediately,
and must brute-force search for the SSSS password. A clever adversary would
prioritize use of the login password in its brute-force search for the SSSS
password, since many users might re-use their login password, or a closely
related password, for SSSS.