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

[WIP] MSC2957: Cryptographically Concealed Credentials #2957

Draft
wants to merge 9 commits into
base: old_master
Choose a base branch
from
315 changes: 315 additions & 0 deletions proposals/2957-cryptographically-concealed-credentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
# MSC2957: Cryptographically Concealed Credentials

When logging in to Matrix using the `POST /login` endpoint, the client
transmits the password to the server, so the server is able to see the user's
password. It is generally expected that the server would handle the password
securely, for example, only storing the password in a salted and hashed
format. However a malicious server could store the user's password. If a user
re-uses their password on other services, the server administrator could then
use the stored password to log in to the user's other accounts. Also, this
means that the user's login password should not be the same as the password
that they use for [Secret
Storage](https://matrix.org/docs/spec/client_server/unstable#storage).

This proposal defines a way for users to authenticate without sending their
password to the server. Additional goals of this proposal include:

- relatively simple to implement
- the data stored on the server cannot be used (apart from attacking the
underlying cryptographic operations) to authenticate with the server
- the user can verify that the server that they are authenticating against is
Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds like a step backwards of that other MSC to have decentrel identities, a that proposes to have your account exist on multiple servers.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think that this conflicts with decentralized identities. Even with decentralized identities, there is some point at which you have to tell a server that you want to use it. This would be analogous to the "registration" part, and then with subsequent logins, you can verify that you're connecting to that same server.

the same as the server where they originally created their account
- a malicious user who unsuccessfully tries to authenticate against the server
does not gain any information about the user's password. For example, an
attacker cannot take any of the server's responses to perform any offline
computations or guesses to try to obtain the user's password.


## Proposal

### Protocol overview

#### Notation

- A Curve25519 key pair is denoted <K<sub>priv</sub>,K<sub>pub</sub>>, where
K<sub>priv</sub> is the private key and K<sub>pub</sub> is the public key.
- ECDH(A<sub>priv</sub>,B<sub>pub</sub>): the elliptic curve Diffie-Hellman of
the private key A<sub>priv</sub> and the public key B<sub>pub</sub>.
- HKDF(K, S, I, L): HKDF-SHA-256 where K is the initial key material, S is the
salt, I is the info, and L is the number of bits to generate.

#### Registration
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you give a less-mathematically cookbook on these (Registration & Login) flows? Like, what is the core idea that is keeping this secure?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can try to do so, though there isn't a single core idea that is keeping this secure, since different aspects provide different parts of the security. If you want a "main" part, it would probably be turning the password into a Curve25519 key pair (the authentication key), which allows the server to verify that you have the password without needing access to the password itself. Using a Curve25519 key also allows us to do more complicated operations (like combining the ephemeral keys), which provide other security properties.

But yes, there is certainly room for making this less "mathematically cookbook".

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could easily list a couple of higher level goals that this MSC tries to solve, for example:

  • Avoid sending passwords to the server
  • Establish an emoji fingerprint of the server when registering which can be used to identify it when we login again.

And then perhaps how each of these goals is achieved. For example the first stages on how we avoid sending the password.

  • The password is expanded into a Curve25519 keypair using PBKDF2
  • The public key and the PBKDF2 parameters are transfered to the server using a shared secret that was established using ephemeral ECDH.

This could also live besides the math at the start of each such step.

Choose a reason for hiding this comment

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

Agreed with the earlier comments here, that a more high-level explanation would help others to digest this. For example, I eventually noticed that the proposal repeatedly uses DH key exchange followed by AES encryption to construct a secure channel within the channel. Giving the reader a heads-up that this is coming (1) makes it easier to understand what's going on when they get there and (2) gives you a place to explain why it's there and what it's doing.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have added some words, which hopefully makes things a bit clearer.

Choose a reason for hiding this comment

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

Here's kind of a crazy suggestion, so feel free to take it or leave it.

But, reading over the 2nd Bellovin and Merritt EKE paper Augmented Encrypted Key Exchange, there's a lot of similarity between that classic paper and the approach proposed here. On the one hand, duh, they're both trying to do the same thing. But I think it's deeper than that -- the two "feel" much more similar to each other than either one is to, say, SRP, right? (And fortunately all the EKE patents expired years ago, so I don't think you have anything to worry about there.)

Here's the crazy part: Are they actually close enough that you could structure your protocol as a fix/update/modernization for AEKE? Then you might be able to borrow a big portion of whatever security analysis has already been done for AEKE. If you could frame it as a series of deltas, and you could show that each delta makes the new protocol strictly better than the old one, then that could save a ton of work. (And remove a ton of uncertainty.)

Anyway, like I said this is kind of crazy. I'm not an expert in either protocol, so maybe I'm seeing more than is really there.


When registering, the client generates an ephemeral Curve25519 key pair <C<sub>priv</sub>,C<sub>pub</sub>>, and
sends to the server:

- the user's desired Matrix ID
- C<sub>pub</sub>

The server generates its own ephemeral Curve25519 key pair
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we doing something about receiving a Curve25519 key from Malory that lies at the point of infinity like we had to for SAS verification? If so could we mention that, if there's no need for it could we mention that as well?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, we're binding the generated keys to the public keys by including them in the HKDF info parameter. I guess I can throw something in the MITM section.

<S<sub>priv</sub>,S<sub>pub</sub>>, and sends the S<sub>pub</sub> to the user.

The client calculates K<sub>1</sub> = ECDH(C<sub>priv</sub>,S<sub>pub</sub>)
and then calculates

- K<sub>AES</sub> = HKDF(K<sub>1</sub>, "", "`encryption key|`\<Matrix ID>`|`C<sub>pub</sub>`|`S<sub>pub</sub>", 256)
- K<sub>MAC</sub> = HKDF(K<sub>1</sub>, "", "`mac key|`\<Matrix ID>`|`C<sub>pub</sub>`|`S<sub>pub</sub>", 256)

where \<Matrix ID> is the user's desired Matrix ID.

The client then takes the user's password and generates the base key
K<sub>base</sub> using PBKDF2 (K<sub>base</sub> can be used as the Secret
Storage key). The client then calculates the Curve25519 private key
A<sub>priv</sub> = HKDF(K<sub>base</sub>, "", "`authentication key|`\<Matrix
ID>", 256), and the corresponding public key A<sub>pub</sub>. The client sends
to the server, encrypted with AES-256-CBC using K<sub>AES</sub> the AES key
generated above:

- A<sub>pub</sub>
- the PBKDF2 parameters used

The ciphertext is MACed using HMAC-SHA-256 using K<sub>MAC</sub> as the key.

The server stores A<sub>pub</sub> securely stores the result of
K<sub>conf</sub> = HKDF(ECDH(S<sub>priv</sub>, C<sub>pub</sub>) ||
ECDH(S<sub>priv</sub>, A<sub>pub</sub>), "", "`confirmation key|`\<Matrix
ID>`|`A<sub>pub</sub>`|`C<sub>pub</sub>`|`S<sub>pub</sub>", 16), called the
confirmation key.

The client calculates K<sub>conf</sub> = HKDF(ECDH(C<sub>priv</sub>,
S<sub>pub</sub>) || ECDH(A<sub>priv</sub>, S<sub>pub</sub>), "", "`confirmation
tag|`\<Matrix ID>`|`A<sub>pub</sub>`|`C<sub>pub</sub>`|`S<sub>pub</sub>", 16),
and calculates HKDF(K<sub>conf</sub>, "", "`security check|`\<Matrix
ID>`|`A<sub>priv</sub>", 3), giving a number between 0 and 7. The client then
displays the emoji (or the text equivalent) from the SAS verification emoji
Copy link
Member

@ara4n ara4n Jan 12, 2021

Choose a reason for hiding this comment

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

how useful really is this UX? given there's a 12.5% chance of the right emoji appearing later even if things have gone wrong, what does it buy us? am worried the additional UX complexity could confuse casual users even more than usual.

Copy link
Contributor

Choose a reason for hiding this comment

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

Since that is something people would likely write on a paper, it might be a good idea to also give the option of numbers, instead of emoji. While emoji are nice for comparing in device verification, numbers are significantly easier to write down.

Also, what if you don't write down the emoji / numbers and forget them? Realistically quite a few people will do just that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Since that is something people would likely write on a paper, it might be a good idea to also give the option of numbers, instead of emoji. While emoji are nice for comparing in device verification, numbers are significantly easier to write down.

Possibly. Or offer both the emoji version and the text equivalent. The idea behind using emoji was that it's something that's easier for people to recognize, as opposed to trying to remember a number. But this part is very much open for discussion.

Copy link

@3nprob 3nprob Mar 27, 2022

Choose a reason for hiding this comment

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

Isn't this purpose of protecting against impersonating homeservers already served by X.509 TLS certificates, which are already verified? I am not sure a user overriding an invalid cert would care about this, either. And if so, that sounds more like something that should be addressed in UX of clients, and not on this level.

Of course there is the angle of protecting against compromised CAs/roots, which is a nice improvement - but perhaps that is better adressed in how that level of PKI is handled?

Copy link
Member Author

Choose a reason for hiding this comment

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

In addition to protecting against compromised CAs/roots, or an attacker who is able to gain control of a valid cert somehow, it would also protect against attacks such as typosquatting.

list corresponding to that number. The user remembers/records the emoji for
later verification.

Resetting a user's password would happen similarly, with the addition that the
data in Secret Storage would need to be re-encrypted if the user is using the
same password for that.

#### Logging in

The client generates an ephemeral Curve25519 key pair
<C'<sub>priv</sub>,C'<sub>pub</sub>> and sends their Matrix ID and
C'<sub>pub</sub> to the server.

The server then generates its own ephemeral Curve25519 key pair
<S'<sub>priv</sub>,S'<sub>pub</sub>> and calculates K'<sub>AES</sub> = HKDF(ECDH(S'<sub>priv</sub>,
A<sub>pub</sub>) || ECDH(S'<sub>priv</sub>, C'<sub>pub</sub>), "", "`server
encryption|`\<Matrix ID>`|`A<sub>pub</sub>`|`C'<sub>pub</sub>`|`S'<sub>pub</sub>", 256).

- the PBKDF2 parameters to use,
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
- S'<sub>pub</sub>
- a random 32-byte nonce
- K<sub>conf</sub>, encrypted with AES-256-CBC using K'<sub>AES</sub>

The client performs PBKDF2 on the user's password using the given parameters to
generate the base key K<sub>base</sub>. It then calculates

- the Curve25519 private key A<sub>priv</sub> = HKDF(K<sub>base</sub>, "",
"`authentication key|`\<Matrix ID>", 256) and the corresponding public key
A<sub>pub</sub>
- K'<sub>AES</sub> = HKDF(ECDH(A<sub>priv</sub>, S'<sub>pub</sub>) ||
ECDH(C'<sub>priv</sub>, S'<sub>pub</sub>), "", "`server encryption|`\<Matrix
ID>`|`A<sub>pub</sub>`|`C'<sub>pub</sub>`|`S'<sub>pub</sub>", 256), and
decrypts K<sub>conf</sub> using K'<sub>AES</sub>.
- HKDF(K<sub>conf</sub>, "", "`security check|`\<Matrix ID>`|`A<sub>priv</sub>",
3)

The client displays the emoji (or text equivalent) from the SAS verification
emoji list corresponding to the number given by the last calculation, and

Choose a reason for hiding this comment

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

I agree with @ara4n's earlier comment on this aspect. Allowing the attacker a 1 in 8 chance of success is generally not sufficient.

Just to make sure I'm following this part correctly: The core problem here is that you need to verify the identity of the server, but you can't rely on (or don't have) the SSL/TLS layer certs, and you can't be sure that the client has anything stored locally. Like, maybe this is a new device. Right?

If so, then yeah, this is a hard problem with a well known set of unsatisfying solutions. The emoji thing is like a modern version of the ASCII art that SSH used to generate.

Some other ideas:

  • Could you get back to the easier case where you do have some sort of PKI? Like, maybe you get one cert for your TLS layer (ie Cloudflare/nginx/etc) and a totally different cert for the back end of this auth protocol. Then life is much easier, as long as you can trust your cert authority. All the client has to do is verify the cert.
  • What if you could have the client generate a sort of "cert" for the server at registration time? Maybe this is just a copy of the server's public key, signed or MAC'd with a key derived from the password that only the user knows.
    • Then when the user goes to log in, the server provides this "cert" in the auth params.
    • Before doing anything else, the client first verifies the signature or MAC, using the private/secret key derived from the password. If the verification fails, then the client terminates the connection.
    • In the protocol, the client uses this verified public key for the server. If there's an MITM who doesn't have the corresponding private key, they shouldn't be able to learn any of the secrets.

Copy link
Member Author

Choose a reason for hiding this comment

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

The emoji check is something that I'm not entirely committed to. It was just an interesting idea that I came up with, and wondered if it would be useful. But the emoji check does two things: 1) it gives the user a way to see if they typo'ed their password (with some reasonable-ish probability), and 2) sort-of authenticates the server. Thing 1 I would classify as nice-to-have, and thing 2 is very optional, given that the server gets fully authenticated later on.

The problem with the client-generated "cert" idea is that it allows an attacker to test different passwords to see if it is the right one by seeing which one yields a successful signature/MAC, which allows an an attacker to do a login attempt and then use the result to perform an offline attack. That's the reason why I only take 3 bits (8 emoji) is that it gives some help to the user without giving too much information to an attacker. Though that number may need to be fine tuned. Or we could just scrap it if it turns out to be a not-so-great idea.

Choose a reason for hiding this comment

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

Ok, that makes a lot of sense. If the emoji is mostly to verify the password, then some chance of failure is fine.

The problem with the client-generated "cert" idea is that it allows an attacker to test different passwords to see if it is the right one by seeing which one yields a successful signature/MAC

Yeah, any password-derived key has to come from a good password-based KDF, like bcrypt / scrypt / argon2 with properly chosen parameters. Otherwise it's wide open to attack.

allows the user to check that the emoji matches the one displayed when the user
registered their account. This allows the user to verify (to a degree of
confidence) that they entered their password correctly, and that they are
communicating with the same entity that they were communicating with when they
registered.

The client then calculates K<sub>2</sub> = ECDH(A<sub>priv</sub>,
S'<sub>pub</sub>) || ECDH(C'<sub>priv</sub>,S'<sub>pub</sub>) and
K'<sub>MAC</sub> = HKDF(K<sub>2</sub>, "", "`client MAC|`\<Matrix
ID>`|`A<sub>pub</sub>`|`C'<sub>pub</sub>`|`S'<sub>pub</sub>`|`K<sub>conf</sub>",
256), and sends an HMAC of the nonce using this key to the server.

The server calculates

- K<sub>2</sub> = ECDH(S'<sub>priv</sub>, A<sub>pub</sub>) ||
ECDH(S'<sub>priv</sub>,C'<sub>pub</sub>)
- K'<sub>MAC</sub> = HKDF(K<sub>2</sub>, "", "`client MAC|`\<Matrix
ID>`|`A<sub>pub</sub>`|`C'<sub>pub</sub>`|`S'<sub>pub</sub>`|`K<sub>conf</sub>",
256)
- K''<sub>MAC</sub> = HKDF(K<sub>2</sub>, "", "`server MAC|`\<Matrix
ID>`|`A<sub>pub</sub>`|`C'<sub>pub</sub>`|`S'<sub>pub</sub>`|`K<sub>conf</sub>",
256)

The server verifies the HMAC sent by the client, which proves to the server
that the client is in possession of the secret key A<sub>priv</sub>. The
server then responds with an HMAC of the nonce using K''<sub>MAC</sub>, which
the client can check to show that the server is in possession of the public key
A<sub>pub</sub>.

### Protocol details

TODO:

## Security characteristics

### Replay attacks

If an attacker tries to replay the client's messages to the server to
authenticate with it, the authentication will fail since authentication depends
on the randomly chosen ephemeral key and nonce which will be different for the
attacker's session.

### Data breach

If the server's database is leaked, this could reveal A<sub>pub</sub> and
K<sub>conf</sub>.

An attacker could try to brute-force the user's password by trying various
passwords and performing the PBKDF2 and HKDF operations to see if it yields a
Curve25519 private key that matches A<sub>pub</sub>. However, PBKDF2 will slow
down the attacker's attempts. This is no worse than the current practice of
storing a hashed version of the user's password. This can also be made more
secure by encrypting A<sub>pub</sub> using a key that is stored separately from
the database, similarly to how Synapse allows specifying a "pepper" value that
is used when hashing the user's password. In this way, an attacker who only
has the database, and not the additional encryption key, cannot retrieve
information about the user's password.

If the attacker obtains A<sub>pub</sub> and K<sub>conf</sub>, they can
impersonate the server to the user. Again, this can be partially mitigated by
encrypting K<sub>conf</sub> in addition to A<sub>pub</sub> using a key that is
stored separately from the database.

Since the server does not know the private key A<sub>priv</sub>, the attacker
cannot use the data stored on the server to authenticate as the given user
short of attacking Curve25519 to obtain A<sub>priv</sub> from A<sub>pub</sub>.

### Deniability

Even though the protocol allows the server to authenticate the user, it cannot
prove to a third party that the user authenticated with it, even if it produces
a full transcript of the authentication process. All information contained in
the client's requests is either known to the server or could be created by the
server, so the server cannot prove that the requests were created by the user
and not fabricated by the server.

### Offline computation

If an attacker initiates a login attempt, the server does not reveal any
information that would help the attacker determine the user's password. The
server only reveals: the PBKDF2 parameters to use (which should be viewed as
public information, and do not give any information about the password),
S'<sub>pub</sub> and a random 32-byte nonce (which are single-use and are not
related to the user's password), and an encrypted version of K<sub>conf</sub>.

If the attacker knows K<sub>conf</sub>, they could try to brute-force the
user's password until they can decrypt the encrypted version of
K<sub>conf</sub> to obtain the known value of K<sub>conf</sub>. However,
K<sub>conf</sub> is only 16 bits long, so there may be multiple passwords that
can be result in its correct decryption. Also, if the attacker knows
uhoreg marked this conversation as resolved.
Show resolved Hide resolved
K<sub>conf</sub>, they would most likely also know A<sub>pub</sub>, which would
already allow them to try to brute-force the user's password, so in this case,
the attacker does not gain any information by attempting to log in. Note that
in this case, it is important that the encryption of K<sub>conf</sub> is done
in an unauthenticated manner to ensure that an attacker is not given any
information about whether or not they have guessed the right decryption key.

It should be noted that an attacker who shoulder-surfs when the user registers
or logs in will be able to see the emoji security check displayed to the user.
The attacker may then use this to reduce the search space when trying
passwords. Since there are eight possible emoji, this reduces the search space
by a factor of 8, which can be compensated for by the user adding an extra
character to their password. As well, the attacker still needs to test each
password by submitting requests to the server, and so can be rate-limited by
the server. Since the emoji security check provides some feedback to the user
on whether they mistyped their password (a mistyped password would have a 7/8
chance of displaying the wrong emoji), servers can more aggressively rate-limit
login attempts when using this method. Clients could also make the emoji
security check optional so that users can disable it when they are in a
situation where shoulder-surfing is likely.

### Man-in-the-middle attacks

An attacker who is able to eavesdrop on the protocol messages could gain
information (for example, A<sub>pub</sub>, if they eavesdrop during the user's
registration) that would allow them to brute-force the user's password. Since
the sensitive data is encrypted using a key produced by an ECDH, it is not
enough for the attacker to be a passive eavesdropper; they would need to be an
active man-in-the-middle.

TODO: ...

### Phishing

TODO: ...

### ...

## Potential issues
Copy link
Member

Choose a reason for hiding this comment

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

how does password reset work?

Copy link
Member Author

Choose a reason for hiding this comment

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

It would be similar to registration. Though of course if you're using the same password for 4S, you'd also need to re-encrypt all your 4S secrets.

Copy link
Contributor

Choose a reason for hiding this comment

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

In order to be able to re-encrypt SSSS secrets you have to have them unencrypted, though. If you use "resent password" you typically don't have your password, and thus can't decrypt them. Meaning all your secret store is effectively lost.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. I was thinking password changing rather than password reset. For password changing, you can re-encrypt. For password reset, you'd have to re-create the SSSS secrets, cross-signing keys, etc.

Copy link
Member

Choose a reason for hiding this comment

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

what i was more getting at is: how do you recover your account if you forget your password? it feels like a recovery code could work - could we just give the user their private key derived from the password to store offline as a way to get back in if they forget their human password?


This proposal cannot be used by users who log in using SSO, or whose passwords
are managed by an external system.

...

## Alternatives
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe compare how this is better than just having the client hash the password w/ a salt (from the server) and sending the hash to the server, and the server treats the hash as a password and hashes the hash in its db?

Copy link
Member Author

Choose a reason for hiding this comment

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

If I'm understanding what you're suggesting, I think one main difference is that it would be open to replay attacks. i.e. if someone listens in on your login attempt, they'll see your hashed password, and they can then use that to log in as you.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, if I understand your proposal, it is somewhat similar to parts of SCRAM. In SCRAM, the client does PBKDF2 followed by HMAC to generate a ClientKey. The server stores a hash of ClientKey. This part sounds like it's the same as your proposal. But to avoid replay attacks, rather than sending the ClientKey to the server, the client uses the ClientKey to generate an HMAC of some messages, which the server is able to verify. (More specifically, the client calculates the HMAC using the hashed ClientKey as the key, and then XORs it with the ClientKey and sends that to the server. The server can calculate the same HMAC, since it has the hashed ClientKey, XORs the HMAC with what it received from the client to recover the ClientKey, and then hashes the recovered ClientKey to check that it matches the stored hashed ClientKey.)


TODO: compare with various PAKEs (e.g. SRP, OPAQUE)

Choose a reason for hiding this comment

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

Yes, it would really help to see some discussion here. How is the problem here different from the problem solved by PAKEs? And why is a PAKE not sufficient here? Otherwise it's tempting to say, just grab the current best PAKE (is that OPAQUE?) and run with it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Basically, the tl;dr for why I'm not suggesting a PAKE is that most PAKEs are complicated to implement (is there a good OPAQUE implementation for, say, JavaScript that's readily available?) and/or tied to specific math problems that will be hard to replace if they are shown to be vulnerable. Whereas the scheme presented here uses primitives that are used elsewhere in Matrix and can be easily replaced by equivalent primitives.

Choose a reason for hiding this comment

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

I was going to say, it's probably easier to implement whatever existing scheme than to design your own. Then I started reading OPAQUE... yikes.

SRP and SCRAM are much simpler and use primitives much closer to what you're using. They also have lots of implementations. What makes them not a good fit?

I'm not saying you can't build your own PAKE or PAKE-like thing, only that it's really hard. And if you screw up, you'll get the Telegram treatment where the "serious" infosec / crypto people decide to laugh at you instead of looking at your fixed version.

All that said, what I've seen so far looks pretty reasonable. I'll find some time later this week to do a full read-through and provide more in depth comments then.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think SRP still requires implementing low-level math stuff. But there's probably at least some decent implementations lying around.

SCRAM is pretty decent too, and could be used, though I'd probably want to make some tweaks to it to tighten things up a bit. The main issue with SCRAM is the one vulnerability that I have mentioned in the section where I talk about SCRAM. But aside from that, it should be usable.

I'm not saying you can't build your own PAKE or PAKE-like thing, only that it's really hard. And if you screw up, you'll get the Telegram treatment where the "serious" infosec / crypto people decide to laugh at you instead of looking at your fixed version.

Yeah, completely agreed. This is basically just an idea that I had in the middle of the night, and I believe that it's better than SCRAM, but I don't want to push this forward until it's been reviewed by other people who are knowledgeable in crypto.


### SCRAM

[SCRAM](https://tools.ietf.org/html/rfc5802) is another protocol that allows a
user to authenticate without the server receiving the user's password. It also
has the feature that the user is also able to authenticate the server since the
client proves that it has access to the `StoredKey`. One of its goals is that
the information stored on the server is not sufficient to impersonate a user.
However, if an attacker has access to the server's storage AND is able to
eavesdrop on an authentication (or impersonate the server), they can compute
the `ClientKey`, which is sufficient for the attacker to authenticate with the
server. This is noted in the "Security Considerations" section of RFC-5802:

> If an attacker obtains the authentication information from the authentication
> repository and either eavesdrops on one authentication exchange or
> impersonates a server, the attacker gains the ability to impersonate that
> user to all servers providing SCRAM access using the same hash function,
> password, iteration count, and salt. For this reason, it is important to use
> randomly generated salt values.

### Argon2

Argon2 would likely be a better option than PBKDF2 for generating a key from
Copy link
Contributor

Choose a reason for hiding this comment

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

Presumably AES-GCM wasn't considered for the same reason?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sort of. By now, there's probably enough implementations of AES-GCM that we could use it if we wanted to. But we're using AES-CBC here for a few reasons. We're already using CBC in olm and SSSS, and we don't use GCM anywhere else (yet -- /me looks at MLS). But another reason is that we do not want the encryption of Kconf (in the server's first response in the login stage) to be authenticated, as noted in the "Offline computation" section. So that encryption cannot use GCM, and it seems kind of silly to use two different encryption modes in the same protocol unless there's a strong reason that both are needed.

a password. However, this proposal uses PBKDF2 for compatibility with Secret
Storage, which used PBKDF2 due to the availability of implementations of both
algorithms. (For example, WebCrypto includes a PBKDF2 implementation, but not
an Argon2 implementation.) In the future, we may switch to Argon2 for both

Choose a reason for hiding this comment

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

FWIW there are some reports that bcrypt is stronger than Argon2 for interactive use, where the computation time is < 1000 ms. Note that this is coming from committee members of the Password Hashing Competition, who authored a competing system (Pufferfish).

https://twitter.com/TerahashCorp/status/1155129705034653698

https://www.reddit.com/r/crypto/comments/hvbgt7/alternatives_to_bcrypt/

authentication and Secret Storage.

## Security considerations

### Old clients

If a user tries to log into their account using a server that does not follow
this proposal may send the user's password to the server by trying to log in
using the current `POST /login` endpoint. This can be partially mitigated in
Copy link
Member

Choose a reason for hiding this comment

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

can we not have a different password for those using the legacy /login flow, separate to the 4S/3C one, much as we do today? So users on old clients (or who want to script their client without understanding this MSC ;) can just grab an AT using an insecure oldschool login - completely independently of whether the account also has 3C set up?

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is that old, existing clients will just POST /login the password people enter in them. So the only way this can be securely implemented is, if we update all old clients to not do that automatically, and then wait until all stable distros have that updated etc.

two ways.

1. Deployments in which the choice of clients is not well controlled should not
enable this feature until most of the commonly-used clients are updated to
support this feature. It is up to individual server administrators to
determine when to do this.

2. Users should be educated to expect to see and verify the emoji security
check before submitting the password. Old clients will not display the
emoji, providing a hint to users that the client does not support this
authentication method.

## Possible future work

TODO: combine 2FA?
Copy link
Contributor

Choose a reason for hiding this comment

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

If we have UIA on /login (#2835) (might be needed to properly implement this on protocol level anyways), and dynamic UIA (#2839) then 2FA could just be added as another UIA stage.

It feels to soru like 2FA would go beyond the scope of this MSC and would better live as its own MSC, which builds on #2835 and #2839

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah. This is more ildly wondering about whether there are any security benefits from integrating 2FA, versus tacking it on as separate thing. I don't think there are any benefits, but I'm not sure enough at the moment that I want to rule it out completely. So this is mainly a note to think about it.


## Unstable prefix

TODO: