cfrg / draft-irtf-cfrg-opaque Goto Github PK
View Code? Open in Web Editor NEWThe OPAQUE Asymmetric PAKE Protocol
Home Page: https://cfrg.github.io/draft-irtf-cfrg-opaque/draft-irtf-cfrg-opaque.html
License: Other
The OPAQUE Asymmetric PAKE Protocol
Home Page: https://cfrg.github.io/draft-irtf-cfrg-opaque/draft-irtf-cfrg-opaque.html
License: Other
The following changes the HMQV key derivation to include the session identifier (essentially what the value info is) under the key calculation (the changes are in the last 4 lines below)
Values IKM and info are defined for each protocol:
For HMQV:
info = "HMQV keys" | nonceU | nonceS | IdU | IdS
IKM = Khmqv
where Khmqv is computed:
by the client: Khmqv = (ePubS * PubS^s)^{ePrivU + u*PrivU}
by the server: Khmqv = (ePubU * PubU^u)^{ePrivS + s*PrivS}
and u = H(ePubU | "user" | info) and s = H(ePubS | "srvr" | info).
Also, in subsection "Instantiation of OPAQUE with HMQV and 3DH", before the KEY DERIVATION heading, add the following note:
[[TODO: Specify that in the login phase, ephemeral DH values need to be
verified to belong to the correct group (via membership tests or cofactor
exponentiation). Same for public keys during the registration phase.
Details of verification depend on the particular group/curve. (What's done in
TLS 1.3?)]]
TLS notation would be easiest, given its ubiquity.
Page 4, second paragraph: “OKI-free” -> “PKI-free”
In that same paragraph "it works for RSA" should be replaced with a better explanation. The point is that SRP requires a ring: it mixes additions and multiplications. It does not work over plain elliptic curves.
Page 8, section 2.1. DH-OPEF -> DH-OPRF
This key is used for applications that choose to process user information beyond the OPAQUE functionality (e.g., additional secrets or credentials).
This is more of an issue with the OPRF draft, but noting it here for now. The OPRF context constructor does not allow creation of a server with an existing key, which makes the OPRF credential response generation function somewhat annoying to implement.
TLS 1.3 is the SIGMA-I instantiation, so I think we should drop this from the OPAQUE document. It's redundant, and will likely cause confusion between people who want to choose between one or the other.
cc @kevinlewi
These would omit the AKE integration pieces, since the wire format for that's still unclear.
ProtocolMessage
defines the length
field to be of type uint24
, i understand this is to handle messages that are composed of a few max 2^16 long parts, and possibly to conserve bandwidth? is it possible to make this 32bit which is a native and widely used size instead of 24bits.
Clients need a way to recover credential information during registration. So we should either (a) fix some Harden parameters, or (b) have servers send these during the registration and authentication flows.
@kevinlewi, what do you think?
The nonce used in envelope encryption should not be derived from RwdU but rather chosen freshly at random at the time of encryption (envelope creation) and be stored (and sent) as part of the envelope. This avoids reusing the same encryption stream for encrypting two different plaintexts.
This basically specifies different configurations, where each configuration identifies the OPRF, RKR-AEAD, and MHF algorithm. For example, one configuration might be DH-OPRF with OPRF-curve448-HKDF-SHA512-ELL2-RO, CTR+HMAC with AES-256 and HMAC-SHA256, and scrypt (with reasonable parameters).
It's currently set 32 bytes, which may be too much (?) in some circumstances. However, adding yet another parameter that applications must choose is not great.
Currently, the 3DH key schedule computes two MAC and two encryption keys, as follows:
Km2 = HKDF-Expand-Label(handshake_secret, "client mac", "", Hash.length)
Km3 = HKDF-Expand-Label(handshake_secret, "server mac", "", Hash.length)
Ke2 = HKDF-Expand-Label(handshake_secret, "client enc", "", key_length)
Ke3 = HKDF-Expand-Label(handshake_secret, "server enc", "", key_length)
Km2
is the key used to compute the server's MAC in KE2
:
KE2 = credential_response, nonceS, info2, epkS, Einfo2, MAC(Km2; transcript2),
Using the label "client mac"
to derive Km2
seems backwards. Let's swap them around, like so:
Km2 = HKDF-Expand-Label(handshake_secret, "server mac", "", Hash.length)
Km3 = HKDF-Expand-Label(handshake_secret, "client mac", "", Hash.length)
Ke2 = HKDF-Expand-Label(handshake_secret, "server enc", "", key_length)
Ke3 = HKDF-Expand-Label(handshake_secret, "client enc", "", key_length)
cc @hugokraw, @kevinlewi
since github does not support reviewing markdown documents i'm submitting this as an issue:
The OPRF primitive defines a
Evaluate(k, M): Evaluate input M using private key k.
function, semantically i think it makes more sense to call this function Contribute as this conveys better what is happening, Evaluate() is less specific to the purpose of what is happening here.
Currently, SHA-512 is used for all OPRF suites, which is quite excessive. (This is more an issue against the OPRF draft, I'm but filing it here to track the change in our doc.)
We currently rely on a GenerateScalar function from the OPRF, which might not be a thing for OPRFs that are not based on Diffie-Hellman. We should consider how we might generalize this here, or perhaps in the OPRF document itself.
Who, when and how determines the length of elements to be generated or received. For example, how does the client know what the value of Nk should be. More significant how does the client know Nk or len(pt) when receiving the envelope?
On a related note, for a derivation like
SK, Km2, Km3, Ke2, Ke3 = HKDF(salt=0, IKM, info, L)
we need to assume that the client knows how to parse the key stream correctly and uniquely hence it needs to know the lengths of each key. This is also needed even if we compute a separate HKDF-Expand for each value (since Expand requires to define a length value). How does the client know these values, are these determined by the specific algorithms or ciphersuite ? Should this information be part of the information authenticated by the envelope or some other mechanism?
How does TLS 1.3 deals with this, namely, how it communicates and authenticates the ciphersuite/algorithms/version?
The DST might include the "RFCXXXX" identifier, similar to what we did for HPKE.
Should we clarify how much trust we place in the server? For example, since OPAQUE stores encrypted keys in the server, and that same server has the OPRF secret key, it could in theory do a dictionary attack and recover the private key for each user. This is pretty nonsensical, but I think it does highlight that OPAQUE assumes the server is honest-but-curious, rather than entirely malicious.
This is sort of fundamental, but some folks might miss it.
The output of Evaluate
in the OPRF draft is an Evaluation
object, which has both a blinded and signed element, as well as a proof. We should either adopt that type here, or make it clear in our notation that the output of Evaluate
is just the blinded and signed element.
Note that this affects what's carried in a RequestMetadata
struct.
The idU parameter is not used by the server when receiving the RegistrationRequest
and CredentialRequest
messages from the client. Can this parameter be omitted from the specification of these messages?
In theory, the server does need some "username" equivalent in order to look up the client's record, so this information does need to be sent, but I don't think it should be included in the OPAQUE messages, since it has no function within OPAQUE other than to set/retrieve the appropriate password file.
Presumably, any wrapper protocol which uses OPAQUE will not want to look into the bytes of the OPAQUE messages in order to figure out what the username is.
In line
export_key = HKDF-Expand(RwdU, concat(nonce, "ExportKey", nonce), Nk)
nonce appears twice
The "cost" details are written assuming a traditional DH-based KE protocol. Should we try to generalize this, e.g., to cover KEM-based protocols? Moreover, the cost details might not age well as TLS 1.3 develops and possibly moves away from signatures, via, e.g., semi-static DH. Or we might just remove this section entirely.
Future implementers may wish to replace HKDF-Expand and HMAC with their own (read:faster?) variants, so we should make clear the rationale for using these. Per the analysis, we require random-key robustness from HMAC, which is not something all MACs provide. (GMAC and poly1305 don't, for example.) HKDF-Expand could possibly be replaced by a different variable-length output PRF.
This might include, for example, what sort of OPRF ciphersuite(s) are used. Or we might just say that this is decided out of band.
Why is it necessary to put pkS in the CredentialResponse struct?
According to this, pkS must be included within the envelope already. So, when the client parses the CredentialResponse struct, shouldn't it be able to obtain pkS from the envelope already?
The configurations are rather loosely defined. A registry that allows one to encode an entire configuration (AKE, OPRF, MHF, etc) could be useful.
Add a note: pkU is often not needed by the client to run the key exchange stage in which case it can be omitted from the envelope and not sent from server to client in the online stage. In particular, this is the case for the three instantiations here: 3DH, HMQV and SIGMA.
Just as we dropped the ProtocolMessages part, for the protocol it might be not necessary to call the function StoreUserRecord. For instance my implementation does not care about any kind of persistence or transport of the outcome of the protocol. It might be that the use-case does not need the storage of the user record, for example by just keeping it in memory, or the record might be further processed before storage. Thus i propose to rename the function to FinalizeUserRecord
instead.
This will shorten the introduction and help readers get straight to the protocol details. HPKE does a good job with this sort of structure, so we might look there as a guide.
According to this, idU is used to construct FinalizeRequest, despite it only being applicable if the application decides to include it in the Credentials struct: > "Applications may optionally include
pkU
,idU
, oridS
in theCredentials.cleartext_credentials
structure, or in
Credentials.secret_credentials
if secrecy of these values is desired."
But if this is the case, then why not also make idS and pkU parameters to FinalizeRequest as well?
Same goes for RecoverCredentials. Either they should take in all of these optional parameters as inputs, or they should not include any of them.
From the definition of Ke2 and Ke3, the encryption keys used to encrypt the optional Einfo parameters in the AKE section, it seems like the application would need to pick an encryption algorithm for OPAQUE to use here.
Can we avoid this selection and simply use an XOR-based one-time pad for encryption here (is this secure)? Thus, instead of setting key_length to be a fixed constant, it would simply be the length of Einfo2 and Einfo3.
Specify that in the login phase, ephemeral DH values need to be verified to belong to the correct group (via membership tests or cofactor exponentiation). Same holds for public keys during the registration phase. Details of verification depend on the particular group/curve.
We might use PKCS#8 as an encoding for credential private keys, or we might use language that suggests this is specific to each instantiation.
The key K3dh needs to computed by the server as
K3dh = ePubU^ePrivS | ePubU^PrivS | PubU^ePrivS
The current draft inverts the order between the last two elements making it inconsistent with the way the client computes the key.
(Reported by Kevin Lewi)
We should reference and rely on [1] when it's updated.
[1] https://tools.ietf.org/html/draft-sullivan-tls-opaque-00
in this section https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md#registration-messages
struct {
opaque data<0..2^16-1>;
opaque pkS<0..2^16-1>;
CredentialType secret_types<1..255>;
CredentialType cleartext_types<0..255>;
} RegistrationResponse;
Specifies
- secret_credentials_list, a list of CredentialType values clients should include in the secret_credentials list of their Credentials structure
- cleartext_credentials_list, a list of CredentialType values clients should include in the cleartext_credentials list of their Credentials structure
what if a CredentialType is in both of these lists? Why not pack this information in 10 bits, where each CredentialType has 2 bits for signalling:
NotPackaged = 0, // field is not packaged in envelope
InSecEnv = 1, // field is encrypted
InClrEnv = 2 // field is plaintext, but authenticated
We might consider standardizing the mechanism by which servers prevent user enumeration attacks.
Should this line (https://github.com/cfrg/draft-irtf-cfrg-opaque/blame/master/draft-irtf-cfrg-opaque.md#L583) be similar to this line (https://github.com/cfrg/draft-irtf-cfrg-opaque/blame/master/draft-irtf-cfrg-opaque.md#L787)?
Either both should read Envelope envelope;
, or both should read opaque envelope<1..2^16-1>
, right?
In this section on AKE, we should clear up exactly which parameters are required and which are optional. Then we can also work towards defining a wire format for these AKEs if necessary.
A couple of concrete questions:
in the source there's a commented out part ## Envelope considerations
which mentions that not even skU
needs to be included in the credentials wrapped in the envelope, instead skU can be derived from rwd
. there's a note that this part should be "brought back after updating". if this is indeed the case, do not forget to change
Applications MUST include skU in secret_credentials
in the {#protocol-messages} section.
in the section https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md#authenticated-key-exchange-messages CredentialRequest is defined as:
struct {
opaque data<1..2^16-1>;
} CredentialRequest;
and right below that is this line:
data : An encoded element in the OPRF group. See {{I-D.irtf-cfrg-voprf}} for a description of this encoding.
and in the https://github.com/cfrg/draft-irtf-cfrg-opaque/blob/master/draft-irtf-cfrg-opaque.md#createcredentialresponserequest section it is even referred to as:
1. (kU, envU, pkU) = LookupUserRecord(request.id)
even though trivial, it might make sense to include id in the CredentialRequest structure definition.
At the end of the #protocol-messages
section is a description of the envelope structure as seen here:
OPAQUE uses an `Envelope` structure to encapsulate an encrypted `Credentials` structure.
It is encoded as follows.
~~~
struct {
opaque nonce[32];
opaque ct<1..2^16-1>;
opaque auth_data<0..2^16-1>;
opaque auth_tag<1..2^16-1>;
} Envelope;
~~~
nonce
: A unique 32-byte nonce used to protect this Envelope.
ct
: Encoding of encrypted and authenticated credential extensions list.
auth_data
: Encoding of an authenticated credential extensions list.
auth_tag
: Authentication tag protecting the contents of the envelope.
as an implementer of opaque (see here https://github.com/stef/libsphinx/blob/master/src/opaque.c) i wonder how to parse this structure when both fields might be of variable length. In such a case it is useful to have the lengths included in the auth_data section at known offsets of the envelope. hence i propose to move the auth_data section infront of the ct section, either at the very beginning before the nonce, or between the nonce and the ct, in both cases an instantiation of the protocol can decide to include the lengths at known offsets and will be able to parse the envelope, even if this means that these lengths are used before they are authenticated.
Some fields, like the user public key, might only require authentication.
We need to add the rationale for KdKey somewhere. Can use the following as a basis:
The main use case I have in mind is retrieving a non-OPAQUE encrypted credential. Namely, expanding the use of OPAQUE with a credential-retrieval functionality. You could always put additional stuff in the envelope but I would prefer to restrict the envelope with its OPAQUE-specific functionality. In addition, having a separate encrypted credential can achieve several properties: allowing the server to send it only after the user authenticated; send it under the session key with forward secrecy; allowing the addition of a credential to the user's record after registration; allowing the use of non-RKR encryption for credential encryption; and just giving better extensibility, flexibility and separation from the core OPAQUE protocol.
in the Cryptographic primitives section there is this sentence:
We also assume the existence of a function KeyGen, which generates an OPRF private and public key.
i'm sorry, but what does that mean? The notion of a keypair in the OPRF context makes no sense to me.
Instead of KEYS = HKDF(salt=0, IKM=k, info="EnvU", Length=L1+L2+L3) one could do
KEYS = HKDF-Expand(key=k, info="EnvU", Length=L1+L2+L3)
which makes more sense since k is already a pseudo random key, we would only need to specify that k is the length of an HMAC key.
Endpoints MUST agree on these identities (and use the right values!) for security.
Presumably we want to use the VOPRF with modeBase set (as opposed to modeVerifiable). But I could not find this mentioned anywhere in the spec in its current state
in the specification there is a part which defines {#protocol-messages}
:
enum {
registration_request(1),
registration_response(2),
registration_upload(3),
credential_request(4),
credential_response(5),
(255)
} ProtocolMessageType;
struct {
ProtocolMessageType msg_type; /* protocol message type */
uint24 length; /* remaining bytes in message */
select (ProtocolMessage.msg_type) {
case registration_request: RegistrationRequest;
case registration_response: RegistrationResponse;
case registration_upload: RegistrationUpload;
case credential_request: CredentialRequest;
case credential_response: CredentialResponse;
};
} ProtocolMessage;
in our implementation at https://github.com/stef/libopaque we omitted implementing this, for the following reasons:
libopaque does however implement the credential and envelope structures.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.