RFC 9807
The OPAQUE Augmented Password-Authenticated Key Exchange (aPAKE) Protocol
Title Block π
Okay bestie, paperwork first πβ¨ This is RFC 9807, published in July 2025 by the Internet Research Task Force (IRTF) π . It's called The OPAQUE Augmented Password-Authenticated Key Exchange (aPAKE) Protocol. It's Informational, so it isn't a Standards Track spec π¦ The ISSN is 2070-1721. The authors are D. Bourdrez, H. Krawczyk (AWS), K. Lewi (Meta), and C. A. Wood (Cloudflare, Inc.) π«Ά The title block doesn't list anything this one obsoletes or updates, so it's standing on its own, no drama πΈ
Abstract β¨
This doc describes OPAQUE, an Augmented (or Asymmetric) Password-Authenticated Key Exchange (aPAKE) protocol π It supports mutual authentication in a client-server setup, and it doesn't need PKI. It's also secure against pre-computation attacks if a server gets compromised π₯ On top of that, it gives you forward secrecy, and it can hide the password from the server, even during registration π This document specifies the core OPAQUE protocol and one instantiation based on 3DH. It's a product of the Crypto Forum Research Group (CFRG) in the IRTF π
Status of This Memo π
Not an Internet Standards Track spec. It's published for informational purposes only π
The IRTF publishes results of Internet-related research and development. Those results might not be suitable for deployment, so be careful π This RFC represents the consensus of the Crypto Forum Research Group of the IRTF. Documents approved for publication by the IRSG are not candidates for any level of Internet Standard. See Section 2 of RFC 7841 for the details π
For the current status, any errata, and how to send feedback, check https://www.rfc-editor.org/info/rfc9807 β¨
Copyright Notice Β©οΈ
Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved π
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, which you can find at https://trustee.ietf.org/license-info as of the publication date. Please actually read them. They explain your rights and restrictions for this document π§
Table of Contents ποΈ
1. Introduction
1.1. Requirements Notation
1.2. Notation
2. Cryptographic Dependencies
2.1. Oblivious Pseudorandom Function
2.2. Key Derivation Function and Message Authentication Code
2.3. Hash Functions
3. Protocol Overview
3.1. Setup
3.2. Registration
3.3. Online Authenticated Key Exchange
4. Client Credential Storage and Key Recovery
4.1. Key Recovery
4.1.1. Envelope Structure
4.1.2. Envelope Creation
4.1.3. Envelope Recovery
5. Registration
5.1. Registration Messages
5.2. Registration Functions
5.2.1. CreateRegistrationRequest
5.2.2. CreateRegistrationResponse
5.2.3. FinalizeRegistrationRequest
6. Online Authenticated Key Exchange
6.1. AKE Messages
6.2. AKE Functions
6.2.1. GenerateKE1
6.2.2. GenerateKE2
6.2.3. GenerateKE3
6.2.4. ServerFinish
6.3. Credential Retrieval
6.3.1. Credential Retrieval Messages
6.3.2. Credential Retrieval Functions
6.4. 3DH Protocol
6.4.1. 3DH Key Exchange Functions
6.4.2. Key Schedule Functions
6.4.3. 3DH Client Functions
6.4.4. 3DH Server Functions
7. Configurations
8. Application Considerations
9. Implementation Considerations
9.1. Implementation Safeguards
9.2. Handling Online Guessing Attacks
9.3. Error Considerations
10. Security Considerations
10.1. Notable Design Differences
10.2. Security Analysis
10.3. Identities
10.4. Export Key Usage
10.5. Static Diffie-Hellman Oracles
10.6. Random-Key Robust MACs
10.7. Input Validation
10.8. OPRF Key Stretching
10.9. Client Enumeration
10.10. Protecting the Registration Masking Key
10.11. Password Salt and Storage Implications
10.12. AKE Private Key Storage
10.13. Client Authentication Using Credentials
11. IANA Considerations
12. References
12.1. Normative References
12.2. Informative References
Appendix A. Alternate Key Recovery Mechanisms
Appendix B. Alternate AKE Instantiations
B.1. HMQV Instantiation Sketch
B.2. SIGMA-I Instantiation Sketch
Appendix C. Test Vectors
C.1. Real Test Vectors
C.1.1. OPAQUE-3DH Real Test Vector 1
C.1.2. OPAQUE-3DH Real Test Vector 2
C.1.3. OPAQUE-3DH Real Test Vector 3
C.1.4. OPAQUE-3DH Real Test Vector 4
C.1.5. OPAQUE-3DH Real Test Vector 5
C.1.6. OPAQUE-3DH Real Test Vector 6
C.2. Fake Test Vectors
C.2.1. OPAQUE-3DH Fake Test Vector 1
C.2.2. OPAQUE-3DH Fake Test Vector 2
C.2.3. OPAQUE-3DH Fake Test Vector 3
Acknowledgments
Authors' Addresses
1. Introduction π
Password auth is everywhere π© Usually, a client sends its client ID and password to the server over a secure connection. The catch is that this makes the password vulnerable to server mishandling, like accidentally logging it or storing it in plaintext in a database π Server compromise that exposes these plaintext passwords is not an uncommon incident, even among security-conscious orgs. Plaintext password auth over secure channels like TLS is also vulnerable whenever TLS might fail. That includes PKI attacks, certificate mishandling, termination outside the security perimeter, visibility to TLS-terminating intermediaries, and more π«
Augmented (or Asymmetric) Password Authenticated Key Exchange (aPAKE) protocols are built to give you password auth and mutually authenticated key exchange in a client-server setting. They don't rely on PKI (except during client registration), and they don't disclose passwords to servers or anyone else besides the client machine π A secure aPAKE should give you the best possible security for a password protocol. Some attacks are unavoidable though, like online impersonation attempts with guessed passwords, and offline dictionary attacks after a server is compromised and its credential file leaks. In that second case, the attacker learns a mapping of the client's password under a one-way function, then uses that mapping to check potential guesses π So it's crucially important that the password protocol uses an unpredictable one-way mapping. Otherwise, the attacker can pre-compute a deterministic list of mapped passwords, and that leads to almost instant password leakage once a server is compromised π±
This document describes OPAQUE, an aPAKE protocol that's secure against pre-computation attacks (as defined in [JKX18]) π¦ OPAQUE gives forward secrecy with respect to password leakage, and it hides the password from the server, even during registration π It also lets applications make offline dictionary attacks harder through iterated hashing or other key-stretching schemes. And it's extensible: clients can safely store and retrieve arbitrary application data on servers using only their password π§
OPAQUE is defined and proven as a composition of three functionalities: an Oblivious Pseudorandom Function (OPRF), a key recovery mechanism, and an authenticated key exchange (AKE) protocol π§© It works like a "compiler" that transforms any suitable AKE protocol into a secure aPAKE protocol. (See Section 10 for requirements of the OPRF and AKE protocols.) This document specifies one OPAQUE instantiation based on [TripleDH]. Other instantiations are possible, as discussed in Appendix B, but their details are out of scope for this document πΈ Overall, OPAQUE's modular design makes it easy to plug in additional AKE protocols, e.g., TLS or HMQV (Hashed Menezes-Qu-Vanstone), and future AKE protocols like those based on post-quantum techniques. Main character design, honestly π
OPAQUE has two stages: registration and authenticated key exchange. In the first stage, a client registers its password with the server and stores information on the server that's used to recover authentication credentials. Recovering those credentials can only be done with knowledge of the client password π In the second stage, the client uses its password to recover those credentials, then uses them as input to an AKE protocol. This stage also has extra mechanisms to stop an active attacker from talking to the server to guess or confirm which clients were registered in the first phase. Servers can use this mechanism to protect registered clients from this type of enumeration attack. See Section 10.9 for more discussion π«Ά
The name "OPAQUE" is a homonym of O-PAKE, where O is for Oblivious. The name "OPAKE" was already taken π
This document complies with the requirements for PAKE protocols set forth in [RFC8125]. It represents the consensus of the Crypto Forum Research Group (CFRG). It is not an IETF product and it is not a standard π
1.1. Requirements Notation
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here π Lowercase versions don't count, so no accidental requirements, bestie π
1.2. Notation
The following functions are used throughout this document β¨
I2OSPandOS2IP: Convert a byte string to and from a non-negative integer as described in Section 4 of [RFC8017]. Note that these functions operate on byte strings in big-endian byte order πconcat(x0, ..., xN): Concatenate byte strings. For example,concat(0x01, 0x0203, 0x040506) = 0x010203040506π§΅random(n): Generates a cryptographically secure pseudorandom byte string of length n bytes π²zeroes(n): Generate a string of n bytes all equal to 0 (zero) π«§xor(a,b): Apply XOR to byte strings. For example,xor(0xF0F0, 0x1234) = 0xE2C4. It is an error to call this function with arguments of unequal length π«ct_equal(a, b): Return true if a is equal to b, and false otherwise. The implementation of this function must be constant-time in the length of a and b, which are assumed to be of equal length, irrespective of the values a or b. This part is a big deal for security, so respect the constant-time vibes π₯
Unless said otherwise, random choices in this specification mean drawing with uniform distribution from a given set. In other words, "random" is short for "uniformly random" π― Random choices can be replaced with fresh outputs from a cryptographically strong pseudorandom generator, following the requirements in [RFC4086], or with a pseudorandom function. For convenience, we define nil as a lack of value π
All protocol messages and structures defined in this document use the syntax from Section 3 of [RFC8446] π
That's part 1 of 15, bestie π§π Part 2 is coming up next!
2. Cryptographic Dependencies β¨
okay so OPAQUE leans on a bunch of crypto building blocks, and bestie, they are all so important ππ here's the lineup:
- Oblivious Pseudorandom Function (OPRF); Section 2.1
- Key Derivation Function (KDF); Section 2.2
- Message Authentication Code (MAC); Section 2.2
- Cryptographic Hash Function; Section 2.3
- Key Stretching Function (KSF); Section 2.3
this section goes through each one in detail π«Ά and ngl the random stuff is very specific: unless it says otherwise, all random nonces and seeds used in these dependencies and the rest of OPAQUE are Nn and Nseed bytes long, where Nn = Nseed = 32. so 32 bytes, periodt π
2.1. Oblivious Pseudorandom Function
okay the OPRF is giving two-party energy π¦ the client and server work together to compute a Pseudorandom Function (PRF). the PRF key lives on the server, and the client supplies the input. the client learns nothing about the PRF except the output it gets back, and the server learns nothing about the client's input or the output. iconic privacy behavior π this spec relies on the prime-order OPRF construction called modeOPRF (0x00) from Section 3.1 of [RFC9497] π
the client-side OPRF APIs are:
Blind(element): makes and outputs (blind, blinded_element). blinded_element is a blinded version of the input element, and blind is the value you need to undo the blinding later π this is the same as the Blind function in Section 3.3.1 of [RFC9497].
Finalize(element, blind, evaluated_element): finishes the OPRF evaluation using the input element, the random inverter blind, and the evaluation output evaluated_element, and gives back oprf_output. same deal as the Finalize function in Section 3.3.1 of [RFC9497] π
and on the server side there are these OPRF APIs:
BlindEvaluate(k, blinded_element): evaluates the blinded input blinded_element using the input key k, giving back evaluated_element. this matches the BlindEvaluate function in Section 3.3.1 of [RFC9497], where k is the private key parameter π
DeriveKeyPair(seed, info): makes and outputs (sk, pk), a private and public key that come out deterministically from the input seed and info, as described in Section 3.2 of [RFC9497] π§
last but not least, the shared APIs and parameters:
SerializeElement(element): maps the input element to a fixed-length byte array πΈ
DeserializeElement(buf): tries to map the input byte array buf to an OPRF group element. it can raise a DeserializeError if it fails, so see Section 2.1 of [RFC9497] for the full story π
Noe: the size of a serialized OPRF group element output from SerializeElement.
Nok: the size of an OPRF private key as output from DeriveKeyPair.
2.2. Key Derivation Function and Message Authentication Code
a Key Derivation Function (KDF) takes some initial keying material and uses it to derive one or more cryptographically strong keys. β¨ this spec uses a KDF with this API and these parameters:
Extract(salt, ikm): pulls out a pseudorandom key of fixed length Nx bytes from the input keying material ikm and an optional byte string salt.
Expand(prk, info, L): expands a pseudorandom key prk, using the string info, into L bytes of output keying material.
Nx: the output size of the Extract() function, in bytes.
this spec also uses a random-key robust Message Authentication Code (MAC) π‘οΈ see Section 10.6 for more on that property. the API and parameters for the random-key robust MAC are:
MAC(key, msg): computes a message authentication code over the input msg with the key key, producing a fixed-length output of Nm bytes.
Nm: the output size of the MAC() function, in bytes.
2.3. Hash Functions
this spec uses a collision-resistant hash function with this API and these parameters π
Hash(msg): applies a cryptographic hash function to the input msg, producing a fixed-length digest of size Nh bytes.
Nh: the output size of the Hash() function, in bytes.
and then there's the Key Stretching Function (KSF), which is a slow and expensive cryptographic hash function. it's the one that makes offline guessing painful for attackers π€ its API is:
Stretch(msg): applies a key stretching function to stretch the input msg and harden it against offline dictionary attacks. this function also has to satisfy collision resistance π₯
3. Protocol Overview πΈ
okay so OPAQUE has two stages: registration and authenticated key exchange (AKE) π in the first stage, a client registers its password with the server and the server stores its credential file. in the second stage (also called the "login" or "online" stage), the client recovers its authentication material and uses it to do a mutually authenticated key exchange π«Ά
3.1. Setup
before both stages, the client and server agree on a configuration that fully specifies the cryptographic algorithm dependencies needed to run the protocol. see Section 7 for details π the server picks a pair of keys (server_private_key and server_public_key) for the AKE protocol, and picks a seed (oprf_seed) of Nh bytes for the OPRF π the server can use server_private_key and server_public_key with multiple clients. it can also choose to use a different seed for each client (so each client gets its own single seed), as long as they are kept across the registration and online AKE stages and stay consistent for each client. why? because an inconsistent mapping of clients to seeds could leak information, as described in Section 10.9 π¬
3.2. Registration
registration is the only stage in OPAQUE that REQUIRES a server-authenticated channel with confidentiality and integrity: either physical, out-of-band, PKI-based, etc. π
the client inputs its credentials, which include its password and user identifier, and the server inputs its parameters, which include its private key and other info π
the client's output from this stage is a single value export_key that the client may use for application-specific purposes, e.g., as a symmetric key to encrypt additional information for storage on the server π the server does not have access to this export_key π
the server's output from this stage is a record for the client's registration, which it stores in a credential file alongside other clients' registrations as needed ποΈ
the registration flow is in Figure 1, and Section 5 has the details π¦
credentials parameters
| |
v v
Client Server
------------------------------------------------
registration request
------------------------->
registration response
<-------------------------
record
------------------------->
------------------------------------------------
| |
v v
export_key record
Figure 1
these messages are named RegistrationRequest, RegistrationResponse, and RegistrationRecord, respectively. their contents and wire format are defined in Section 5.1 π§
3.3. Online Authenticated Key Exchange
in this second stage, a client gets credentials it registered earlier, recovers private key material using the password, and then uses them as input to the AKE protocol π like in registration, the client inputs its credentials (password and user identifier), and the server inputs its parameters plus the credential file record for that client. the client outputs two values: an export_key (matching the one from registration) and a session_key, and the session_key is the main AKE output. the server outputs a single session_key that matches the client's π once it's done, clients and servers can use these values however they need β¨
the authenticated key exchange flow is in Figure 2 π¦
credentials (parameters, record)
| |
v v
Client Server
------------------------------------------------
AKE message 1
------------------------->
AKE message 2
<-------------------------
AKE message 3
------------------------->
------------------------------------------------
| |
v v
(export_key, session_key) session_key
Figure 2
these messages are named KE1, KE2, and KE3, respectively π they carry the messages of the concurrent execution of the key recovery process (OPRF) and the authenticated key exchange (AKE). their wire formats are specified in Section 6.1 π
the rest of this document gets into the specifics of these stages. Section 4 covers how client credential info is generated, encoded, and stored on the server during registration and recovered during login π¦ Section 5 covers the first registration stage, Section 6 covers the second authentication stage, and Section 7 explains how to instantiate OPAQUE with different cryptographic dependencies and parameters π
4. Client Credential Storage and Key Recovery β¨
Okay so OPAQUE has this structure called Envelope π that holds the client's credentials. The client makes its Envelope at registration and sends it to the server for storage. Then on every login, the server sends that Envelope back so the client can recover its key material for the AKE. Iconic storage behavior π
Applications can pin key material to identities if they want π¦ If no identity is given for a party, its value MUST default to its public key. These are the types of application credential info we're looking at:
client_private_key: the encoded client private key for the AKE protocol πclient_public_key: the encoded client public key for the AKE protocol πserver_public_key: the encoded server public key for the AKE protocol πΈclient_identity: the client identity. This is app-specific, like an email address or an account name π If it's not specified, it defaults to the client's public key.server_identity: the server identity. This is usually a domain name, like example.com π If it's not specified, it defaults to the server's public key. See Section 10.3 for info about this identity.
Some of these credential values get used in the CleartextCredentials structure, like this:
struct {
uint8 server_public_key[Npk];
uint8 server_identity<1..2^16-1>;
uint8 client_identity<1..2^16-1>;
} CleartextCredentials;
The function CreateCleartextCredentials builds a CleartextCredentials structure from the application credential info. Let's look at how it works π
CreateCleartextCredentials
Input:
server_public_key, the encoded server public key for the AKE protocol.client_public_key, the encoded client public key for the AKE protocol.server_identity, the optional encoded server identity.client_identity, the optional encoded client identity.
Output:
cleartext_credentials, a CleartextCredentials structure.
def CreateCleartextCredentials(server_public_key, client_public_key,
server_identity, client_identity):
# Set identities as public keys if no
# application-layer identity is provided
if server_identity == nil
server_identity = server_public_key
if client_identity == nil
client_identity = client_public_key
cleartext_credentials = CleartextCredentials {
server_public_key,
server_identity,
client_identity
}
return cleartext_credentials
4.1. Key Recovery
Here's the vibe: this spec defines a key recovery mechanism that uses the stretched OPRF output as a seed to directly derive the private and public keys. It does that using the DeriveDiffieHellmanKeyPair() function from Section 6.4.1 π§ β¨
4.1.1. Envelope Structure
The key recovery mechanism defines its Envelope like this π
struct {
uint8 envelope_nonce[Nn];
uint8 auth_tag[Nm];
} Envelope;
envelope_nonce: a randomly sampled nonce of length Nn that protects this Envelope π²auth_tag: an authentication tag that protects the contents of the Envelope. It coversenvelope_nonceand CleartextCredentials π‘οΈ
4.1.2. Envelope Creation
Clients create an Envelope at registration using the Store function defined below. Heads up: DeriveDiffieHellmanKeyPair in this function can fail with negligible probability π If that happens, servers should re-run the function, sampling a new envelope_nonce, all the way to completion.
Store
Input:
randomized_password, a randomized password.server_public_key, the encoded server public key for the AKE protocol.server_identity, the optional encoded server identity.client_identity, the optional encoded client identity.
Output:
envelope, the client's Envelope structure.client_public_key, the client's AKE public key.masking_key, an encryption key the server uses with the sole purpose of defending against client enumeration attacks π«£export_key, an additional client key.
def Store(randomized_password, server_public_key,
server_identity, client_identity):
envelope_nonce = random(Nn)
masking_key = Expand(randomized_password, "MaskingKey", Nh)
auth_key =
Expand(randomized_password, concat(envelope_nonce, "AuthKey"),
Nh)
export_key =
Expand(randomized_password, concat(envelope_nonce, "ExportKey"),
Nh)
seed =
Expand(randomized_password, concat(envelope_nonce, "PrivateKey"),
Nseed)
(_, client_public_key) = DeriveDiffieHellmanKeyPair(seed)
cleartext_credentials =
CreateCleartextCredentials(server_public_key, client_public_key,
server_identity, client_identity)
auth_tag =
MAC(auth_key, concat(
envelope_nonce,
server_public_key,
I2OSP(len(cleartext_credentials.server_identity), 2),
cleartext_credentials.server_identity,
I2OSP(len(cleartext_credentials.client_identity), 2),
cleartext_credentials.client_identity
))
envelope = Envelope {
envelope_nonce,
auth_tag
}
return (envelope, client_public_key, masking_key, export_key)
4.1.3. Envelope Recovery
Clients recover their Envelope during login using the Recover function below. This is where the magic happens β¨
Recover
Input:
randomized_password, a randomized password.server_public_key, the encoded server public key for the AKE protocol.envelope, the client's Envelope structure.server_identity, the optional encoded server identity.client_identity, the optional encoded client identity.
Output:
client_private_key, the encoded client private key for the AKE protocol.cleartext_credentials, a CleartextCredentials structure.export_key, an additional client key.
Exceptions:
EnvelopeRecoveryError, the Envelope fails to be recovered π
def Recover(randomized_password, server_public_key, envelope,
server_identity, client_identity):
auth_key =
Expand(randomized_password, concat(envelope.nonce, "AuthKey"),
Nh)
export_key =
Expand(randomized_password, concat(envelope.nonce, "ExportKey"),
Nh)
seed =
Expand(randomized_password, concat(envelope.nonce, "PrivateKey"),
Nseed)
(client_private_key, client_public_key) =
DeriveDiffieHellmanKeyPair(seed)
cleartext_credentials =
CreateCleartextCredentials(server_public_key, client_public_key,
server_identity, client_identity)
expected_tag =
MAC(auth_key, concat(envelope.nonce, cleartext_credentials))
If !ct_equal(envelope.auth_tag, expected_tag)
raise EnvelopeRecoveryError
return (client_private_key, cleartext_credentials, export_key)
If EnvelopeRecoveryError gets raised, all previously computed intermediary values in this function MUST be deleted ποΈ No leftovers allowed, bestie.
5. Registration π
Okay, registration time π₯° The client puts in these values:
password: the client's password πcreds: the client credentials, as described in Section 4.
The server puts in these values:
server_public_key: the server public key for the AKE protocol.credential_identifier: a unique identifier for the client's credential, generated by the server.client_identity: the optional client identity, as described in Section 4.oprf_seed: a seed used to derive per-client OPRF keys.
The registration protocol then goes like this:
Client Server
------------------------------------------------------
(request, blind) = CreateRegistrationRequest(password)
request
------------------------->
response = CreateRegistrationResponse(request,
server_public_key,
credential_identifier,
oprf_seed)
response
<-------------------------
(record, export_key) = FinalizeRegistrationRequest(password,
blind,
response,
server_identity,
client_identity)
record
------------------------->
Section 5.1 describes the formats for the above messages, and Section 5.2 walks through the functions and their parameters π
At the end of this interaction, the server stores the record object as the credential file for each client, along with the associated credential_identifier and client_identity (if it's different). Also, the oprf_seed and server_private_key from the server's setup phase must be persisted too πΎ The oprf_seed value SHOULD be used for all clients. See Section 10.9 for the reasoning behind that, plus the exception where applications may choose to avoid a global oprf_seed across clients and instead sample OPRF keys uniquely for each client π¦ The server_private_key may be unique for each client.
Both client and server MUST validate the other party's public key before use π See Section 10.7 for more details. Once that's done, the server stores the client's credentials for later use. And the client MAY use the output export_key for further application-specific purposes, see Section 10.4 π
5.1. Registration Messages
This section defines the RegistrationRequest, RegistrationResponse, and RegistrationRecord messages that get exchanged between client and server during registration π¨
struct {
uint8 blinded_message[Noe];
} RegistrationRequest;
blinded_message: a serialized OPRF group element.
struct {
uint8 evaluated_message[Noe];
uint8 server_public_key[Npk];
} RegistrationResponse;
evaluated_message: a serialized OPRF group element.server_public_key: the server's encoded public key, which will be used for the online AKE stage.
struct {
uint8 client_public_key[Npk];
uint8 masking_key[Nh];
Envelope envelope;
} RegistrationRecord;
client_public_key: the client's encoded public key, corresponding to the private keyclient_private_key.masking_key: an encryption key the server uses with the sole purpose of defending against client enumeration attacks π«£envelope: the client's Envelope structure.
5.2. Registration Functions
This section defines the functions the client and server use during registration, including CreateRegistrationRequest, CreateRegistrationResponse, and FinalizeRegistrationRequest π οΈ
5.2.1. CreateRegistrationRequest
To kick off the registration flow, the client runs the following function. Heads up: this function can fail with an InvalidInputError with negligible probability π If that happens, the client needs a different input password.
CreateRegistrationRequest
Input:
password, an opaque byte string containing the client's password.
Output:
request, a RegistrationRequest structure.blind, an OPRF scalar value.
Exceptions:
InvalidInputError, when Blind fails
def CreateRegistrationRequest(password):
(blind, blinded_element) = Blind(password)
blinded_message = SerializeElement(blinded_element)
request = RegistrationRequest {
blinded_message
}
return (request, blind)
5.2.2. CreateRegistrationResponse β¨
Okay bestie, so the server runs this function to handle the client's registration request π«Ά. Heads up, it can fail with a DeriveKeyPairError with negligible probability π. If that happens, apps can pick a new credential_identifier for this registration record and rerun the function π.
CreateRegistrationResponse
Input:
request, aRegistrationRequeststructure.server_public_key, the server's public key.credential_identifier, an identifier that uniquely represents the credential.oprf_seed, the seed ofNhbytes used by the server to generate anoprf_key.
Output:
response, aRegistrationResponsestructure.
Exceptions:
DeserializeError, when OPRF element deserialization fails.DeriveKeyPairError, when OPRF key derivation fails.
def CreateRegistrationResponse(request, server_public_key,
credential_identifier, oprf_seed):
seed =
Expand(oprf_seed, concat(credential_identifier, "OprfKey"), Nok)
(oprf_key, _) = DeriveKeyPair(seed, "OPAQUE-DeriveKeyPair")
blinded_element = DeserializeElement(request.blinded_message)
evaluated_element = BlindEvaluate(oprf_key, blinded_element)
evaluated_message = SerializeElement(evaluated_element)
response = RegistrationResponse {
evaluated_message,
server_public_key
}
return response
5.2.3. FinalizeRegistrationRequest π
Now the client gets to build the user record it'll use for future logins, and that wraps up the registration flow π. Here's the function that does it.
FinalizeRegistrationRequest
Input:
password, an opaque byte string containing the client's password.blind, an OPRF scalar value.response, aRegistrationResponsestructure.server_identity, the optional encoded server identity.client_identity, the optional encoded client identity.
Output:
record, aRegistrationRecordstructure.export_key, an additional client key.
Exceptions:
DeserializeError, when OPRF element deserialization fails.
def FinalizeRegistrationRequest(password, blind, response,
server_identity, client_identity):
evaluated_element = DeserializeElement(response.evaluated_message)
oprf_output = Finalize(password, blind, evaluated_element)
stretched_oprf_output = Stretch(oprf_output)
randomized_password =
Extract("", concat(oprf_output, stretched_oprf_output))
(envelope, client_public_key, masking_key, export_key) =
Store(randomized_password, response.server_public_key,
server_identity, client_identity)
record = RegistrationRecord {
client_public_key,
masking_key,
envelope
}
return (record, export_key)
See Section 6 for details about the output export_key usage π.
6. Online Authenticated Key Exchange π€
The generic outline of OPAQUE with a 3-message AKE protocol has three messages: KE1, KE2, and KE3 πΆ. KE1 and KE2 carry key exchange shares (think DH values) sent by the client and server, in that order. KE3 gives explicit client authentication and full forward security. Without it, forward secrecy only holds against eavesdroppers, and that's not enough for OPAQUE security π ββοΈ.
This section walks through the online authenticated key exchange flow, the message encoding, and the helper functions. It's a concurrent OPRF and key exchange flow, and the key exchange is authenticated using the client and server credentials set up during registration (see Section 5). By the end, the client has proven it knows the password, and both sides agree on (1) a mutually authenticated shared secret key and (2) any optional application information exchanged during the handshake. Periodt π .
In this stage, the client inputs the following values:
password: The client's password.client_identity: The client identity as described in Section 4.
The server inputs the following values:
server_private_key: The server's private key for the AKE protocol.server_public_key: The server's public key for the AKE protocol.server_identity: The server identity as described in Section 4.record: The RegistrationRecord object corresponding to the client's registration.credential_identifier: An identifier that uniquely represents the credential.oprf_seed: The seed used to derive per-client OPRF keys.
The client gets two outputs: a session secret and an export key π. The export key is only available to the client and can be used for extra application-specific purposes, as outlined in Section 10.4. Clients MUST NOT use the output export_key before authenticating the peer in the authenticated key exchange protocol π«. See Appendix A for more details about this requirement. The server gets a single output: a session secret that matches the client's β¨.
The protocol runs like this:
Client Server
------------------------------------------------------
ke1 = GenerateKE1(password)
ke1
------------------------->
ke2 = GenerateKE2(server_identity, server_private_key,
server_public_key, record,
credential_identifier, oprf_seed, ke1)
ke2
<-------------------------
(ke3,
session_key,
export_key) = GenerateKE3(client_identity,
server_identity, ke2)
ke3
------------------------->
session_key = ServerFinish(ke3)
Both client and server may use implicit internal state objects to hold the material they need for the OPRF and AKE, called client_state and server_state respectively π§ .
The client state ClientState may have the following fields:
password: The client's password.blind: The random blinding inverter returned byBlind().client_ake_state: TheClientAkeStateas defined in Section 6.4.
The server state ServerState may have the following fields:
server_ake_state: TheServerAkeStateas defined in Section 6.4.
Both of these states are ephemeral and should be erased after the protocol completes π§Ή.
The rest of this section describes these authenticated key exchange messages and their parameters in more detail. Section 6.1 defines the structure of the messages passed between client and server in the setup above. Section 6.2 describes the details of the functions and corresponding parameters mentioned above. Section 6.3 covers the internal functions used for retrieving client credentials, and Section 6.4 covers how those functions are used to run the authenticated key exchange protocol π.
6.1. AKE Messages π¨
In this section we define the KE1, KE2, and KE3 structs that make up the AKE messages used in the protocol. KE1 is made of a CredentialRequest and an AuthRequest, and KE2 is made of a CredentialResponse and an AuthResponse π«.
struct {
uint8 client_nonce[Nn];
uint8 client_public_keyshare[Npk];
} AuthRequest;
client_nonce: A fresh randomly generated nonce of lengthNn.client_public_keyshare: A serialized client ephemeral public key of fixed sizeNpk.
struct {
CredentialRequest credential_request;
AuthRequest auth_request;
} KE1;
credential_request: ACredentialRequeststructure.auth_request: AnAuthRequeststructure.
struct {
uint8 server_nonce[Nn];
uint8 server_public_keyshare[Npk];
uint8 server_mac[Nm];
} AuthResponse;
server_nonce: A fresh randomly generated nonce of lengthNn.server_public_keyshare: A server ephemeral public key of fixed sizeNpk, whereNpkdepends on the corresponding prime order group.server_mac: An authentication tag computed over the handshake transcript usingKm2, which is defined below.
struct {
CredentialResponse credential_response;
AuthResponse auth_response;
} KE2;
credential_response: ACredentialResponsestructure.auth_response: AnAuthResponsestructure.
struct {
uint8 client_mac[Nm];
} KE3;
client_mac: An authentication tag computed over the handshake transcript, of fixed sizeNm, usingKm2, defined below.
6.2. AKE Functions π οΈ
In this section we define the main functions used to produce the AKE messages in the protocol. Heads up, this section leans on subroutines defined in later sections π:
CreateCredentialRequest,CreateCredentialResponse, andRecoverCredentialsare defined in Section 6.3.AuthClientStart,AuthServerRespond,AuthClientFinalize, andAuthServerFinalizeare defined in Sections 6.4.3 and 6.4.4.
6.2.1. GenerateKE1 π
The GenerateKE1 function kicks off the AKE protocol and produces the client's KE1 output for the server.
GenerateKE1
State:
state, aClientStatestructure.
Input:
password, an opaque byte string containing the client's password.
Output:
ke1, a KE1 message structure.
def GenerateKE1(password):
request, blind = CreateCredentialRequest(password)
state.password = password
state.blind = blind
ke1 = AuthClientStart(request)
return ke1
6.2.2. GenerateKE2 π¦
The GenerateKE2 function keeps the AKE protocol going by processing the client's KE1 message and producing the server's KE2 output.
GenerateKE2
State:
state, aServerStatestructure.
Input:
server_identity, the optional encoded server identity, which is set toserver_public_keyif not specified.server_private_key, the server's private key.server_public_key, the server's public key.record, the client'sRegistrationRecordstructure.credential_identifier, an identifier that uniquely represents the credential.oprf_seed, the server-side seed ofNhbytes used to generate anoprf_key.ke1, a KE1 message structure.client_identity, the optional encoded client identity, which is set toclient_public_keyif not specified.
Output:
ke2, a KE2 structure.
def GenerateKE2(server_identity, server_private_key,
server_public_key, record, credential_identifier,
oprf_seed, ke1, client_identity):
credential_response =
CreateCredentialResponse(ke1.credential_request,
server_public_key, record,
credential_identifier, oprf_seed)
cleartext_credentials =
CreateCleartextCredentials(server_public_key,
record.client_public_key,
server_identity, client_identity)
auth_response =
AuthServerRespond(cleartext_credentials, server_private_key,
record.client_public_key, ke1,
credential_response)
ke2 = KE2 {
credential_response,
auth_response
}
return ke2
6.2.3. GenerateKE3 π
okay bestie, this is the client's big finale π
GenerateKE3 wraps up the AKE for the client, cooks up the KE3 message for the server, and hands back the session_key and export_key too π₯
State:
state, aClientStatestructure.
Input:
client_identity, the optional encoded client identity, which is set toclient_public_keyif not specified.server_identity, the optional encoded server identity, which is set toserver_public_keyif not specified.ke2, aKE2message structure.
Output:
ke3, aKE3message structure.session_key, the session's shared secret.export_key, an additional client key.
def GenerateKE3(client_identity, server_identity, ke2):
(client_private_key, cleartext_credentials, export_key) =
RecoverCredentials(state.password, state.blind,
ke2.credential_response,
server_identity, client_identity)
(ke3, session_key) =
AuthClientFinalize(cleartext_credentials,
client_private_key, ke2)
return (ke3, session_key, export_key)
6.2.4. ServerFinish π«Ά
ServerFinish is the server's turn to close things out, and it gives back the session_key πΈ Since the OPRF is a two-message protocol, KE3 has no OPRF element in it. So KE3 calls the AKE's AuthServerFinalize directly. That function takes KE3 as input and MUST verify the client authentication material inside it before the session_key can be used. This check is what gives you forward secrecy against active attackers, so it is not optional vibes, it is the whole point π
State:
state, aServerStatestructure.
Input:
ke3, aKE3structure.
Output:
session_key, the shared session secret if and only ifke3is valid.
def ServerFinish(ke3):
return AuthServerFinalize(ke3)
This function MUST NOT return the session_key value if the client authentication material is invalid π
ββοΈ Instead it may return an appropriate error message, such as ClientAuthenticationError, which is invoked from AuthServerFinalize.
6.3. Credential Retrieval π
this section is all about the sub-protocol that runs during authentication to fetch and recover the client's credentials π§
6.3.1. Credential Retrieval Messages π
this part covers the CredentialRequest and CredentialResponse messages that the client and server swap to do credential retrieval π¬
struct {
uint8 blinded_message[Noe];
} CredentialRequest;
blinded_message: a serialized OPRF group element.
struct {
uint8 evaluated_message[Noe];
uint8 masking_nonce[Nn];
uint8 masked_response[Npk + Nn + Nm];
} CredentialResponse;
evaluated_message: a serialized OPRF group element.masking_nonce: a nonce used for the confidentiality of themasked_responsefield.masked_response: an encrypted form of the server's public key and the client'sEnvelopestructure.
6.3.2. Credential Retrieval Functions β¨
this section introduces the CreateCredentialRequest, CreateCredentialResponse, and RecoverCredentials functions used for credential retrieval π¦
6.3.2.1. CreateCredentialRequest π
the client uses CreateCredentialRequest to kick off credential retrieval, and it spits out a CredentialRequest message plus the OPRF state. Like CreateRegistrationRequest, this function can fail with an InvalidInputError error, but only with negligible probability. Honestly this should not occur, because registration (via CreateRegistrationRequest) will already fail when it's given the same password input π
Input:
password, an opaque byte string containing the client's password.
Output:
request, aCredentialRequeststructure.blind, an OPRF scalar value.
Exceptions:
InvalidInputError, whenBlindfails
def CreateCredentialRequest(password):
(blind, blinded_element) = Blind(password)
blinded_message = SerializeElement(blinded_element)
request = CredentialRequest {
blinded_message
}
return (request, blind)
6.3.2.2. CreateCredentialResponse π
the server uses CreateCredentialResponse to process the client's CredentialRequest and finish the credential retrieval, producing a CredentialResponse π
there are two scenarios to handle when building a CredentialResponse object. Either the record for the client exists (meaning it's a properly registered client), or it was never created (meaning it's an unregistered client identity, which could be the result of an enumeration attack attempt π)
when there's an existing record with the matching credential_identifier, the server calls the following function to make a CredentialResponse:
Input:
request, aCredentialRequeststructure.server_public_key, the public key of the server.record, an instance ofRegistrationRecord, which is the server's output from registration.credential_identifier, an identifier that uniquely represents the credential.oprf_seed, the server-side seed ofNhbytes used to generate anoprf_key.
Output:
response, aCredentialResponsestructure.
Exceptions:
DeserializeError, when OPRF element deserialization fails.
def CreateCredentialResponse(request, server_public_key, record,
credential_identifier, oprf_seed):
seed =
Expand(oprf_seed, concat(credential_identifier, "OprfKey"), Nok)
(oprf_key, _) = DeriveKeyPair(seed, "OPAQUE-DeriveKeyPair")
blinded_element = DeserializeElement(request.blinded_message)
evaluated_element = BlindEvaluate(oprf_key, blinded_element)
evaluated_message = SerializeElement(evaluated_element)
masking_nonce = random(Nn)
credential_response_pad = Expand(record.masking_key,
concat(masking_nonce,
"CredentialResponsePad"),
Npk + Nn + Nm)
masked_response = xor(credential_response_pad,
concat(server_public_key, record.envelope))
response = CredentialResponse {
evaluated_message,
masking_nonce,
masked_response
}
return response
now for the case where the record doesn't exist and the server wants client enumeration prevention π‘οΈ The server MUST respond to the credential request in a way that fakes the existence of the record. The server SHOULD call the CreateCredentialResponse function with a fake client record argument that is set up so that:
record.client_public_keyis set to a randomly generated public key of lengthNpkrecord.masking_keyis set to a random byte string of lengthNhrecord.envelopeis set to the byte string consisting only of zeros of lengthNn + Nm
It is RECOMMENDED that a fake client record is created once (for example, as the first user record of the application) and then stored alongside the legitimate client records to serve later client requests β¨ This lets servers retrieve the record in a time comparable to that of a legitimate client record πββοΈ
Note that the responses from either scenario are indistinguishable to an adversary that is unable to guess the registered password for the client corresponding to credential_identifier π
6.3.2.3. RecoverCredentials π§
the client uses RecoverCredentials to process the server's CredentialResponse message and produce the client's private key, the server public key, and the export_key π
Input:
password, an opaque byte string containing the client's password.blind, an OPRF scalar value.response, aCredentialResponsestructure.server_identity, the optional encoded server identity.client_identity, the encoded client identity.
Output:
client_private_key, the encoded client private key for the AKE protocol.cleartext_credentials, aCleartextCredentialsstructure.export_key, an additional client key.
Exceptions:
DeserializeError, when OPRF element deserialization fails.
def RecoverCredentials(password, blind, response,
server_identity, client_identity):
evaluated_element = DeserializeElement(response.evaluated_message)
oprf_output = Finalize(password, blind, evaluated_element)
stretched_oprf_output = Stretch(oprf_output)
randomized_password =
Extract("", concat(oprf_output, stretched_oprf_output))
masking_key = Expand(randomized_password, "MaskingKey", Nh)
credential_response_pad =
Expand(masking_key,
concat(response.masking_nonce, "CredentialResponsePad"),
Npk + Nn + Nm)
concat(server_public_key, envelope) =
xor(credential_response_pad, response.masked_response)
(client_private_key, cleartext_credentials, export_key) =
Recover(randomized_password, server_public_key, envelope,
server_identity, client_identity)
return (client_private_key, cleartext_credentials, export_key)
6.4. 3DH Protocol π
this section describes the authenticated key exchange protocol for OPAQUE using 3DH π§ββοΈ It's a 3-message AKE that satisfies the forward secrecy and KCI properties discussed in Section 10, so it's giving security π₯
The client AKE state ClientAkeState mentioned in Section 6 has the following fields:
client_secret: an opaque byte string of lengthNsk.ke1: a value of typeKE1.
The server AKE state ServerAkeState mentioned in Section 6 has the following fields:
expected_client_mac: an opaque byte string of lengthNm.session_key: an opaque byte string of lengthNx.
Sections 6.4.3 and 6.4.4 spell out the inner workings of the client and server functions, respectively π
6.4.1. 3DH Key Exchange Functions π¦’
we assume the following functions exist for all Diffie-Hellman key exchange variants:
DeriveDiffieHellmanKeyPair(seed): derive a private and public Diffie-Hellman key pair deterministically from the input seed. The type of the private key depends on the implementation, whereas the type of the public key is a byte string ofNpkbytes.DiffieHellman(k, B): a function that performs the Diffie-Hellman operation between the private inputkand the public inputB. The output of this function is a unique, fixed-length byte string.
It is RECOMMENDED to use Elliptic Curve Diffie-Hellman for this key exchange protocol π« Implementations for the recommended groups in Section 7, as well as the groups covered by test vectors in Appendix C, are described in the following sections.
6.4.1.1. 3DH ristretto255 πΈ
this section describes the implementation of the Diffie-Hellman key exchange functions based on ristretto255 as defined in [RFC9496] π¬
DeriveDiffieHellmanKeyPair(seed): this function is implemented asDeriveKeyPair(seed, "OPAQUE-DeriveDiffieHellmanKeyPair"), whereDeriveKeyPairis as specified in Section 3.2 of [RFC9497]. The public value fromDeriveKeyPairis encoded usingSerializeElementfrom Section 2.1 of [RFC9497].DiffieHellman(k, B): implemented as scalar multiplication as described in [RFC9496] after decodingBfrom its encoded input using theDecodefunction in Section 4.3.1 of [RFC9496]. The output is then encoded using theSerializeElementfunction of the OPRF group described in Section 2.1 of [RFC9497] π¦
6.4.1.2. 3DH P-256 πΈ
okay so this bit is about the Diffie-Hellman key exchange functions over NIST P-256, as defined in [NISTCurves]. it's giving elliptic curve glow-up π
DeriveDiffieHellmanKeyPair(seed): As defined in Section 6.4.1.1. β¨
DiffieHellman(k, B): Implemented as scalar multiplication as described in [NISTCurves] after decoding B from its encoded input using the compressed Octet-String-to-Elliptic-Curve-Point method according to [NISTCurves]. The output is then encoded using the SerializeElement function of the OPRF group described in Section 2.1 of [RFC9497]. π¦
6.4.1.3. 3DH Curve25519 π
this section is the Curve25519 version, based on [RFC7748]. bestie, it's the one everybody's obsessed with π«Ά
DeriveDiffieHellmanKeyPair(seed): This function is implemented by returning the private key k based on seed (of length Nseed = 32 bytes) as described in Section 5 of [RFC7748], as well as the result of DiffieHellman(k, B), where B is the base point of Curve25519. π§
DiffieHellman(k, B): Implemented using the X25519 function in Section 5 of [RFC7748]. The output is then used raw with no processing. π
6.4.2. Key Schedule Functions π
this section contains the functions used for the AKE key schedule. the main character of the whole handshake π
6.4.2.1. Transcript Functions π
the OPAQUE-3DH key derivation procedures make use of the functions below that are repurposed from TLS 1.3 [RFC8446]. it's giving reuse and recycle, iconic π
Expand-Label(Secret, Label, Context, Length) =
Expand(Secret, CustomLabel, Length)
Where CustomLabel is specified and encoded (following Section 3.4 of [RFC8446]) as:
struct {
uint16 length = Length;
opaque label<8..255> = "OPAQUE-" + Label;
uint8 context<0..255> = Context;
} CustomLabel;
Derive-Secret(Secret, Label, Transcript-Hash) =
Expand-Label(Secret, Label, Transcript-Hash, Nx)
Note that the Label parameter is not a NULL-terminated string. π
OPAQUE-3DH can optionally include application-specific, shared context information in the transcript, such as configuration parameters or application-specific information, e.g., "appXYZ-v1.2.3". π«Ά
The OPAQUE-3DH key schedule requires a preamble, which is computed as follows. π
Preamble
Parameters:
- context, optional shared context information.
Input:
- client_identity, the optional encoded client identity, which is set to client_public_key if not specified.
- ke1, a KE1 message structure.
- server_identity, the optional encoded server identity, which is set to server_public_key if not specified.
- credential_response, the corresponding field on the KE2 structure.
- server_nonce, the corresponding field on the AuthResponse structure.
- server_public_keyshare, the corresponding field on the AuthResponse structure.
Output:
- preamble, the protocol transcript with identities and messages.
def Preamble(client_identity, ke1, server_identity,
credential_response, server_nonce,
server_public_keyshare):
preamble = concat("OPAQUEv1-",
I2OSP(len(context), 2), context,
I2OSP(len(client_identity), 2), client_identity,
ke1,
I2OSP(len(server_identity), 2), server_identity,
credential_response,
server_nonce,
server_public_keyshare)
return preamble
6.4.2.2. Shared Secret Derivation π₯
the OPAQUE-3DH shared secret derived during the key exchange protocol is computed using the following helper function. this is where the secret gets made, periodt β¨
DeriveKeys
Input:
- ikm, input key material.
- preamble, the protocol transcript with identities and messages.
Output:
- Km2, a MAC authentication key.
- Km3, a MAC authentication key.
- session_key, the shared session secret.
def DeriveKeys(ikm, preamble):
prk = Extract("", ikm)
handshake_secret =
Derive-Secret(prk, "HandshakeSecret", Hash(preamble))
session_key =
Derive-Secret(prk, "SessionKey", Hash(preamble))
Km2 = Derive-Secret(handshake_secret, "ServerMAC", "")
Km3 = Derive-Secret(handshake_secret, "ClientMAC", "")
return (Km2, Km3, session_key)
6.4.3. 3DH Client Functions π
the AuthClientStart function is used by the client to create a KE1 structure. this is the client's opening move, so make it count π¦
AuthClientStart
Parameters:
- Nn, the nonce length.
State:
- state, a ClientAkeState structure.
Input:
- credential_request, a CredentialRequest structure.
Output:
- ke1, a KE1 structure.
def AuthClientStart(credential_request):
client_nonce = random(Nn)
client_keyshare_seed = random(Nseed)
(client_secret, client_public_keyshare) =
DeriveDiffieHellmanKeyPair(client_keyshare_seed)
auth_request = AuthRequest {
client_nonce,
client_public_keyshare
}
ke1 = KE1 {
credential_request,
auth_request
}
state.client_secret = client_secret
state.ke1 = ke1
return ke1
the AuthClientFinalize function is used by the client to create a KE3 message and output session_key using the server's KE2 message and recovered credential information. the client finishes the handshake here, lowkey the most dramatic part π
AuthClientFinalize
State:
- state, a ClientAkeState structure.
Input:
- cleartext_credentials, a CleartextCredentials structure.
- client_private_key, the client's private key.
- ke2, a KE2 message structure.
Output:
- ke3, a KE3 structure.
- session_key, the shared session secret.
Exceptions:
- ServerAuthenticationError, the handshake fails.
def AuthClientFinalize(cleartext_credentials,
client_private_key, ke2):
dh1 = DiffieHellman(state.client_secret,
ke2.auth_response.server_public_keyshare)
dh2 = DiffieHellman(state.client_secret,
cleartext_credentials.server_public_key)
dh3 = DiffieHellman(client_private_key,
ke2.auth_response.server_public_keyshare)
ikm = concat(dh1, dh2, dh3)
preamble = Preamble(cleartext_credentials.client_identity,
state.ke1,
cleartext_credentials.server_identity,
ke2.credential_response,
ke2.auth_response.server_nonce,
ke2.auth_response.server_public_keyshare)
Km2, Km3, session_key = DeriveKeys(ikm, preamble)
expected_server_mac = MAC(Km2, Hash(preamble))
if !ct_equal(ke2.auth_response.server_mac, expected_server_mac),
raise ServerAuthenticationError
client_mac = MAC(Km3, Hash(concat(preamble, expected_server_mac)))
ke3 = KE3 {
client_mac
}
return (ke3, session_key)
6.4.4. 3DH Server Functions π
the AuthServerRespond function is used by the server to process the client's KE1 message and public credential information to create a KE2 message. the server's glow-up moment, no cap π
def AuthServerRespond(cleartext_credentials, server_private_key,
client_public_key, ke1, credential_response):
server_nonce = random(Nn)
server_keyshare_seed = random(Nseed)
(server_private_keyshare, server_public_keyshare) =
DeriveDiffieHellmanKeyPair(server_keyshare_seed)
preamble = Preamble(cleartext_credentials.client_identity,
ke1,
cleartext_credentials.server_identity,
credential_response,
server_nonce,
server_public_keyshare)
dh1 = DiffieHellman(server_private_keyshare,
ke1.auth_request.client_public_keyshare)
dh2 = DiffieHellman(server_private_key,
ke1.auth_request.client_public_keyshare)
dh3 = DiffieHellman(server_private_keyshare,
client_public_key)
ikm = concat(dh1, dh2, dh3)
Km2, Km3, session_key = DeriveKeys(ikm, preamble)
server_mac = MAC(Km2, Hash(preamble))
state.expected_client_mac =
MAC(Km3, Hash(concat(preamble, server_mac)))
state.session_key = session_key
auth_response = AuthResponse {
server_nonce,
server_public_keyshare,
server_mac
}
return auth_response
the AuthServerFinalize function is used by the server to process the client's KE3 message and output the final session_key. the last step of the handshake, and it's giving closure β¨
AuthServerFinalize
State:
- state, a ServerAkeState structure.
Input:
- ke3, a KE3 structure.
Output:
- session_key, the shared session secret if and only if ke3 is valid.
Exceptions:
- ClientAuthenticationError, the handshake fails.
def AuthServerFinalize(ke3):
if !ct_equal(ke3.client_mac, state.expected_client_mac):
raise ClientAuthenticationError
return state.session_key
7. Configurations β¨
okay bestie, this section is all about what an OPAQUE-3DH configuration actually is π it's a tuple of seven things: (OPRF, KDF, MAC, Hash, KSF, Group, Context) π and there are some conditions it has to meet so it's not just a random grab bag of algorithms π
7.1. OPRF π¦
hold up, the OPRF bit has to use the modeOPRF configuration in Section 3.1 of [RFC9497] and it has to implement the interface in Section 2 π₯ examples you'll see are ristretto255-SHA512 and P256-SHA256 π
7.2. KDF, MAC, and Hash πΈ
the KDF, MAC, and Hash functions need to implement the interfaces in Section 2 π«Ά examples include HKDF [RFC5869] for the KDF, HMAC [RFC2104] for the MAC, and SHA-256 and SHA-512 for the Hash functions π
now here's the tea β if you use an extensible output function like SHAKE128 [FIPS202], then the output length Nh MUST be chosen to line up with the target security level of the OPAQUE configuration β¨ like, if the target is 128 bits, then Nh SHOULD be at least 32 bytes π
7.3. KSF π
the KSF is picked by the application and it has to implement the interface in Section 2 π as we said before, collision resistance is REQUIRED π₯ examples are Argon2id [RFC9106], scrypt [RFC7914], and PBKDF2 [RFC8018] with fixed parameter choices π see Section 8 for more on why this choice matters π§
7.4. Group π
the Group mode says which group the OPAQUE-3DH AKE uses π¦ it SHOULD match the OPRF's group π so if your OPRF is ristretto255-SHA512, then Group SHOULD be ristretto255 β
7.5. Context π
Context is the shared parameter used to build the preamble in Section 6.4.2.1 πΈ it SHOULD include any application-specific configuration info or parameters needed to stop cross-protocol or downgrade attacks π₯ this one is so important π
7.6. Recommended Configurations π
okay so if there's no application-specific profile, these configurations are RECOMMENDED π«Ά here they are, bestie:
- ristretto255-SHA512, HKDF-SHA-512, HMAC-SHA-512, SHA-512, Argon2id(S = zeroes(16), p = 4, T = Nh, m = 2^21, t = 1, v = 0x13, K = nil, X = nil, y = 2), ristretto255
- P256-SHA256, HKDF-SHA-256, HMAC-SHA-256, SHA-256, Argon2id(S = zeroes(16), p = 4, T = Nh, m = 2^21, t = 1, v = 0x13, K = nil, X = nil, y = 2), P-256
- P256-SHA256, HKDF-SHA-256, HMAC-SHA-256, SHA-256, scrypt(S = zeroes(16), N = 32768, r = 8, p = 1, dkLen = 32), P-256
all of these target 128-bit security π§β¨ the list above is just the vibes, the real tuple format is in the code block below π
(OPRF, KDF, MAC, Hash, KSF, Group, Context)
7.7. Future Configurations π¦
future configs can mix up the dependent algorithms, but there are a couple of things to keep in mind π
- the size of AKE public and private keys (Npk and Nsk) MUST adhere to the output length limits of the KDF Expand function π if HKDF is used, that means Npk, Nsk <= 255 * Nx, where Nx is the output size of the underlying hash function π see [RFC5869] for the details π₯
- the output size of the Hash function SHOULD be long enough to make a MAC key of suitable length π«Ά for example, if MAC is HMAC-SHA256, then Nh could be 32 bytes π
8. Application Considerations π
okay so beyond picking a good configuration, there are a few parameters apps can use to control OPAQUE π let's go through them π
8.1. Credential Identifier πΈ
as we covered in Section 5, this is a unique handle to the client's stored credential π¦ if your app has other client identities that come with an account, like a username or email, the identifier can be set to those π for simplicity, apps MAY set credential_identifier to be equal to client_identity β¨ but apps MUST NOT use the same credential identifier for multiple clients ππ₯
8.2. Context Information π§
as described in Section 7, apps may include a shared context string that's authenticated as part of the handshake π this SHOULD include any config info or parameters needed to prevent cross-protocol or downgrade attacks π«Ά this context info is NOT sent over the wire in any key exchange messages π however, apps may choose to send it alongside key exchange messages if their use case needs it π
8.3. Client and Server Identities π
as covered in Section 4, clients and servers are identified with their public keys by default π₯ but apps may choose alternate identities pinned to those public keys β for example, servers may use a domain name instead of a public key as their identifier π absent alternate notions of identity, apps SHOULD set these identities to nil and rely solely on public key info π
8.4. Configuration and Envelope Updates π¦
apps may want to update their configuration or other params that affect the client's RegistrationRecord over time π reasons include switching cryptographic algorithms (like a different KSF with better parameters) or updating key material cryptographically bound to the RegistrationRecord, such as the server's public key (server_public_key) π any change like this will require users to reregister to create a new RegistrationRecord π supporting these kinds of updates can be super helpful for apps that expect changes in their deployment π«Ά
8.5. Password Hardening Parameters π
key stretching is done to help prevent password disclosure if the server gets compromised, see Section 10.8 π₯ there's no ideal or default set of parameters π but the specs for KSFs do give some reasonable defaults β¨
8.6. Enumeration Prevention π
if servers get a credential request for a non-existent client, they SHOULD respond with a "fake" response to prevent active client enumeration attacks, as described in Section 6.3.2.2 π§ servers that do this mitigation SHOULD use the same config info (like the oprf_seed) for all clients, see Section 10.9 π in settings where this attack isn't a concern, servers MAY choose not to support this π¦
8.7. Handling Password Changes π
if the password changes, the client and server can run the registration phase with the new password as a fresh instance (and make sure to resample all random values π₯) π the result can then replace the old record for the client's previous password β
9. Implementation Considerations π
this section is all the stuff to think about when building OPAQUE implementations π it covers safeguards and error handling π«Ά
9.1. Implementation Safeguards π₯
some info in OPAQUE is sensitive, like all private key material and intermediate values, plus the outputs of the key exchange phase π all of that is secret π implementations SHOULD NOT keep these values in memory once they're no longer needed π wait, I'm sorry, that's not what the original says, let me fix that bestie: implementations SHOULD NOT retain these values in memory when no longer needed πΈ
okay and all operations, especially the crypto and group arithmetic, SHOULD be constant-time and independent of the bits of any secrets π¦ this includes any conditional branching during creation of the credential response, as needed to mitigate client enumeration attacks π§
as Section 5 and Section 6 specify, OPAQUE only needs the client password as OPRF input for registration and authentication π however, if client_identity can be bound to the client's registration record (meaning the identity won't change during the record's lifetime), then an implementation SHOULD include client_identity alongside the password as OPRF input β¨ furthermore, it is RECOMMENDED to include server_identity alongside the password as OPRF input π these additions give domain separation for clients and servers, see Section 10.2 π₯
9.2. Handling Online Guessing Attacks π
online guessing attacks (against any aPAKE) can happen from both the client side and the server side π a malicious server can try to simulate honest responses to learn the client's password π this is an exhaustive online attack (as expensive as a guessing attack from the client side), but it can be mitigated when the channel between client and server is authenticated, like with server-authenticated TLS π«Ά in that case, these online attacks are limited to the clients and the authenticated server itself π§ and that channel also gives privacy for user info, including identity and envelope values π
also, a client in the online login stage will learn whether authentication succeeded after receiving the KE2 message π that means the server SHOULD treat any client that fails to send a follow-up KE3 message as an authentication failure π apps that want to track auth failures can do this by assuming any client auth attempt is a failure unless a KE3 message arrives and passes ServerFinish without error π₯
9.3. Error Considerations π
some functions in this spec can fail π for example, the authenticated key exchange might fail because the client's password was wrong or the auth check failed, giving an error π here are the explicit errors in this spec, with the conditions that trigger each one π
- EnvelopeRecoveryError: the Envelope Recover function failed to produce any authentication key material; see Section 4.1.3 π¦
- ServerAuthenticationError: the client failed to complete the authenticated key exchange with the server; see Section 6.4.3 πΈ
- ClientAuthenticationError: the server failed to complete the authenticated key exchange with the client; see Section 6.4.4 π§
beyond these explicit errors, implementations can produce implicit errors too π«Ά for example, if protocol messages sent between client and server don't match their expected size, an implementation SHOULD produce an error π more generally, if any protocol message received from the peer is invalid, maybe because it has an invalid public key (shown by the AKE DeserializeElement function failing) or an invalid OPRF element (shown by the OPRF DeserializeElement), then an implementation SHOULD produce an error β¨
the errors in this document are a guide for implementors π they're NOT an exhaustive list of every error an implementation might emit π for example, an implementation might run out of memory π
10. Security Considerations ππ₯
okay bestie, this is the big one and it's not a joke π security matters a LOT here, so read this part carefully π«Ά
OPAQUE is defined as the composition of two functionalities: an OPRF and an AKE protocol π¦ it can be seen as a "compiler" that transforms any AKE protocol (with Key Compromise Impersonation (KCI) security and forward secrecy; see Section 10.2) into a secure aPAKE protocol π§ in OPAQUE, the client derives a private key during password registration and retrieves this key each time it needs to authenticate to the server π the OPRF security properties make sure only the correct password can unlock the private key, while also avoiding potential offline guessing attacks π₯ this general composability property gives great flexibility and enables a variety of OPAQUE instantiations, from optimized performance to integration with existing authenticated key exchange protocols such as TLS πβ¨
10.1. Notable Design Differences π β¨
Okay bestie, this spec is NOT the same as the original crypto design in [JKX18] and the CFRG doc [Krawczyk20]. Both of those were inputs to the CFRG PAKE competition π. This section spills the tea on what's different, why we changed it, and why OPAQUE is still provably secure based on [JKX18] ππ«Ά
First up, the important functional differences that got made during the protocol spec process to deal with application or implementation stuff π:
- Clients construct envelope contents without revealing the password to the server, as described in Section 5, whereas the servers construct envelopes in [JKX18]. This change adds to the security of the protocol π. [JKX18] considered the case where the envelope was constructed by the server for reasons of compatibility with previous Universal Composability (UC) security modeling. [HJKW23] analyzes the registration phase as specified in this document. This change was made to support registration flows where the client chooses the password and wants to keep it secret from the server π€«, and it is compatible with the variant in [JKX18] that was originally analyzed π.
- Envelopes do not contain encrypted credentials. Instead, envelopes contain information used to derive client private key material for the AKE ποΈ. This change improves the assumption behind the protocol by getting rid of equivocality and random key robustness for the encryption function π. The random-key robustness property defined in Section 2.2 is only needed for the MAC function. This change was made for two reasons β¨. First, it reduces the number of bytes stored in envelopes, which is a helpful improvement for large applications of OPAQUE with many registered users π¦. Second, it removes the need for client applications to generate private keys during registration. Instead, this responsibility is handled by OPAQUE, thereby simplifying the client interface to the protocol π.
- Envelopes are masked with a per-user masking key as a way of preventing client enumeration attacks π΅οΈββοΈ. See Section 10.9 for more details. This extension is not needed for the security of OPAQUE as an aPAKE protocol, but is only used to provide a defense against enumeration attacks π‘οΈ. In the analysis, the masking key can be simulated as a (pseudo) random key. This change was made to support real-world use cases where client or user enumeration is a security (or privacy) risk π.
- Per-user OPRF keys are derived from a client identity and cross-user PRF seed as a mitigation against client enumeration attacks πΈ. See Section 10.9 for more details. The analysis of OPAQUE assumes OPRF keys of different users are independently random or pseudorandom. Deriving these keys via a single PRF (i.e., with a single cross-user key) applied to users' identities satisfies this assumption β
. This change was made to support real-world use cases where client or user enumeration is a security (or privacy) risk. Note that the derivation of the OPRF key via a PRF keyed by
oprf_seedand applied to the uniquecredential_identifierensures the critical requirement of the per-user OPRF keys being unique per client ππ . - The protocol outputs an export key for the client in addition to a shared session key that can be used for application-specific purposes π¦. This key is a pseudorandom value derived from the client password (among other values) and has no influence on the security analysis (it can be simulated with a random output) π. This change was made to support more application use cases for OPAQUE, such as the use of OPAQUE for end-to-end encrypted backups; see [WhatsAppE2E] π¬.
- The AKE protocol describes a 3-message protocol where the third message includes client authentication material that the server is required to verify π§. This change (from the original 2-message protocol) was made to provide explicit client authentication and full forward security π₯. The 3-message protocol is analyzed in [JKX18].
- The protocol admits optional application-layer client and server identities. In the absence of these identities, the client and server are authenticated against their public keys πͺͺ. Binding authentication to identities is part of the AKE part of OPAQUE. The type of identities and their semantics are application-dependent and independent of the protocol analysis. This change was made to simplify client and server interfaces to the protocol by removing the need to specify additional identities alongside their corresponding public authentication keys when not needed π.
- The protocol admits application-specific context information configured out-of-band in the AKE transcript π. This allows domain separation between different application uses of OPAQUE. This is a mechanism for the AKE component and is best practice for domain separation between different applications of the protocol π. This change was made to allow different applications to use OPAQUE without the risk of cross-protocol attacks β.
- Servers use a separate identifier for computing OPRF evaluations and indexing into the registration record storage called the
credential_identifier. This allows clients to change their application-layer identity (client_identity) without inducing server-side changes, e.g., by changing an email address associated with a given account π§. This mechanism is part of the derivation of OPRF keys via a single PRF. As long as the derivation of different OPRF keys from a single PRF has different PRF inputs, the protocol is secure π«§. The choice of such inputs is up to the application. - [JKX18] comments on a defense against offline dictionary attacks upon server compromise or honest-but-curious servers π§. The authors suggest implementing the OPRF phase as a threshold OPRF [TOPPSS], effectively forcing an attacker to act online or control at least
tkey shares (of the totaln), wheretis the threshold number of shares necessary to recombine the secret OPRF key π§©. Only then would an attacker be able to run an offline dictionary attack. This implementation only affects the server and changes nothing for the client π. Furthermore, if the threshold OPRF servers holding these keys are separate from the authentication server, then recovering allnshares would still not suffice to run an offline dictionary attack without access to the client record database ποΈ. However, this mechanism is out of scope for this document π«.
Next, the notable differences and refinements from the original crypto design in [JKX18] and the corresponding CFRG doc [Krawczyk20] that were made so this spec is suitable for interoperable implementations ππ«Ά:
- [JKX18] used a generic prime-order group for the DH-OPRF and HMQV operations, and includes necessary prime-order subgroup checks when receiving attacker-controlled values over the wire π‘οΈ. This specification instantiates the prime-order group used for 3DH using prime-order groups based on elliptic curves as described in Section 2.1 of [RFC9497]. This specification also delegates OPRF group choice and operations to Section 4 of [RFC9497]. As such, the prime-order group as used in the OPRF and 3DH as specified in this document both adhere to the requirements in [JKX18] β .
- Appendix B of [JKX18] specified DH-OPRF to instantiate the OPRF functionality in the protocol π¦. A critical part of DH-OPRF is the hash-to-group operation, which was not instantiated in the original analysis. However, the requirements for this operation were included. This specification instantiates the OPRF functionality based on Section 3.3.1 of [RFC9497], which is identical to the DH-OPRF functionality in [JKX18] and, concretely, uses the hash-to-curve functions in [RFC9380] β. All hash-to-curve methods in [RFC9380] are compliant with the requirement in [JKX18], namely, that the output be a member of the prime-order group π.
- [JKX18] and [Krawczyk20] both used HMQV as the AKE for the protocol. However, this document fully specifies 3DH instead of HMQV (though a sketch for how to instantiate OPAQUE using HMQV is included in Appendix B.1) π. Since 3DH satisfies the essential requirements for the AKE protocol as described in [JKX18] and [Krawczyk20], as recalled in Section 10.2, this change preserves the overall security of the protocol π. 3DH was chosen for its simplicity and ease of implementation π§.
- The DH-OPRF and HMQV instantiation of OPAQUE as shown in Figure 12 [JKX18] uses a different transcript than that which is described in this specification π§Ύ. In particular, the key exchange transcript specified in Section 6.4 is a superset of the transcript as defined in [JKX18]. This was done to align with best practices, like what is done for key exchange protocols like TLS 1.3 [RFC8446] π.
- Neither [JKX18] nor [Krawczyk20] included wire format details for the protocol, which is essential for interoperability π‘. This specification fills this gap by including such wire format details and corresponding test vectors; see Appendix C ππ¦.
10.2. Security Analysis β¨
okay bestie, this section is the "receipts" part of OPAQUE π§Ύπ Jarecki et al. [JKX18] proved OPAQUE is secure (with the design differences from Section 10.1 taken into account) in a strong aPAKE model π₯ that protects against precomputation attacks. The proof is written in the UC framework [Canetti01] under the random oracle model πΈ. It assumes the OPRF is secure and that the underlying key exchange protocol is secure too π«Ά
OPAQUE is built on a whole family tree of earlier work π³ It starts with the seminal paper by Ford and Kaliski [FK00] and borrows from the HPAKE protocol of Xavier Boyen [Boyen09] and the (1,1)-PPSS protocol from Jarecki et al. [JKKX16]. Lowkey the important plot twist π None of those papers looked at precomputation attacks, and none of them proved aPAKE security, not even in a weak model π
The KCI property is a big deal for AKE protocols used with OPAQUE π It means that knowing a party's private key does not let an attacker impersonate other people to that party π Most public-key-based AKE protocols have this, including ones that use signatures or public key encryption for authentication π Some implicitly authenticated protocols have it too, like HMQV π¦, but not all of them do. Also, key exchange protocols based on shared keys do NOT satisfy KCI, so they aren't considered in the OPAQUE setting π
KCI matters because it's the reason that, even if the server gets compromised, the attacker still can't impersonate the client to the server without first running an exhaustive dictionary attack π«£ Another must-have for AKE protocols in OPAQUE is forward secrecy against active attackers π‘οΈβ¨
Quick heads-up on scope π§ The [JKX18] proof covers one instance (one key) of OPAQUE, with no batching. There is currently no security analysis for the OPAQUE protocol described in this document in a setting with multiple server keys or batching π
As Section 9.1 mentioned, including client_identity adds domain separation π, especially against servers that pick the same OPRF key for multiple clients. client_identity as OPRF input also works as a key identifier, which would be needed for a proof in the multi-key setting, since the [JKX18] analysis assumes single server-key instances π Adding server_identity to the OPRF input gives domain separation for clients that reuse the same client_identity across different server instantiations π
10.3. Identities πͺͺ
AKE protocols generate keys that have to be uniquely and verifiably bound to a pair of identities π In OPAQUE, those are client_identity and server_identity. So the parties really need to agree on these identities, including an agreed bit representation when needed π€
Here's the thing: how client_identity gets sent from client to server is OUTSIDE the scope of this protocol π ββοΈ The application decides how to deliver it (for instance, alongside the first OPAQUE message, or agreed on before OPAQUE starts) π§
Different applications can have different policies for how and when identities get decided πΈ A natural approach is to tie client_identity to the identity the server uses to fetch the envelope (determined during password registration), and to tie server_identity to the server identity the client uses to start an offline password registration or an online authenticated key exchange session π server_identity and client_identity can also live inside the envelope or be tied to the parties' public keys π¦ In principle, identities can change across sessions as long as there's a policy that establishes whether the identity is acceptable to the peer π₯ BUT, and this is important bestie π¨, the public keys of both the server and the client must ALWAYS be the ones defined at the time of password registration π
client_identity and server_identity are OPTIONAL parameters, left to the application to designate as aliases for the client and server π If the application layer doesn't supply values for them, they get omitted when the envelope is created during registration π Then, during the authenticated key exchange stage, they get substituted with client_identity = client_public_key and server_identity = server_public_key π
Why bother supplying a custom client_identity and server_identity instead of just falling back to client_public_key and server_public_key? π€ Because then the client can make sure any mappings between client_identity and client_public_key (and between server_identity and server_public_key) are protected by the authentication from the envelope β¨ So the client can check that the client_identity and server_identity in its envelope match the ones the server supplied π
BUT if that extra check isn't needed for your app, it's totally fine to leave client_identity and server_identity unspecified and just use client_public_key and server_public_key instead πββοΈ
10.4. Export Key Usage ποΈ
The export key can be used (separately from the OPAQUE protocol) to give confidentiality and integrity to other data that only the client should be able to process π For example, if the client wants to store secrets with a third party, the client can use this export key to encrypt them π§ That way they stay hidden from a passive adversary who doesn't have access to the server's secret keys or the client's password π«Ά
10.5. Static Diffie-Hellman Oracles π§ͺ
You'd expect the practical security of the OPRF function (meaning how hard it is to compute the function without knowing the key) to be roughly on par with computing discrete logarithms or solving Diffie-Hellman π€ But Brown and Gallant [BG04] and Cheon [Cheon06] showed an attack that slightly beats generic attacks π³ For typical curves, the attack either needs an infeasible number of OPRF calls or costs insignificant security π See Section 7.2.3 of [RFC9497] for more info π For OPAQUE, these attacks are especially impractical, since they'd translate into an infeasible number of failed authentication attempts aimed at individual users π
10.6. Random-Key Robust MACs π
The random-key robustness property for a MAC says that, given two random keys k1 and k2, it's infeasible to find a message m where MAC(k1, m) = MAC(k2, m) π₯Ή Heads up: not every MAC function is key-robust π« GMAC (which underlies GCM) is NOT key-robust, but HMAC with a collision-resistant hash function IS key-robust π
An application CAN use a non-key-robust MAC within the AKE portion of the protocol described in Section 6.4, but it MUST use a key-robust MAC to create the auth_tag parameter in Section 4.1.2 π Not optional, bestie π
10.7. Input Validation π΅οΈββοΈ
Both client and server MUST validate the other party's public key(s) used for OPAQUE π¨ That covers the keys shared during registration AND any keys shared during the online key agreement phase π How you validate depends on the type of key π§ For example, for OPAQUE using 3DH with P-256, P-384, or P-521 as the underlying group, validation follows Section 5.6.2.3.4 of [keyagreement] π That means checking that the coordinates are in the correct range, that the point is on the curve, and that the point is not the point at infinity π ββοΈ Also, validation MUST make sure the Diffie-Hellman shared secret is not the point at infinity πβ¨
10.8. OPRF Key Stretching π³
Running a key stretching function on the OPRF output seriously ups the cost of an offline attack after the credential file on the server is compromised π₯ Applications SHOULD pick KSF parameters that balance cost and complexity across different client implementations and deployments π― Note that in OPAQUE, the key stretching function runs on the client, not the server like in common password hashing setups π So apps have to weigh performance on clients (especially low-end devices) against protection from offline attacks after a server compromise βοΈπ
10.9. Client Enumeration π
Client enumeration is when an attacker tries to figure out whether a given user identity is registered with a server, or whether a re-registration or password change happened for that user π΅οΈββοΈ OPAQUE fights this by making servers act with unregistered client identities in a way that looks identical to how they act with registered clients πͺ Servers do this by simulating a fake CredentialResponse as specified in Section 6.3.2.2 for unregistered users, and by encrypting CredentialResponse with a masking key π So real and fake CredentialResponse messages can't be told apart π«₯ Implementations also have to avoid side-channel leaks (like timing attacks) that could help tell these operations apart from a regular server response β±οΈ Heads up, this might open up abuse vectors, since the server's cost of generating a CredentialResponse is less than the client's cost of generating a CredentialRequest π Servers CAN skip building a simulated credential response for unregistered clients if these enumeration attacks can be handled by other app-specific means or just don't apply to their threat model π
OPAQUE does NOT prevent this kind of attack during the registration flow π¬ Servers necessarily react differently during registration for registered and unregistered clients π So an attacker can use the server's registration response as an oracle to check whether a given client identity is registered π Applications SHOULD mitigate this with rate limiting or other restrictions on the registration flow π¦
Last one, bestie π«Ά Apps that don't need protection against client enumeration can derive independent OPRF keys for different clients π The win here is that the server doesn't have to keep the oprf_seed value across different clients π§ If oprf_seed leaked, it would compromise security for every client that relies on it, as noted in [DL24] π±
10.10. Protecting the Registration Masking Key π‘οΈ
The user enumeration prevention method in this document uses a symmetric encryption key, masking_key ποΈ The client generates it and sends it to the server during registration π€ That means you NEED a confidential channel between client and server during registration, for example using TLS [RFC8446] π If the channel is only authenticated (which IS required for correctly identifying the parties πͺͺ), a confidential channel can be set up using public-key encryption, e.g., with HPKE [RFC9180] π The details of this mechanism are out of scope for this document πβ¨
10.11. Password Salt and Storage Implications π§β¨
okay so bestie, the OPRF key is literally doing the job of a secret salt here π§π₯ which means the whole precomputation attack thing is basically dead on arrival. no extra salt needed, we're good π and also clients never send their actual password to the server, not even during registration π«Ά that's the whole vibe of OPAQUE!!
BUT ngl there's a catch π a corrupted server can still run an exhaustive offline dictionary attack to check guesses for the client's password. this is inevitable in any single-server aPAKE protocol π the way to dodge it with OPAQUE is a multi-server threshold OPRF setup, e.g., [TOPPSS] π also if the server doesn't sample the PRF seed with high enough entropy, or doesn't keep it hidden from an adversary, then anything derived from the client's password could be vulnerable to an offline dictionary attack to recover the original password π yikes, so protect that seed like it's your main character energy π
Some apps want to learn the client's password so they can enforce password rules π€ doing that totally breaks this important OPAQUE security property, so it is NOT RECOMMENDED unless it's genuinely impossible to move those checks to the client π« limited checks at the server are still possible though, like detecting repeated passwords on reregistration or password change π
and overall, passwords should have sufficient entropy so they're not susceptible to dictionary attacks, online or offline π choose strong ones bestie!!
10.12. AKE Private Key Storage π
servers running OPAQUE don't need the raw AKE private key π they just need to be able to compute shared secrets as specified in Section 6.4.2 β¨ so applications can totally store the server AKE private key in a Hardware Security Module (HSM) or something similar π‘οΈ and if oprf_seed and client envelopes ever get compromised, that setup stops an attacker from using that data to pull off a server spoofing attack π¦ supporting implementations should think about allowing separate AKE and OPRF algorithms in cases where the HSM doesn't play nice with the OPRF algorithm π
10.13. Client Authentication Using Credentials π
okay so for scenarios where the client has private state that can stick around across registration and login π§ the client can back up the randomized_password variable (computed in Section 5.2.3) π then on a future login, the client can authenticate to the server using randomized_password instead of the original password π to do this, you supply an arbitrary password as input to CreateCredentialRequest during login, then use randomized_password from the backup in RecoverCredentials (which GenerateKE3 invokes) instead of computing it from the password πΈ
this is actually a perk compared to the regular login flow π¦ because even if randomized_password gets compromised, an adversary still can't use it to successfully impersonate the server to the client during login π the catch is that it only works in settings where randomized_password can be treated as a credential that's stored securely after registration and fetched at login π
11. IANA Considerations π
this document has no IANA actions π
12. References π
12.1. Normative References π
- [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-Hashing for Message Authentication", RFC 2104, DOI 10.17487/RFC2104, February 1997, <https://www.rfc-editor.org/info/rfc2104>.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, March 1997, <https://www.rfc-editor.org/info/rfc2119>.
- [RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, <https://www.rfc-editor.org/info/rfc4086>.
- [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, May 2017, <https://www.rfc-editor.org/info/rfc8174>.
- [RFC9497] Davidson, A., Faz-Hernandez, A., Sullivan, N., and C. A. Wood, "Oblivious Pseudorandom Functions (OPRFs) Using Prime-Order Groups", RFC 9497, DOI 10.17487/RFC9497, December 2023, <https://www.rfc-editor.org/info/rfc9497>.
12.2. Informative References ππ
okay bestie, here's the extra-reading list π§ these are the informative refs, so bookmark them for later π
- [BG04] Brown, D. and R. Galant, "The Static Diffie-Hellman Problem", Cryptology ePrint Archive, Paper 2004/306, 2004,
https://eprint.iacr.org/2004/306. - [Boyen09] Boyen, X., "HPAKE: Password Authentication Secure against Cross-Site User Impersonation", Cryptology and Network Security (CANS 2009), Lecture Notes in Computer Science, vol. 5888, pp. 279-298, DOI 10.1007/978-3-642-10433-6_19, 2009,
https://doi.org/10.1007/978-3-642-10433-6_19. - [Canetti01] Canetti, R., "Universally composable security: A new paradigm for cryptographic protocols", 42nd IEEE Symposium on Foundations of Computer Science, pp. 136-145, DOI 10.1109/SFCS.2001.959888, 2001,
https://doi.org/10.1109/SFCS.2001.959888. - [Cheon06] Cheon, J. H., "Security Analysis of the Strong Diffie-Hellman Problem", Advances in Cryptology - EUROCRYPT 2006, Lecture Notes in Computer Science, vol. 4004, pp. 1-11, DOI 10.1007/11761679_1, 2006,
https://doi.org/10.1007/11761679_1. - [DL24] Dayanikli, D. and A. Lehmann, "(Strong) aPAKE Revisited: Capturing Multi-User Security and Salting", Cryptology ePrint Archive, Paper 2024/756, 2024,
https://eprint.iacr.org/2024/756. - [FIPS202] NIST, "SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions", NIST FIPS 202, DOI 10.6028/NIST.FIPS.202, August 2015,
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf. - [FK00] Ford, W. and B. S. Kaliski, Jr, "Server-assisted generation of a strong secret from a password", IEEE 9th International Workshops on Enabling Technologies: Infrastructure for Collaborative Enterprises (WET ICE 2000), pp. 176-180, DOI 10.1109/ENABL.2000.883724, 2000,
https://doi.org/10.1109/ENABL.2000.883724. - [HJKW23] Hesse, J., Jarecki, S., Krawczyk, H., and C. Wood, "Password-Authenticated TLS via OPAQUE and Post-Handshake Authentication", Advances in Cryptology - EUROCRYPT 2023, Lecture Notes in Computer Science, vol. 14008, pp. 98-127, DOI 10.1007/978-3-031-30589-4_4, 2023,
https://doi.org/10.1007/978-3-031-30589-4_4. - [HMQV] Krawczyk, H., "HMQV: A High-Performance Secure Diffie-Hellman Protocol", Cryptology ePrint Archive, Paper 2005/176, 2005,
https://eprint.iacr.org/2005/176. - [JKKX16] Jarecki, S., Kiayias, A., Krawczyk, H., and J. Xu, "Highly-Efficient and Composable Password-Protected Secret Sharing (Or: How to Protect Your Bitcoin Wallet Online)", 2016 IEEE European Symposium on Security and Privacy (EuroS&P), pp. 276-291, DOI 10.1109/EuroSP.2016.30, 2016,
https://doi.org/10.1109/EuroSP.2016.30. - [JKX18] Jarecki, S., Krawczyk, H., and J. Xu, "OPAQUE: An Asymmetric PAKE Protocol Secure Against Pre-Computation Attacks", Cryptology ePrint Archive, Paper 2018/163, 2018,
https://eprint.iacr.org/2018/163. - [keyagreement] Barker, E., Chen, L., Roginsky, A., Vassilev, A., and R. Davis, "Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography", DOI 10.6028/nist.sp.800-56ar3, NIST SP 800-56Ar3, April 2018,
https://doi.org/10.6028/nist.sp.800-56ar3. - [Krawczyk20] Krawczyk, H., "The OPAQUE Asymmetric PAKE Protocol", Work in Progress, Internet-Draft, draft-krawczyk-cfrg-opaque-06, 19 June 2020,
https://datatracker.ietf.org/doc/html/draft-krawczyk-cfrg-opaque-06. - [LGR20] Len, J., Grubbs, P., and T. Ristenpart, "Partitioning Oracle Attacks", Cryptology ePrint Archive, Paper 2020/1491, 2021,
https://eprint.iacr.org/2020/1491.pdf. - [NISTCurves] NIST, "Digital Signature Standard (DSS)", NIST FIPS 186-5, DOI 10.6028/nist.fips.186-5, 2013,
https://doi.org/10.6028/nist.fips.186-5. - [RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, May 2010,
https://www.rfc-editor.org/info/rfc5869. - [RFC7748] Langley, A., Hamburg, M., and S. Turner, "Elliptic Curves for Security", RFC 7748, DOI 10.17487/RFC7748, January 2016,
https://www.rfc-editor.org/info/rfc7748. - [RFC7914] Percival, C. and S. Josefsson, "The scrypt Password-Based Key Derivation Function", RFC 7914, DOI 10.17487/RFC7914, August 2016,
https://www.rfc-editor.org/info/rfc7914. - [RFC8017] Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch, "PKCS #1: RSA Cryptography Specifications Version 2.2", RFC 8017, DOI 10.17487/RFC8017, November 2016,
https://www.rfc-editor.org/info/rfc8017. - [RFC8018] Moriarty, K., Ed., Kaliski, B., and A. Rusch, "PKCS #5: Password-Based Cryptography Specification Version 2.1", RFC 8018, DOI 10.17487/RFC8018, January 2017,
https://www.rfc-editor.org/info/rfc8018. - [RFC8125] Schmidt, J., "Requirements for Password-Authenticated Key Agreement (PAKE) Schemes", RFC 8125, DOI 10.17487/RFC8125, April 2017,
https://www.rfc-editor.org/info/rfc8125. - [RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
https://www.rfc-editor.org/info/rfc8446. - [RFC9106] Biryukov, A., Dinu, D., Khovratovich, D., and S. Josefsson, "Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications", RFC 9106, DOI 10.17487/RFC9106, September 2021,
https://www.rfc-editor.org/info/rfc9106. - [RFC9180] Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, February 2022,
https://www.rfc-editor.org/info/rfc9180. - [RFC9380] Faz-Hernandez, A., Scott, S., Sullivan, N., Wahby, R. S., and C. A. Wood, "Hashing to Elliptic Curves", RFC 9380, DOI 10.17487/RFC9380, August 2023,
https://www.rfc-editor.org/info/rfc9380. - [RFC9496] de Valence, H., Grigg, J., Hamburg, M., Lovecruft, I., Tankersley, G., and F. Valsorda, "The ristretto255 and decaf448 Groups", RFC 9496, DOI 10.17487/RFC9496, December 2023,
https://www.rfc-editor.org/info/rfc9496. - [SIGMA-I] Krawczyk, H., "SIGMA: The 'SIGn-and-MAc' Approach to Authenticated Diffie-Hellman and its Use in the IKE Protocols", 2003,
https://www.iacr.org/cryptodb/archive/2003/CRYPTO/1495/1495.pdf. - [TOPPSS] Jarecki, S., Kiayias, A., Krawczyk, H., and J. Xu, "TOPPSS: Cost-Minimal Password-Protected Secret Sharing based on Threshold OPRF", Applied Cryptology and Network Security - ACNS 2017, Lecture Notes in Computer Science, vol. 10355, pp. 39-58, DOI 10.1007/978-3-319-61204-1_3, 2017,
https://doi.org/10.1007/978-3-319-61204-1_3. - [TripleDH] Marlinspike, M., "Simplifying OTR deniability", Signal Blog, 27 July 2013,
https://signal.org/blog/simplifying-otr-deniability. - [WhatsAppE2E] WhatsApp, "Security of End-to-End Encrypted Backups", WhatsApp Security Whitepaper, 10 September 2021,
https://www.whatsapp.com/security/WhatsApp_Security_Encrypted_Backups_Whitepaper.pdf.
Appendix A. Alternate Key Recovery Mechanisms πβ¨
Okay so client authentication material can be stored and retrieved using different key recovery mechanisms ποΈ and this is the part where the envelope gets its glow-up π . Any key recovery mechanism that encrypts data in the envelope MUST use an authenticated encryption scheme with random key-robustness (or key-committing) π«Ά. Deviating from the key-robustness requirement may open the protocol to attacks, e.g., [LGR20] π so please don't skip this one, bestie. This specification enforces this property by using a MAC over the envelope contents π₯.
We remark that export_key for authentication or encryption requires no special properties from the authentication or encryption schemes, as long as export_key is used only after authentication material is successfully recovered, i.e., after the MAC in RecoverCredentials passes verification πΈ.
Appendix B. Alternate AKE Instantiations π¦
It is possible to instantiate OPAQUE with other AKEs, such as HMQV [HMQV] and SIGMA-I [SIGMA-I] π. HMQV is similar to 3DH but varies in its key schedule π§. SIGMA-I uses digital signatures rather than static DH keys for authentication β¨. Specification of these instantiations is left to future documents π. A sketch of how these instantiations might change is included in the next subsection for posterity π.
OPAQUE may also be instantiated with any post-quantum (PQ) AKE protocol that has the message flow above and security properties (KCI resistance and forward secrecy) outlined in Section 10 π. Note that such an instantiation is not quantum-safe unless the OPRF is quantum-safe β οΈ. However, an OPAQUE instantiation where the AKE protocol is quantum-safe, but the OPRF is not, would still ensure the confidentiality and integrity of application data encrypted under session_key (or a key derived from it) with a quantum-safe encryption function π. However, the only effect of a break of the OPRF by a future quantum attacker would be the ability of this attacker to run at that time an exhaustive dictionary attack against the old user's password and only for users whose envelopes were harvested while in use (in the case of OPAQUE run over a TLS channel with the server, harvesting such envelopes requires targeted active attacks) ππ«Ά.
B.1. HMQV Instantiation Sketch π
okay so this one is giving "what if we swapped the engine but kept the car" πβ¨ An HMQV instantiation would work similarly to OPAQUE-3DH, differing primarily in the key schedule [HMQV]. First, the key schedule preamble value would use a different constant prefix, "HMQV" instead of "3DH", as shown below. π
preamble = concat("HMQV",
I2OSP(len(client_identity), 2), client_identity,
KE1,
I2OSP(len(server_identity), 2), server_identity,
KE2.credential_response,
KE2.auth_response.server_nonce,
KE2.auth_response.server_public_keyshare)
Second, the IKM derivation would change. Assuming HMQV is instantiated with a cyclic group of prime order p with bit length L, clients would compute IKM as follows: π¦
u' = (eskU + u \* skU) mod p
IKM = (epkS \* pkS^s)^u'
Likewise, servers would compute IKM as follows: πΈ
s' = (eskS + s \* skS) mod p
IKM = (epkU \* pkU^u)^s'
In both cases, u would be computed as follows: π«Ά
hashInput = concat(I2OSP(len(epkU), 2), epkU,
I2OSP(len(info), 2), info,
I2OSP(len("client"), 2), "client")
u = Hash(hashInput) mod L
Likewise, s would be computed as follows: π
hashInput = concat(I2OSP(len(epkS), 2), epkS,
I2OSP(len(info), 2), info,
I2OSP(len("server"), 2), "server")
s = Hash(hashInput) mod L
Hash is the same hash function used in the main OPAQUE protocol for key derivation. Its output length (in bits) must be at least L. β¨
Both parties should perform validation (as in Section 10.7) on each other's public keys before computing the above parameters. π₯
B.2. SIGMA-I Instantiation Sketch
ngl this one is a whole different vibe π A SIGMA-I [SIGMA-I] instantiation differs more drastically from OPAQUE-3DH since authentication uses digital signatures instead of Diffie-Hellman. In particular, both KE2 and KE3 would carry a digital signature, computed using the server and client private keys established during registration, respectively, as well as a MAC, where the MAC is computed as in OPAQUE-3DH but it also covers the identity of the sender. π
The key schedule would also change. Specifically, the key schedule preamble value would use a different constant prefix, "SIGMA-I" instead of "3DH", and the IKM computation would use only the ephemeral public keys exchanged between client and server. π
Appendix C. Test Vectors π§
This section contains real and fake test vectors for the OPAQUE-3DH specification. Each real test vector in Appendix C.1 specifies the configuration information, protocol inputs, intermediate values computed during registration and authentication, and protocol outputs. π
Similarly, each fake test vector in Appendix C.2 specifies the configuration information, protocol inputs, and protocol outputs computed during the authentication of an unknown or unregistered user. Note that masking_key, client_private_key, and client_public_key are used as additional inputs as described in Section 6.3.2.2. client_public_key is used as the fake record's public key, and masking_key is used for the fake record's masking key parameter. π
All values are encoded in hexadecimal strings. The configuration information includes the (OPRF, Hash, KSF, KDF, MAC, Group, Context) tuple, where the Group matches that which is used in the OPRF. The KSF used for each test vector is the identity function (denoted Identity), which returns as output the input message supplied to the function without any modification, i.e., msg = Stretch(msg). β
C.1. Real Test Vectors
C.1.1. OPAQUE-3DH Real Test Vector 1
C.1.1.1. Configuration
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: ristretto255
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.1.1.2. Input Values
oprf_seed: f433d0227b0b9dd54f7c4422b600e764e47fb503f1f9a0f0a47c6606b0
54a7fdc65347f1a08f277e22358bbabe26f823fca82c7848e9a75661f4ec5d5c1989e
f
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d2
3ba7a38dfec
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: 47451a85372f8b3537e249d7b54188091fb18edde78094b43
e2ba42b5eb89f0d
server_public_key: b2fe7af9f48cc502d016729d2fe25cdd433f2c4bc904660b2a
382c9b79df1a78
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb38
0cae6a6cc
client_keyshare_seed: 82850a697b42a505f5b68fcdafce8c31f0af2b581f063cf
1091933541936304b
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: 76cfbfe758db884bebb33582331ba9f159720ca8784a2a070
a265d9c2d6abe01
blind_login: 6ecc102d2e7a7cf49617aad7bbe188556792d4acd60a1a8a8d2b65d4
b0790308
C.1.1.3. Intermediate Values
client_public_key: 76a845464c68a5d2f7e442436bb1424953b17d3e2e289ccbac
cafb57ac5c3675
auth_key: 6cd32316f18d72a9a927a83199fa030663a38ce0c11fbaef82aa9003773
0494fc555c4d49506284516edd1628c27965b7555a4ebfed2223199f6c67966dde822
randomized_password: aac48c25ab036e30750839d31d6e73007344cb1155289fb7
d329beb932e9adeea73d5d5c22a0ce1952f8aba6d66007615cd1698d4ac85ef1fcf15
0031d1435d9
envelope: ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d23ba7a3
8dfec634b0f5b96109c198a8027da51854c35bee90d1e1c781806d07d49b76de6a28b
8d9e9b6c93b9f8b64d16dddd9c5bfb5fea48ee8fd2f75012a8b308605cdd8ba5
handshake_secret: 81263cb85a0cfa12450f0f388de4e92291ec4c7c7a0878b6245
50ff528726332f1298fc6cc822a432c89504347c7a2ccd70316ae3da6a15e0399e6db
3f7c1b12
server_mac_key: 0d36b26cfe38f51f804f0a9361818f32ee1ce2a4e5578653b5271
84af058d3b2d8075c296fd84d24677913d1baa109290cd81a13ed383f9091a3804e65
298dfc
client_mac_key: 91750adbac54a5e8e53b4c233cc8d369fe83b0de1b6a3cd85575e
eb0bb01a6a90a086a2cf5fe75fff2a9379c30ba9049510a33b5b0b1444a88800fc3ee
e2260d
oprf_key: 5d4c6a8b7c7138182afb4345d1fae6a9f18a1744afbcc3854f8f5a2b4b4
c6d05
C.1.1.4. Output Values
registration_request: 5059ff249eb1551b7ce4991f3336205bde44a105a032e74
7d21bf382e75f7a71
registration_response: 7408a268083e03abc7097fc05b587834539065e86fb0c7
b6342fcf5e01e5b019b2fe7af9f48cc502d016729d2fe25cdd433f2c4bc904660b2a3
82c9b79df1a78
registration_upload: 76a845464c68a5d2f7e442436bb1424953b17d3e2e289ccb
accafb57ac5c36751ac5844383c7708077dea41cbefe2fa15724f449e535dd7dd562e
66f5ecfb95864eadddec9db5874959905117dad40a4524111849799281fefe3c51fa8
2785c5ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d23ba7a38dfe
c634b0f5b96109c198a8027da51854c35bee90d1e1c781806d07d49b76de6a28b8d9e
9b6c93b9f8b64d16dddd9c5bfb5fea48ee8fd2f75012a8b308605cdd8ba5
KE1: c4dedb0ba6ed5d965d6f250fbe554cd45cba5dfcce3ce836e4aee778aa3cd44d
da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb380cae6a6cc6e29b
ee50701498605b2c085d7b241ca15ba5c32027dd21ba420b94ce60da326
KE2: 7e308140890bcde30cbcea28b01ea1ecfbd077cff62c4def8efa075aabcbb471
38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6dd6ec6
0bcdb26dc455ddf3e718f1020490c192d70dfc7e403981179d8073d1146a4f9aa1ced
4e4cd984c657eb3b54ced3848326f70331953d91b02535af44d9fedc80188ca46743c
52786e0382f95ad85c08f6afcd1ccfbff95e2bdeb015b166c6b20b92f832cc6df01e0
b86a7efd92c1c804ff865781fa93f2f20b446c8371b671cd9960ecef2fe0d0f749498
6fa3d8b2bb01963537e60efb13981e138e3d4a1c4f62198a9d6fa9170c42c3c71f197
1b29eb1d5d0bd733e40816c91f7912cc4a660c48dae03e57aaa38f3d0cffcfc21852e
bc8b405d15bd6744945ba1a93438a162b6111699d98a16bb55b7bdddfe0fc5608b23d
a246e7bd73b47369169c5c90
KE3: 4455df4f810ac31a6748835888564b536e6da5d9944dfea9e34defb9575fe5e2
661ef61d2ae3929bcf57e53d464113d364365eb7d1a57b629707ca48da18e442
export_key: 1ef15b4fa99e8a852412450ab78713aad30d21fa6966c9b8c9fb3262a
970dc62950d4dd4ed62598229b1b72794fc0335199d9f7fcc6eaedde92cc04870e63f
16
session_key: 42afde6f5aca0cfa5c163763fbad55e73a41db6b41bc87b8e7b62214
a8eedc6731fa3cb857d657ab9b3764b89a84e91ebcb4785166fbb02cedfcbdfda215b
96f
C.1.2. OPAQUE-3DH Real Test Vector 2
C.1.2.1. Configuration
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: ristretto255
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.1.2.2. Input Values
client_identity: 616c696365
server_identity: 626f62
oprf_seed: f433d0227b0b9dd54f7c4422b600e764e47fb503f1f9a0f0a47c6606b0
54a7fdc65347f1a08f277e22358bbabe26f823fca82c7848e9a75661f4ec5d5c1989e
f
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d2
3ba7a38dfec
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: 47451a85372f8b3537e249d7b54188091fb18edde78094b43
e2ba42b5eb89f0d
server_public_key: b2fe7af9f48cc502d016729d2fe25cdd433f2c4bc904660b2a
382c9b79df1a78
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb38
0cae6a6cc
client_keyshare_seed: 82850a697b42a505f5b68fcdafce8c31f0af2b581f063cf
1091933541936304b
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: 76cfbfe758db884bebb33582331ba9f159720ca8784a2a070
a265d9c2d6abe01
blind_login: 6ecc102d2e7a7cf49617aad7bbe188556792d4acd60a1a8a8d2b65d4
b0790308
C.1.2.3. Intermediate Values
client_public_key: 76a845464c68a5d2f7e442436bb1424953b17d3e2e289ccbac
cafb57ac5c3675
auth_key: 6cd32316f18d72a9a927a83199fa030663a38ce0c11fbaef82aa9003773
0494fc555c4d49506284516edd1628c27965b7555a4ebfed2223199f6c67966dde822
randomized_password: aac48c25ab036e30750839d31d6e73007344cb1155289fb7
d329beb932e9adeea73d5d5c22a0ce1952f8aba6d66007615cd1698d4ac85ef1fcf15
0031d1435d9
envelope: ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d23ba7a3
8dfec1ac902dc5589e9a5f0de56ad685ea8486210ef41449cd4d8712828913c5d2b68
0b2b3af4a26c765cff329bfb66d38ecf1d6cfa9e7a73c222c6efe0d9520f7d7c
handshake_secret: 5e723bed1e5276de2503419eba9da61ead573109c4012268323
98c7e08155b885bfe7bc93451f9d887a0c1d0c19233e40a8e47b347a9ac3907f94032
a4cff64f
server_mac_key: dad66bb9251073d17a13f8e5500f36e5998e3cde520ca0738e708
5af62fd97812eb79a745c94d0bf8a6ac17f980cf435504cf64041eeb6bb237796d2c7
f81e9a
client_mac_key: f816fe2914f7c5b29852385615d7c7f31ac122adf202d7ccd4976
06d7aabd48930323d1d02b1cc9ecd456c4de6f46c7950becb18bffd921dd5876381b5
486ffe
oprf_key: 5d4c6a8b7c7138182afb4345d1fae6a9f18a1744afbcc3854f8f5a2b4b4
c6d05
C.1.2.4. Output Values π
okay so this is where the whole thing comes together!! π these are the outputs for vector 2: the registration messages, both KE messages, the export_key, and the session_key. if your implementation matches these bytes, you're doing it right. π
registration_request: 5059ff249eb1551b7ce4991f3336205bde44a105a032e74
7d21bf382e75f7a71
registration_response: 7408a268083e03abc7097fc05b587834539065e86fb0c7
b6342fcf5e01e5b019b2fe7af9f48cc502d016729d2fe25cdd433f2c4bc904660b2a3
82c9b79df1a78
registration_upload: 76a845464c68a5d2f7e442436bb1424953b17d3e2e289ccb
accafb57ac5c36751ac5844383c7708077dea41cbefe2fa15724f449e535dd7dd562e
66f5ecfb95864eadddec9db5874959905117dad40a4524111849799281fefe3c51fa8
2785c5ac13171b2f17bc2c74997f0fce1e1f35bec6b91fe2e12dbd323d23ba7a38dfe
c1ac902dc5589e9a5f0de56ad685ea8486210ef41449cd4d8712828913c5d2b680b2b
3af4a26c765cff329bfb66d38ecf1d6cfa9e7a73c222c6efe0d9520f7d7c
KE1: c4dedb0ba6ed5d965d6f250fbe554cd45cba5dfcce3ce836e4aee778aa3cd44d
da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb380cae6a6cc6e29b
ee50701498605b2c085d7b241ca15ba5c32027dd21ba420b94ce60da326
KE2: 7e308140890bcde30cbcea28b01ea1ecfbd077cff62c4def8efa075aabcbb471
38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6dd6ec6
0bcdb26dc455ddf3e718f1020490c192d70dfc7e403981179d8073d1146a4f9aa1ced
4e4cd984c657eb3b54ced3848326f70331953d91b02535af44d9fea502150b67fe367
95dd8914f164e49f81c7688a38928372134b7dccd50e09f8fed9518b7b2f94835b3c4
fe4c8475e7513f20eb97ff0568a39caee3fd6251876f71cd9960ecef2fe0d0f749498
6fa3d8b2bb01963537e60efb13981e138e3d4a1c4f62198a9d6fa9170c42c3c71f197
1b29eb1d5d0bd733e40816c91f7912cc4a292371e7809a9031743e943fb3b56f51de9
03552fc91fba4e7419029951c3970b2e2f0a9dea218d22e9e4e0000855bb6421aa361
0d6fc0f4033a6517030d4341
KE3: 7a026de1d6126905736c3f6d92463a08d209833eb793e46d0f7f15b3e0f62c76
43763c02bbc6b8d3d15b63250cae98171e9260f1ffa789750f534ac11a0176d5
export_key: 1ef15b4fa99e8a852412450ab78713aad30d21fa6966c9b8c9fb3262a
970dc62950d4dd4ed62598229b1b72794fc0335199d9f7fcc6eaedde92cc04870e63f
16
session_key: ae7951123ab5befc27e62e63f52cf472d6236cb386c968cc47b7e34f
866aa4bc7638356a73cfce92becf39d6a7d32a1861f12130e824241fe6cab34fbd471
a57
C.1.3. OPAQUE-3DH Real Test Vector 3 π¦
next up is vector 3! same family, but it's giving a fresh set of inputs and outputs, so no peeking at the last one. π
C.1.3.1. Configuration
ok the setup for vector 3. it's the same suite as before, so every name and size is the same. β¨
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: curve25519
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.1.3.2. Input Values
these are the starting ingredients: the seeds, nonces, keys and blinds we feed in. π
oprf_seed: a78342ab84d3d30f08d5a9630c79bf311c31ed7f85d9d4959bf492ec67
a0eec8a67dfbf4497248eebd49e878aab173e5e4ff76354288fdd53e949a5f7c9f7f1
b
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: 40d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cc
a9bf44d6e0b
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: c06139381df63bfc91c850db0b9cfbec7a62e86d80040a41a
a7725bf0e79d564
server_public_key: a41e28269b4e97a66468cc00c5a57753e192e1527669897706
88aa90486ef031
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb38
0cae6a6cc
client_keyshare_seed: 82850a697b42a505f5b68fcdafce8c31f0af2b581f063cf
1091933541936304b
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: c575731ffe1cb0ca5ba63b42c4699767b8b9ab78ba39316ee
04baddb2034a70a
blind_login: 6ecc102d2e7a7cf49617aad7bbe188556792d4acd60a1a8a8d2b65d4
b0790308
C.1.3.3. Intermediate Values
the in-between stuff that gets derived along the way. it's a lot, but it's how you check your work step by step. π§
client_public_key: 0936ea94ab030ec332e29050d266c520e916731a052d05ced7
e0cfe751142b48
auth_key: 7e880ab484f750e80e6f839d975aff476070ce65066d85ea62523d1d576
4739d91307fac47186a4ab935e6a5c7f70cb47faa9473311947502c022cc67ae9440c
randomized_password: 3a602c295a9c323d9362fe286f104567ed6862b25dbe30fa
da844f19e41cf40047424b7118e15dc2c1a815a70fea5c8de6c30aa61440cd4b4b5e8
f3963fbb2e1
envelope: 40d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cca9bf44
d6e0b20c1e81fef28e92e897ca8287d49a55075b47c3988ff0fff367d79a3e350ccac
150b4a3ff48b4770c8e84e437b3d4e68d2b95833f7788f7eb93fa6a8afb85ecb
handshake_secret: 178c8c15e025252380c3edb1c6ad8ac52573b38d536099e2f86
5786f5e31c642608550c0c6f281c37ce259667dd72768af31630e0eb36f1096a2e642
1c2aa163
server_mac_key: f3c6a8e069c54bb0d8905139f723c9e22f5c662dc08848243a665
4c8223800019b9823523d84da2ef67ca1c14277630aace464c113be8a0a658c39e181
a8bb71
client_mac_key: b1ee7ce52dbd0ab72872924ff11596cb196bbabfc319e74aca78a
de54a0f74dd15dcf5621f6d2e79161b0c9b701381d494836dedbb86e584a65b34267a
370e01
oprf_key: 62ef7f7d9506a14600c34f642aaf6ef8019cc82a6755db4fded5248ea14
6030a
C.1.3.4. Output Values
and here are the final outputs for vector 3: the registration messages, KE1 through KE3, plus the export_key and session_key. π
registration_request: 26f3dbfd76b8e5f85b4da604f42889a7d4b1bc919f65538
1a67de02c59fd5436
registration_response: 506e8f1b89c098fb89b5b6210a05f7898cafdaea221761
e8d5272fc39e0f9f08a41e28269b4e97a66468cc00c5a57753e192e15276698977068
8aa90486ef031
registration_upload: 0936ea94ab030ec332e29050d266c520e916731a052d05ce
d7e0cfe751142b486d23c6ed818882f9bdfdcf91389fcbc0b7a3faf92bd0bd6be4a1e
7730277b694fc7c6ba327fbe786af18487688e0f7c148bbd54dc2fc80c28e7a976d9e
f53c3540d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cca9bf44d6e0
b20c1e81fef28e92e897ca8287d49a55075b47c3988ff0fff367d79a3e350ccac150b
4a3ff48b4770c8e84e437b3d4e68d2b95833f7788f7eb93fa6a8afb85ecb
KE1: c4dedb0ba6ed5d965d6f250fbe554cd45cba5dfcce3ce836e4aee778aa3cd44d
da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb380cae6a6cc10a83
b9117d3798cb2957fbdb0268a0d63dbf9d66bde5c00c78affd80026c911
KE2: 9a0e5a1514f62e005ea098b0d8cf6750e358c4389e6add1c52aed9500fa19d00
38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6d22cc3
1127d6f0096755be3c3d2dd6287795c317aeea10c9485bf4f419a786642c19a8f151c
eb5e8767d175248c62c017de94057398d28bf0ed00d1b50ee4f812fd9afddf98af8cd
58067ca43b0633b6cadd0e9d987f89623fed4d3583bdf6910c425600e90dab3c6b351
3188a465461a67f6bbc47aeba808f7f7e2c6d66f5c3271cd9960ecef2fe0d0f749498
6fa3d8b2bb01963537e60efb13981e138e3d4a141f55f0bef355cfb34ccd468fdacad
75865ee7efef95f4cb6c25d477f720502676f06a3b806da262139bf3fa76a1090b94d
ac78bc3bc6f8747d5b35acf94eff3ec2ebe7d49b8cf16be64120b279fe92664e47be5
da7e60f08f12e91192652f79
KE3: 550e923829a544496d8316c490da2b979b78c730dd75be3a17f237a26432c19f
bba54b6a0467b1c22ecbd6794bc5fa5b04215ba1ef974c6b090baa42c5bb984f
export_key: 9dec51d6d0f6ce7e4345f10961053713b07310cc2e45872f57bbd2fe5
070fdf0fb5b77c7ddaa2f3dc5c35132df7417ad7fefe0f690ad266e5a54a21d045c9c
38
session_key: fd2fdd07c1bcc88e81c1b1d1de5ad62dfdef1c0b8209ff9d671e1fac
55ce9c34d381c1fb2703ff53a797f77daccbe33047ccc167b8105171e10ec962eea20
3aa
C.1.4. OPAQUE-3DH Real Test Vector 4 π
vector 4 is up next, and it's a bit of a twist: this one adds client and server identities to the mix. π«Ά
C.1.4.1. Configuration
same suite again, so the setup is identical to vector 3. β
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: curve25519
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.1.4.2. Input Values
the inputs now include the client and server identities, which are the main difference from vector 3. π
client_identity: 616c696365
server_identity: 626f62
oprf_seed: a78342ab84d3d30f08d5a9630c79bf311c31ed7f85d9d4959bf492ec67
a0eec8a67dfbf4497248eebd49e878aab173e5e4ff76354288fdd53e949a5f7c9f7f1
b
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: 40d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cc
a9bf44d6e0b
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: c06139381df63bfc91c850db0b9cfbec7a62e86d80040a41a
a7725bf0e79d564
server_public_key: a41e28269b4e97a66468cc00c5a57753e192e1527669897706
88aa90486ef031
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb38
0cae6a6cc
client_keyshare_seed: 82850a697b42a505f5b68fcdafce8c31f0af2b581f063cf
1091933541936304b
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: c575731ffe1cb0ca5ba63b42c4699767b8b9ab78ba39316ee
04baddb2034a70a
blind_login: 6ecc102d2e7a7cf49617aad7bbe188556792d4acd60a1a8a8d2b65d4
b0790308
C.1.4.3. Intermediate Values
the derived values for vector 4. some of these shift now that the identities are in, so don't copy them over from vector 3 by mistake. π
client_public_key: 0936ea94ab030ec332e29050d266c520e916731a052d05ced7
e0cfe751142b48
auth_key: 7e880ab484f750e80e6f839d975aff476070ce65066d85ea62523d1d576
4739d91307fac47186a4ab935e6a5c7f70cb47faa9473311947502c022cc67ae9440c
randomized_password: 3a602c295a9c323d9362fe286f104567ed6862b25dbe30fa
da844f19e41cf40047424b7118e15dc2c1a815a70fea5c8de6c30aa61440cd4b4b5e8
f3963fbb2e1
envelope: 40d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cca9bf44
d6e0bb4c0eab6143959a650c5f6b32acf162b1fbe95bb36c5c4f99df53865c4d3537d
69061d80522d772cd0efdbe91f817f6bf7259a56e20b4eb9cbe9443702f4b759
handshake_secret: 13e7dc6afa5334b9dfffe26bee3caf744ef4add176caee464cd
eb3d37303b90de35a8bf095df84471ac77d705f12fe232f1571de1d6a001d3e808998
73a142dc
server_mac_key: a58135acfb2bde92d506cf59119729a6404ad94eba294e4b52a63
baf58cfe03f21bcf735222c7f2c27a60bd958be7f6aed50dc03a78f64e7ae4ac1ff07
1b95aa
client_mac_key: 1e1a8ba156aadc4a302f707d2193c9dab477b355f430d450dd407
ce40dc75613f76ec33dec494f8a6bfdcf951eb060dac33e6572c693954fe92e33730c
9ab0a2
oprf_key: 62ef7f7d9506a14600c34f642aaf6ef8019cc82a6755db4fded5248ea14
6030a
C.1.4.4. Output Values
the final outputs for vector 4, with the identities baked in. this is the one to check against if your identity handling is giving you grief. π₯
registration_request: 26f3dbfd76b8e5f85b4da604f42889a7d4b1bc919f65538
1a67de02c59fd5436
registration_response: 506e8f1b89c098fb89b5b6210a05f7898cafdaea221761
e8d5272fc39e0f9f08a41e28269b4e97a66468cc00c5a57753e192e15276698977068
8aa90486ef031
registration_upload: 0936ea94ab030ec332e29050d266c520e916731a052d05ce
d7e0cfe751142b486d23c6ed818882f9bdfdcf91389fcbc0b7a3faf92bd0bd6be4a1e
7730277b694fc7c6ba327fbe786af18487688e0f7c148bbd54dc2fc80c28e7a976d9e
f53c3540d6b67fdd7da7c49894750754514dbd2070a407166bd2a5237cca9bf44d6e0
bb4c0eab6143959a650c5f6b32acf162b1fbe95bb36c5c4f99df53865c4d3537d6906
1d80522d772cd0efdbe91f817f6bf7259a56e20b4eb9cbe9443702f4b759
KE1: c4dedb0ba6ed5d965d6f250fbe554cd45cba5dfcce3ce836e4aee778aa3cd44d
da7e07376d6d6f034cfa9bb537d11b8c6b4238c334333d1f0aebb380cae6a6cc10a83
b9117d3798cb2957fbdb0268a0d63dbf9d66bde5c00c78affd80026c911
KE2: 9a0e5a1514f62e005ea098b0d8cf6750e358c4389e6add1c52aed9500fa19d00
38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6d22cc3
1127d6f0096755be3c3d2dd6287795c317aeea10c9485bf4f419a786642c19a8f151c
eb5e8767d175248c62c017de94057398d28bf0ed00d1b50ee4f812699bff7663be3c5
d59de94d8e7e58817c7da005b39c25d25555c929e1c5cf6c1b82837b1367c839aab56
a422c0d97719426a79a16f9869cf852100597b23b5a071cd9960ecef2fe0d0f749498
6fa3d8b2bb01963537e60efb13981e138e3d4a141f55f0bef355cfb34ccd468fdacad
75865ee7efef95f4cb6c25d477f72050267cc22c87edbf3ecaca64cb33bc60dc3bfc5
51e365f0d46a7fed0e09d96f9afbb48868f5bb3c3e05a86ed8c9476fc22c58306c5a2
91be34388e09548ba9d70f39
KE3: d16344e791c3f18594d22ba068984fa18ec1e9bead662b75f66826ffd627932f
cd1ec40cd01dcf5f63f4055ebe45c7717a57a833aad360256cf1e1c20c0eae1c
export_key: 9dec51d6d0f6ce7e4345f10961053713b07310cc2e45872f57bbd2fe5
070fdf0fb5b77c7ddaa2f3dc5c35132df7417ad7fefe0f690ad266e5a54a21d045c9c
38
session_key: f6116d3aa0e4089a179713bad4d98ed5cb57e5443cae8d36ef78996f
a60f3dc6e9fcdd63c001596b06dbc1285d80211035cc0e485506b3f7a650cbf78c5bf
fc9
C.1.5. OPAQUE-3DH Real Test Vector 5 π
last one in this part! vector 5 switches things up: it uses the P256 suite instead of ristretto255 and curve25519, so the sizes change too. π¦
C.1.5.1. Configuration
here's the P256 setup. notice that the hash, KDF, MAC and group are all different from the earlier vectors, and so are the byte lengths. πΈ
OPRF: P256-SHA256
Hash: SHA256
KSF: Identity
KDF: HKDF-SHA256
MAC: HMAC-SHA256
Group: P256_XMD:SHA-256_SSWU_RO_
Context: 4f50415155452d504f43
Nh: 32
Npk: 33
Nsk: 32
Nm: 32
Nx: 32
Nok: 32
C.1.5.2. Input Values β¨
okay bestie, these are the inputs π every single one is a hex string, so copy them exactly π
oprf_seed: 62f60b286d20ce4fd1d64809b0021dad6ed5d52a2c8cf27ae6582543a0
a8dce2
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: a921f2a014513bd8a90e477a629794e89fec12d12206dde662ebd
cf65670e51f
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: c36139381df63bfc91c850db0b9cfbec7a62e86d80040a41a
a7725bf0e79d5e5
server_public_key: 035f40ff9cf88aa1f5cd4fe5fd3da9ea65a4923a5594f84fd9
f2092d6067784874
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: ab3d33bde0e93eda72392346a7a73051110674bbf6b1b7ffab8be4f
91fdaeeb1
client_keyshare_seed: 633b875d74d1556d2a2789309972b06db21dfcc4f5ad51d
7e74d783b7cfab8dc
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: 411bf1a62d119afe30df682b91a0a33d777972d4f2daa4b34
ca527d597078153
blind_login: c497fddf6056d241e6cf9fb7ac37c384f49b357a221eb0a802c989b9
942256c1
C.1.5.3. Intermediate Values πΈ
the intermediate values are where the magic gets cooked π₯ same deal, match them character for character
client_public_key: 03b218507d978c3db570ca994aaf36695a731ddb2db272c817
f79746fc37ae5214
auth_key: 5bd4be1602516092dc5078f8d699f5721dc1720a49fb80d8e5c16377abd
0987b
randomized_password: 06be0a1a51d56557a3adad57ba29c5510565dcd8b5078fa3
19151b9382258fb0
envelope: a921f2a014513bd8a90e477a629794e89fec12d12206dde662ebdcf6567
0e51fad30bbcfc1f8eda0211553ab9aaf26345ad59a128e80188f035fe4924fad67b8
handshake_secret: 83a932431a8f25bad042f008efa2b07c6cd0faa8285f335b636
3546a9f9b235f
server_mac_key: 13e928581febfad28855e3e7f03306d61bd69489686f621535d44
a1365b73b0d
client_mac_key: afdc53910c25183b08b930e6953c35b3466276736d9de2e9c5efa
f150f4082c5
oprf_key: 2dfb5cb9aa1476093be74ca0d43e5b02862a05f5d6972614d7433acdc66
f7f31
C.1.5.4. Output Values π
and the final outputs, the ones that actually matter π
registration_request: 029e949a29cfa0bf7c1287333d2fb3dc586c41aa652f507
0d26a5315a1b50229f8
registration_response: 0350d3694c00978f00a5ce7cd08a00547e4ab5fb5fc2b2
f6717cdaa6c89136efef035f40ff9cf88aa1f5cd4fe5fd3da9ea65a4923a5594f84fd
9f2092d6067784874
registration_upload: 03b218507d978c3db570ca994aaf36695a731ddb2db272c8
17f79746fc37ae52147f0ed53532d3ae8e505ecc70d42d2b814b6b0e48156def71ea0
29148b2803aafa921f2a014513bd8a90e477a629794e89fec12d12206dde662ebdcf6
5670e51fad30bbcfc1f8eda0211553ab9aaf26345ad59a128e80188f035fe4924fad6
7b8
KE1: 037342f0bcb3ecea754c1e67576c86aa90c1de3875f390ad599a26686cdfee6e
07ab3d33bde0e93eda72392346a7a73051110674bbf6b1b7ffab8be4f91fdaeeb1022
ed3f32f318f81bab80da321fecab3cd9b6eea11a95666dfa6beeaab321280b6
KE2: 0246da9fe4d41d5ba69faa6c509a1d5bafd49a48615a47a8dd4b0823cc147648
1138fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6d2f0
c547f70deaeca54d878c14c1aa5e1ab405dec833777132eea905c2fbb12504a67dcbe
0e66740c76b62c13b04a38a77926e19072953319ec65e41f9bfd2ae26837b6ce688bf
9af2542f04eec9ab96a1b9328812dc2f5c89182ed47fead61f09f71cd9960ecef2fe0
d0f7494986fa3d8b2bb01963537e60efb13981e138e3d4a103c1701353219b53acf33
7bf6456a83cefed8f563f1040b65afbf3b65d3bc9a19b50a73b145bc87a157e8c58c0
342e2047ee22ae37b63db17e0a82a30fcc4ecf7b
KE3: e97cab4433aa39d598e76f13e768bba61c682947bdcf9936035e8a3a3ebfb66e
export_key: c3c9a1b0e33ac84dd83d0b7e8af6794e17e7a3caadff289fbd9dc769a
853c64b
session_key: 484ad345715ccce138ca49e4ea362c6183f0949aaaa1125dc3bc3f80
876e7cd1
C.1.6. OPAQUE-3DH Real Test Vector 6 π¦
C.1.6.1. Configuration
OPRF: P256-SHA256
Hash: SHA256
KSF: Identity
KDF: HKDF-SHA256
MAC: HMAC-SHA256
Group: P256_XMD:SHA-256_SSWU_RO_
Context: 4f50415155452d504f43
Nh: 32
Npk: 33
Nsk: 32
Nm: 32
Nx: 32
Nok: 32
C.1.6.2. Input Values
client_identity: 616c696365
server_identity: 626f62
oprf_seed: 62f60b286d20ce4fd1d64809b0021dad6ed5d52a2c8cf27ae6582543a0
a8dce2
credential_identifier: 31323334
password: 436f7272656374486f72736542617474657279537461706c65
envelope_nonce: a921f2a014513bd8a90e477a629794e89fec12d12206dde662ebd
cf65670e51f
masking_nonce: 38fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80
f612fdfc6d
server_private_key: c36139381df63bfc91c850db0b9cfbec7a62e86d80040a41a
a7725bf0e79d5e5
server_public_key: 035f40ff9cf88aa1f5cd4fe5fd3da9ea65a4923a5594f84fd9
f2092d6067784874
server_nonce: 71cd9960ecef2fe0d0f7494986fa3d8b2bb01963537e60efb13981e
138e3d4a1
client_nonce: ab3d33bde0e93eda72392346a7a73051110674bbf6b1b7ffab8be4f
91fdaeeb1
client_keyshare_seed: 633b875d74d1556d2a2789309972b06db21dfcc4f5ad51d
7e74d783b7cfab8dc
server_keyshare_seed: 05a4f54206eef1ba2f615bc0aa285cb22f26d1153b5b40a
1e85ff80da12f982f
blind_registration: 411bf1a62d119afe30df682b91a0a33d777972d4f2daa4b34
ca527d597078153
blind_login: c497fddf6056d241e6cf9fb7ac37c384f49b357a221eb0a802c989b9
942256c1
C.1.6.3. Intermediate Values
client_public_key: 03b218507d978c3db570ca994aaf36695a731ddb2db272c817
f79746fc37ae5214
auth_key: 5bd4be1602516092dc5078f8d699f5721dc1720a49fb80d8e5c16377abd
0987b
randomized_password: 06be0a1a51d56557a3adad57ba29c5510565dcd8b5078fa3
19151b9382258fb0
envelope: a921f2a014513bd8a90e477a629794e89fec12d12206dde662ebdcf6567
0e51f4d7773a36a208a866301dbb2858e40dc5638017527cf91aef32d3848eebe0971
handshake_secret: 80bdcc498f22de492e90ee8101fcc7c101e158dd49c77f7c283
816ae329ed62f
server_mac_key: 0f82432fbdb5b90daf27a91a3acc42299a9590dba1b77932c2207
b4cb3d4a157
client_mac_key: 7f629eb0b1b69979b07ca1f564b3e92ed22f07569fd1d11725d93
e46731fbe71
oprf_key: 2dfb5cb9aa1476093be74ca0d43e5b02862a05f5d6972614d7433acdc66
f7f31
C.1.6.4. Output Values
registration_request: 029e949a29cfa0bf7c1287333d2fb3dc586c41aa652f507
0d26a5315a1b50229f8
registration_response: 0350d3694c00978f00a5ce7cd08a00547e4ab5fb5fc2b2
f6717cdaa6c89136efef035f40ff9cf88aa1f5cd4fe5fd3da9ea65a4923a5594f84fd
9f2092d6067784874
registration_upload: 03b218507d978c3db570ca994aaf36695a731ddb2db272c8
17f79746fc37ae52147f0ed53532d3ae8e505ecc70d42d2b814b6b0e48156def71ea0
29148b2803aafa921f2a014513bd8a90e477a629794e89fec12d12206dde662ebdcf6
5670e51f4d7773a36a208a866301dbb2858e40dc5638017527cf91aef32d3848eebe0
971
KE1: 037342f0bcb3ecea754c1e67576c86aa90c1de3875f390ad599a26686cdfee6e
07ab3d33bde0e93eda72392346a7a73051110674bbf6b1b7ffab8be4f91fdaeeb1022
ed3f32f318f81bab80da321fecab3cd9b6eea11a95666dfa6beeaab321280b6
KE2: 0246da9fe4d41d5ba69faa6c509a1d5bafd49a48615a47a8dd4b0823cc147648
1138fe59af0df2c79f57b8780278f5ae47355fe1f817119041951c80f612fdfc6d2f0
c547f70deaeca54d878c14c1aa5e1ab405dec833777132eea905c2fbb12504a67dcbe
0e66740c76b62c13b04a38a77926e19072953319ec65e41f9bfd2ae268d7f10604202
1c80300e4c6f585980cf39fc51a4a6bba41b0729f9b240c729e5671cd9960ecef2fe0
d0f7494986fa3d8b2bb01963537e60efb13981e138e3d4a103c1701353219b53acf33
7bf6456a83cefed8f563f1040b65afbf3b65d3bc9a19b84922c7e5d074838a8f27859
2c53f61fb59f031e85ad480c0c71086b871e1b24
KE3: 46833578cee137775f6be3f01b80748daac5a694101ad0e9e7025480552da56a
export_key: c3c9a1b0e33ac84dd83d0b7e8af6794e17e7a3caadff289fbd9dc769a
853c64b
session_key: 27766fabd8dd88ff37fbd0ef1a491e601d10d9f016c2b28c4bd1b0fb
7511a3c3
C.2. Fake Test Vectors π§
C.2.1. OPAQUE-3DH Fake Test Vector 1 π
C.2.1.1. Configuration
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: ristretto255
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.2.1.2. Input Values
client_identity: 616c696365
server_identity: 626f62
oprf_seed: 743fc168d1f826ad43738933e5adb23da6fb95f95a1b069f0daa0522d0
a78b617f701fc6aa46d3e7981e70de7765dfcd6b1e13e3369a582eb8dc456b10aa53b
0
credential_identifier: 31323334
masking_nonce: 9c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c61
9e27b6e5a6
client_private_key: 2b98980aa95ab53a0f39f0291903d2fdf04b00c167f081416
9922df873002409
client_public_key: 84f43f9492e19c22d8bdaa4447cc3d4db1cdb5427a9f852c47
07921212c36251
server_private_key: c788585ae8b5ba2942b693b849be0c0426384e41977c18d2e
81fbe30fd7c9f06
server_public_key: 825f832667480f08b0c9069da5083ac4d0e9ee31b49c4e0310
031fea04d52966
server_nonce: 1e10f6eeab2a7a420bf09da9b27a4639645622c46358de9cf7ae813
055ae2d12
client_keyshare_seed: a270dc715dc2b4612bc7864312a05c3e9788ee1bad1f276
d1e15bdeb4c355e94
server_keyshare_seed: 360b0937f47d45f6123a4d8f0d0c0814b6120d840ebb8bc
5b4f6b62df07f78c2
masking_key: 39ebd51f0e39a07a1c2d2431995b0399bca9996c5d10014d6ebab445
3dc10ce5cef38ed3df6e56bfff40c2d8dd4671c2b4cf63c3d54860f31fe40220d690b
b71
KE1: b0a26dcaca2230b8f5e4b1bcab9c84b586140221bb8b2848486874b0be448905
42d4e61ed3f8d64cdd3b9d153343eca15b9b0d5e388232793c6376bd2d9cfd0ab641d
7f20a245a09f1d4dbb6e301661af7f352beb0791d055e48d3645232f77f
C.2.1.3. Output Values
KE2: 928f79ad8df21963e91411b9f55165ba833dea918f441db967cdc09521d22925
9c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c619e27b6e5a632b5a
b1bff96636144faa4f9f9afaac75dd88ea99cf5175902ae3f3b2195693f165f11929b
a510a5978e64dcdabecbd7ee1e4380ce270e58fea58e6462d92964a1aaef72698bca1
c673baeb04cc2bf7de5f3c2f5553464552d3a0f7698a9ca7f9c5e70c6cb1f706b2f17
5ab9d04bbd13926e816b6811a50b4aafa9799d5ed7971e10f6eeab2a7a420bf09da9b
27a4639645622c46358de9cf7ae813055ae2d1298251c5ba55f6b0b2d58d9ff0c88fe
4176484be62a96db6e2a8c4d431bd1bf27fe6c1d0537603835217d42ebf7b25819827
32e74892fd28211b31ed33863f0beaf75ba6f59474c0aaf9d78a60a9b2f4cd24d7ab5
4131b3c8efa192df6b72db4c
C.2.2. OPAQUE-3DH Fake Test Vector 2 π
C.2.2.1. Configuration
OPRF: ristretto255-SHA512
Hash: SHA512
KSF: Identity
KDF: HKDF-SHA512
MAC: HMAC-SHA512
Group: curve25519
Context: 4f50415155452d504f43
Nh: 64
Npk: 32
Nsk: 32
Nm: 64
Nx: 64
Nok: 32
C.2.2.2. Input Values
client_identity: 616c696365
server_identity: 626f62
oprf_seed: 66e650652a8266b2205f31fdd68adeb739a05b5e650b19e7edc75e734a
1296d6088188ca46c31ae8ccbd42a52ed338c06e53645387a7efbc94b6a0449526155
e
credential_identifier: 31323334
masking_nonce: 9c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c61
9e27b6e5a6
client_private_key: 288bf63470199221847bb035d99f96531adf8badd14cb1571
b48f7a506649660
client_public_key: 3c64a3153854cc9f0c23aab3c1a19106ec8bab4730736d1d00
3880a1d5a59005
server_private_key: 30fbe7e830be1fe8d2187c97414e3826040cbe49b893b6422
9bab5e85a588846
server_public_key: 78b3040047ff26572a7619617601a61b9c81899bee92f00cfc
aa5eed96863555
server_nonce: 1e10f6eeab2a7a420bf09da9b27a4639645622c46358de9cf7ae813
055ae2d12
client_keyshare_seed: a270dc715dc2b4612bc7864312a05c3e9788ee1bad1f276
d1e15bdeb4c355e94
server_keyshare_seed: 360b0937f47d45f6123a4d8f0d0c0814b6120d840ebb8bc
5b4f6b62df07f78c2
masking_key: 79ad2621b0757a447dff7108a8ae20a068ce67872095620f415ea611
c9dcc04972fa359538cd2fd6528775ca775487b2b56db642049b8a90526b975a38484
c6a
KE1: b0a26dcaca2230b8f5e4b1bcab9c84b586140221bb8b2848486874b0be448905
42d4e61ed3f8d64cdd3b9d153343eca15b9b0d5e388232793c6376bd2d9cfd0ac059b
7ba2aec863933ae48816360c7a9022e83d822704f3b0b86c0502a66e574
C.2.2.3. Output Values β¨
KE2: 6606b6fedbb33f19a81a1feb5149c600fe77252f58acd3080d7504d3dad4922f
9c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c619e27b6e5a67db39
8c0f65d8c298eac430abdae4c80e82b552fb940c00f0cbcea853c0f96c1c15099f3d4
b0e83ecc249613116d605b8d77bb68bdf76994c2bc507e2dcae4176f00afed68ad25c
f3040a0e991acece31ca532117f5c12816997372ff031ad04ebcdce06c501da24e7b4
db95343456e2ed260895ec362694230a1fa20e24a9c71e10f6eeab2a7a420bf09da9b
27a4639645622c46358de9cf7ae813055ae2d122d9055eb8f83e1b497370adad5cc2a
417bf9be436a792def0c7b7ccb92b9e275d7c663104ea4655bd70570d975c05351655
d55fbfb392286edb55600a23b55ce18f8c60e0d1960c960412dd08eabc81ba7ca8ae2
b04aad65462321f51c298010
C.2.3. Fake Test Vector 3 π
C.2.3.1. Configuration
OPRF: P256-SHA256
Hash: SHA256
KSF: Identity
KDF: HKDF-SHA256
MAC: HMAC-SHA256
Group: P256_XMD:SHA-256_SSWU_RO_
Context: 4f50415155452d504f43
Nh: 32
Npk: 33
Nsk: 32
Nm: 32
Nx: 32
Nok: 32
C.2.3.2. Input Values πΈ
client_identity: 616c696365
server_identity: 626f62
oprf_seed: bb1cd59e16ac09bc0cb6d528541695d7eba2239b1613a3db3ade77b362
80f725
credential_identifier: 31323334
masking_nonce: 9c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c61
9e27b6e5a6
client_private_key: d423b87899fc61d014fc8330a4e26190fcfa470a3afe59243
24294af7dbbc1dd
client_public_key: 03b81708eae026a9370616c22e1e8542fe9dbebd36ce8a2661
b708e9628f4a57fc
server_private_key: 34fbe7e830be1fe8d2187c97414e3826040cbe49b893b6422
9bab5e85a5888c7
server_public_key: 0221e034c0e202fe883dcfc96802a7624166fed4cfcab4ae30
cf5f3290d01c88bf
server_nonce: 1e10f6eeab2a7a420bf09da9b27a4639645622c46358de9cf7ae813
055ae2d12
client_keyshare_seed: a270dc715dc2b4612bc7864312a05c3e9788ee1bad1f276
d1e15bdeb4c355e94
server_keyshare_seed: 360b0937f47d45f6123a4d8f0d0c0814b6120d840ebb8bc
5b4f6b62df07f78c2
masking_key: caecc6ccb4cae27cb54d8f3a1af1bac52a3d53107ce08497cdd362b1
992e4e5e
KE1: 0396875da2b4f7749bba411513aea02dc514a48d169d8a9531bd61d3af3fa9ba
ae42d4e61ed3f8d64cdd3b9d153343eca15b9b0d5e388232793c6376bd2d9cfd0a021
47a6583983cc9973b5082db5f5070890cb373d70f7ac1b41ed2305361009784
C.2.3.3. Output Values π¦
KE2: 0201198dcd13f9792eb75dcfa815f61b049abfe2e3e9456d4bbbceec5f442efd
049c035896a043e70f897d87180c543e7a063b83c1bb728fbd189c619e27b6e5a6fac
da65ce0a97b9085e7af07f61fd3fdd046d257cbf2183ce8766090b8041a8bf28d79dd
4c9031ddc75bb6ddb4c291e639937840e3d39fc0d5a3d6e7723c09f7945df485bcf9a
efe3fe82d149e84049e259bb5b33d6a2ff3b25e4bfb7eff0962821e10f6eeab2a7a42
0bf09da9b27a4639645622c46358de9cf7ae813055ae2d12023f82bbb24e75b8683fd
13b843cd566efae996cd0016cffdcc24ee2bc937d026f80144878749a69565b433c10
40aff67e94f79345de888a877422b9bbe21ec329
Acknowledgments π
We are so grateful to the OPAQUE reviewers during CFRG's aPAKE selection process, especially Julia Hesse and Bjorn Tackmann π. This document got better thanks to comments from a bunch of people. Special shoutout to Richard Barnes, Dan Brown, Matt Campagna, Eric Crockett, Paul Grubbs, Fredrik Kuivinen, Stefan Marsiske, Payman Mohassel, Marta Mularczyk, Jason Resch, Greg Rubin, and Nick Sullivan π«Ά. Hugo Krawczyk wants to thank his OPAQUE co-authors Stas Jarecki and Jiayu Xu, because honestly, this work would not have happened without them πβ¨.
Authors' Addresses π
Daniel Bourdrez Email: d@bytema.re
Hugo Krawczyk AWS Email: hugokraw@gmail.com
Kevin Lewi Meta Email: lewi.kevin.k@gmail.com
Christopher A. Wood Cloudflare, Inc. Email: caw@heapingbits.net
π¦ where this page comes from
- ietf: rfc9807 2025-07
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms