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

Addressing client enumeration section with protocol changes #156

Merged
merged 1 commit into from
Mar 24, 2021

Conversation

kevinlewi
Copy link
Collaborator

  • Introduces an oprf_seed parameter which must be generated during server setup and persisted across all registration/login requests (similar to server_private_key)
  • Introduces a cred_identifier parameter -- this could be the same as client_identity, but does not need to be
  • Derives oprf_key from oprf_seed and cred_identifier -- this results in altering RegistrationResponse and CredentialResponse
  • Modifies the "Credential File" section to just store the RegistrationUpload object, while making clear that the oprf_seed and server_private_key need to be kept around too
  • In FinalizeRequest, adds a new element envelope_key which must be stored as part of a RegistrationUpload object
  • Reverts the changes described in Simplifying envelope construction #120 (PR Simplify envelope secret derivation #126), necessary in order for the decryption step involving envelope_key to work properly
  • Introduces an encryption_nonce parameter, and steps for encrypting the envelope using envelope_key, in CreateCredentialResponse
  • Adds a new FakeCredentialResponse function
  • Introduces a new "N" parameter, Nok for representing the size of an OPRF key. Removes unused calls to SerializeScalar and DeserializeScalar from the VOPRF draft
  • Removes the text corresponding to the "Client Enumeration" section

Closes #22.

@hugokraw
Copy link
Collaborator

hugokraw commented Mar 7, 2021

Hi Kevin, I can't say I did a thorough review but here are a few comments from a cursory reading. Nothing major.

  • the name envelope_key is too generic, there are too many envelope-related keys, how about enum_masking_key
  • till now, we only required the registration to go over authenticated channels, with the sending of the enum_masking_key we now need to explicitly assume authenticated and confidential channel (e.g., TLS)
  • In the derivation of prk, prk = HKDF-Extract("prk", Harden(y, params)), replace "prk" (acting as salt) with 0 (as in HKDF-Extract(salt=0, IKM)). There is no real value-add in using a different string. A salt of zero is the indication that instead of an extractor you are assuming a random oracle. Domain separators (as "prk" can be thought of) are not part of HKDF-Extract but of HKDF-Expand (there is good rationale behind that distinction)
  • The operations under FakeCredentialResponse need to be defined as closely to the "real case", particularly to avoid identifying the fake executions via side channels, particularly timing attacks. So instead of a random() in encrypted_response = random(Npk + Nsk + Nh + 33), l you may want to specify that the server generates encrypted_response as a fresh encryption of 0's using some dummy encryption key (even this can result in some timing difference if the encryption key is fixed leading to some optimizations).
  • Rationale for the enumeration-prevention measures. While you have now eliminated the text that proposed (and asked feedback for) these measures, some of this text needs to stay to provide rationale (otherwise this whole thing looks "suspicious). Moreover, the issue in the previous bullet (timing attacks) needs to be explained and implementations for which enumeration is a real threat need to take measures to do an as accurate as possible simulation of the real case.

@kevinlewi
Copy link
Collaborator Author

kevinlewi commented Mar 8, 2021

See my most recent comment on #22 (comment) -- I believe we should not make the changes I proposed to the protocol after all.

@kevinlewi
Copy link
Collaborator Author

kevinlewi commented Mar 9, 2021

Nevermind about the previous comment for not making the changes -- after some clarification in #22 where we discussed the oprf_key doesn't need to be rotated upon re-registration of a user, I believe we can continue with these proposed changes.

Addressed @hugokraw's comments:

  • renamed envelope_key to masking_key
  • clarified that registration now needs an authenticated and confidential channel
  • changed prk derivation to use salt=0 instead of salt="prk"
  • mirrored the fake credential response function after the real one to prevent timing attacks
  • added a bunch of text into the Client Enumeration section (text copied below) -- @hugokraw please take a look at this when you get a chance!

Preventing Client Enumeration

Client enumeration refers to attacks where the attacker tries to learn
extra information about the behavior of clients that have registered with
the server. There are two types of attacks we consider:

  1. An attacker tries to learn whether a given client identity is registered
    with a server, and
  2. An attacker tries to learn whether a given client identity has recently
    completed registration, or has re-registered (e.g. after a password change).

Preventing the first type of attack requires the server to act with
unregistered client identities in a way that is indistinguishable from its
behavior with existing registered clients. This is achieved in
{{create-credential-response}} for an unregistered client by simulating a
CredentialResponse for unregistered clients through the sampling of a
random masking_key value and relying on the semantic security provided by
the XOR-based pad over the envelope.

Implementations must employ care to avoid side-channel leakage (e.g.,
timing attacks) from helping differentiate these operations from a regular
server response.

Preventing the second type of attack requires the server to supply a
cred_identifier value for a given client identity, consistently between the
{{create-reg-response}} and {{create-credential-response}} steps.
Note that cred_identifier can be set to client_identity, for simplicity.

In the event of a server compromise that results in a re-registration of
credentials for all compromised clients, the oprf_seed value must be resampled,
resulting in a change in the oprf_key value for each client. Although this
change can be detected by an adversary, it is only leaked upon password rotation
after the exposure of the credential files.

@hugokraw
Copy link
Collaborator

hugokraw commented Mar 9, 2021

The essential difference between the two attacks is that to address the first one the protocol needs not change. The defense against this attack is achieved via server-side implementations that do not impact the client side. The second attack does require a change in the form of the masking of the envelope. However, it is important to note (and this addresses some of @kevinlewi concerns), that once we include the masking technique, the server is free to apply or not apply the defenses we describe. For example, an application where user enumeration is not an issue can ignore the deterministic derivation of oprf_key from the user's identifier and could generate a fresh independent oprf_key with each registration. You may want to comment on this as the need for deterministic derivation and rotation of oprf_seed may be seen as added complexity for those that don't need such defense. They would still be doing the envelope masking since it is now part of the protocol and not just an optional element. The important thing is that the deterministic derivation with oprf_seed is only needed to defend against attack 2 but has no other security implication.

@hugokraw
Copy link
Collaborator

hugokraw commented Mar 9, 2021

why is confidentiality required?

I assume you are asking about the need for a confidential channel during registration. This is because the (secret) masking_key needs to be sent from client to server (before, we only needed an authenticated channel). In most cases, if you have an authenticated channel (e.g. TLS) then you have a confidential one too (in particular, you can always upgrade authenticated to confidential by sending a public key over the authenticated channel)

@hugokraw
Copy link
Collaborator

hugokraw commented Mar 9, 2021

One more comment on the user enumeration defenses:
I assume that given that you want to minimize options and security decisions by implementers/applications, you will not want to do the following. But "for the record", one could specify OPAQUE without talking about enumeration attacks and use of oprf_seed and instead use oprf_key as values chosen with each registration. Then, you would have a section dedicated to Defenses against User Enumeration attacks in which you would say that applications that are concerned with such attacks, can address them via the FakeCredentialResponse mechanism that you would specify specifically for this case.
(Note that envelope masking will always be implemented hence leaving the defense against user enumeration as a server-side implementation choice.)

@kevinlewi
Copy link
Collaborator Author

@hugokraw makes sense. Yes, as you guessed, I think we are leaning towards reducing the number of security decisions that applications would need to make.

Are you saying that you would like to have text added to the current doc that specifically clarifies the flexibility of the server in choosing or not choosing to use the "FakeCredentialResponse" mechanism? I believe that this is already inherent/implied, since a server can simply return "FAIL" rather than bother with running FakeCredentialResponse, and so my vote is to not add this text since it is unnecessary. But, happy to add something if you feel like we should add the clarification.

@hugokraw
Copy link
Collaborator

It may be worth having language saying that other than envelope masking, which is part of the basic protocol and a must-to-have for interoperability, the rest of FakeCredentialResponse (such as the use of oprf_seed) becomes a pure server-side implementation decision.

@hugokraw
Copy link
Collaborator

@chris-wood Could the IETF/CFRG impose on the server to implement FakeCredentialResponse for standard-compliance even though it is not needed for interoperability? In principle, such compliance could be tested by simulating a user enumeration attacker. But I doubt it is something IETF would mandate. What do you think?

Copy link
Collaborator

@chris-wood chris-wood left a comment

Choose a reason for hiding this comment

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

@chris-wood Could the IETF/CFRG impose on the server to implement FakeCredentialResponse for standard-compliance even though it is not needed for interoperability? In principle, such compliance could be tested by simulating a user enumeration attacker. But I doubt it is something IETF would mandate. What do you think?

Well, we can certainly say that servers MUST implement the protocol this way to achieve the desired security properties. (This is akin, in my view, to saying that servers MUST choose ephemeral TLS key shares for connections, but some may choose to reuse them.) Whether or not implementations follow the requirement is up to them. :-)

draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
@kevinlewi
Copy link
Collaborator Author

Rebased, added DeriveKeyPair reference, and added the following optional text to the end of the Client Enumeration section:

Finally, note that server implementations may choose to forego the construction
of a simulated credential response message for an unregistered client if these client
enumeration attacks can be mitigated through other application-specific means.

draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
- SerializedElement: A serialized OPRF group element, a byte array of fixed
length.
- SerializedScalar: A serialized OPRF scalar, a byte array of fixed length.
- Nok: The size of an OPRF private key

- Deterministic Key Pair Generation Function:
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is part of the OPRF API, right? Why not move it up?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, it is. Can you clarify what you mean by "move it up"? It is already under the OPRF section -- do you mean to move it up in front of Blind(x), Evaluate(k, M), etc?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep! If it's an OPRF API, it should be in the same list as Blind() etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess what I am saying is, it is already in the same list as Blind() etc... unless I am misinterpreting what you are saying?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's in a separate list, like the KDF and other functions below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OH I see what you mean -- I thought you were referring to the Nok parameter as needing to move up. But now I realize that you are referring to DeriveKeyPair being needed to move up. Got it! :) The comment line numbers this points to was a bit ambiguous :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, yep :) Sorry, I should have been more clear.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should be resolved in the latest diff.

## Setup Phase {#setup-phase}

In a setup phase, C chooses its password, and S chooses its own pair of private-public
AKE keys (server_private_key, server_public_key) for use with the AKE, along with a Nh-byte oprf_seed. S can use
the same pair of keys with multiple clients. These steps can happen offline, i.e.,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it required that the same seed be used across all users?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It does not have to be one seed for all. For example, there could be a seed for all userid's that start with letter a, one for those that start with letter b, etc. But a userid should always use the same oprf seed. A change of seed can only happen at re-registration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@chris-wood: How should we allow for the flexibility in this API, in terms of producing test vectors, etc. ? Does it suffice to just add a note here saying that multiple oprf_seeds can be used?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, I think noting that multiple seeds may be used is sufficient, but the test vectors should just use one seed for all users.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a side note that multiple seeds can be used

draft-irtf-cfrg-opaque.md Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
@kevinlewi
Copy link
Collaborator Author

Addressed above comments. Also changed the use of "CredentialResponsePad" literal string to use encryption_nonce instead.

@kevinlewi
Copy link
Collaborator Author

Changed names:

  • encryption_nonce -> masking_nonce
  • encrypted_response -> masked_response

Copy link
Collaborator

@chris-wood chris-wood left a comment

Choose a reason for hiding this comment

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

LGTM modulo some final nits!

draft-irtf-cfrg-opaque.md Show resolved Hide resolved
draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
@@ -638,21 +647,22 @@ Output:
Steps:
1. y = Finalize(password, blind, response.data)
2. envelope_nonce = random(32)
3. prk = Extract(envelope_nonce, Harden(y, params))
3. prk = Extract(salt=0, Harden(y, params))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
3. prk = Extract(salt=0, Harden(y, params))
3. prk = Extract("", Harden(y, params))

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Cool, also changed the salt=0 instance to "" in the "OPAQUE-3DH Key Schedule" section

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, it is better to set the salt to 0. It does not have to be described as salt=0 but just 0 (instead of ""). In principle, any fixed salt value can do, but I prefer to see 0 being used as the default "fixed value". In particular, 0 is used in TLS 1.3 key derivation when a random value for salt is not available and here we want to stay as close as possible to TLS 1.3 key derivation.

Copy link
Collaborator

@chris-wood chris-wood Mar 24, 2021

Choose a reason for hiding this comment

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

For what it's worth, "" is the notation used in TLS for zero-length strings, which is what we want here, I think.

Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the meaning of 0 in the following TLS 1.3 figure. Is it the value 0 or a 0-length string?
0
|
v
PSK -> HKDF-Extract = Early Secret

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I see the answer: "0" indicates a string of Hash.length bytes set to zero.

We should specify the same here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's a zero-length string (or NULL, or the equivalent of providing no salt at all), but I was referring to "" used elsewhere where context strings.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually I see the answer: "0" indicates a string of Hash.length bytes set to zero.

I'm not sure this is best, since the KDF might not be HKDF (and therefore have an implied hash function underneath). I think nil/""/0/whatever are equivalent, and "" seems to be most clear (to me) and consistent with 8446.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point. If we are treating the KDF as a general Extract-then-Expand primitive then we cannot say "a string of Hash.length bytes set to zero" but we also cannot say the empty string as it may not make sense in some other KDF. If you want to be generic, then you should refer to this salt value as a "constant" (as opposed to a random one), maybe call it const_salt and set it to "a string of Hash.length bytes set to zero" in the case of HKDF.

draft-irtf-cfrg-opaque.md Outdated Show resolved Hide resolved
@chris-wood chris-wood merged commit a9c566d into cfrg:master Mar 24, 2021
@nategraf nategraf mentioned this pull request Oct 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lift user enumeration text to the OPAQUE AKE stage
4 participants