In a previous blog post, we took a look at several key exchange protocols, including the classic Diffie-Hellman Key Exchange protocol, the station-to-station protocol, and Secure Remote Password. These protocols were generally tried and tested across decades, but their age also means that improvements accrued across the decades are not included in these legacy protocols. Thus the need for a modern, Augmented Password-Authenticated Key Exchange (aPAKE) protocol was born. One of these state-of-the-art aPAKE protocols is OPAQUE, first introduced in 2018 and formally specified in Request for Comments (RFC) 9807 in 2025. How does it work? Let’s find out!
A Look Back On Secure Remote Password (SRP)
Let’s recap on what SRP is trying to accomplish. Two friends, Alice and Bob, are talking to each other. Alice wants to send a message to Bob, but doesn’t want anyone else from reading it. Thus they would need to have a common key (a symmetric key) for encrypting and decrypting data. Alice would also want to verify that she is indeed talking to Bob and not someone else.
These two desiderata were achieved by the SRP protocol, which was first introduced in 1997. However, even the latest version, SRP-6a (as defined by RFC5054 in 2007), is not perfect.
- “Security Through Legacy”: In short, we assume that SRP is secure because we haven’t found anything wrong with it. SRP was developed in a time before Universal Composability became the norm, and so we haven’t truly analysed it under adversarial network conditions.
- Vulnerability to Server Compromise: Recall in SRP that a verifier value is stored on the server. In effect, this verifier value still acts as a ‘password’ of sorts; an attacker that has precomputed all possible values of can still recover the secret key .
- Flexibility: SRP-6a is heavily tied to modular operations on integers and cannot harness modern developments such as elliptic curve technologies. In fact, if the Discrete Logarithm Problem (explained further below) is solved by a quantum computer, all Diffie-Hellman-related protocols (including SRP) would be trivially broken and no longer secure.
What we want for a modern aPAKE protocol is to address all of these concerns — where the protocol’s security has been rigorously examined and vetted, is resistant to attacks in case of a server compromise, and grants greater flexibility in its underlying operations in case a pivot to post-quantum cryptographic techniques is warranted.
Triple Diffie-Hellman (3DH)
The first thing that needs replacing within SRP is the key exchange mechanism. As previously mentioned, the SRP key exchange mechanism is derived from Diffie-Hellman Key Exchange (DHKE). The fundamental principle that DHKE relied on is the observation that
for certain structures, called groups. Traditional DHKE uses the multiplicative group of integers modulo as the underlying group, where is a prime, but the principle of DHKE extends beyond just numbers. All we need is a cyclic group, which is a group where all the elements are simply ‘exponents’1 of (at least) one element , called a generator. To put it more formally, for any cyclic group , there is an element such that for any element in , say , we are able to find a corresponding integer such that . Crucially, trying to reverse this process, i.e. to find given the group and , is difficult. This is called the Discrete Logarithm Problem2.
Now let us recall how DHKE works. Suppose Alice and Bob wants to derive a shared key. They first agree, publicly, on a cyclic group to use and a generator .
- Alice randomly chooses a positive integer . Bob does the same, choosing .
- Alice computes and sends it to Bob over the public channel. Bob computes and sends it to Alice.
- Alice computes and Bob computes .
Observe that both Alice and Bob derive the same secret key at the end of the DHKE protocol. They can then use to encrypt messages between each other going forward. This is almost perfect, if not for the fact that a malicious actor (say, Mallory) could perform a man-in-the-middle (MiTM) attack and read the messages.
In this case, Mallory simply intercepts the key exchange messages of Alice and Bob and sets up two encrypted channels: one between Alice and Mallory and one between Mallory and Bob. To Alice and Bob, it would seem as though they are still communicating over an encrypted channel, but in reality their messages are being read by a third party.
There are several solutions to this problem, such as the Station-to-Station protocol described in the previous blog post. Here we will use another solution — triple Diffie-Hellman (3DH). It is almost the same as regular DHKE, except that we introduce public keys into the mix: Alice has a private key and a public key , and Bob has a private key with public key . Let’s ignore how Alice and Bob share their public keys with each other beforehand3; how does the 3DH key exchange work?
- Alice and Bob choose positive integers and respectively, called the secret keyshares.
- Alice computes and sends it to Bob over the public channel. Bob computes and sends it to Alice. The values and are called the public keyshares.
- Alice and Bob then compute the ‘shared values’ as follows.
- The first shared value is akin to the DHKE shared value. Alice computes and Bob computes .
- The second shared value checks if Bob can successfully prove that he is, indeed, Bob. While Alice computes using Bob’s public key, Bob has to use his private key to compute . That way .
- The third shared value is similar to the second, but now it’s Alice who needs to prove who she is. Alice computes while Bob computes ; both sides are the same because .
- With all the shared values obtained, a Key Derivation Function (KDF) is used to ‘combine’ , , and alongside all public values , , , and into a single shared key4, say .
Let’s look at a simple demonstration of a 3DH key exchange in Python. Here we use the multiplicative group of integers modulo 23 (i.e. ) with the generator being . Note that a function call like pow(a, b, m) means , i.e. raise to the power of modulo .
In the end you can see that the dh1, dh2, and dh3 values are the same for Alice (on the left) and Bob (on the right). Note that 3DH successfully prevents a MiTM attack since Mallory can’t recover the private key of either Alice or Bob and impersonate them. In fact, 3DH is a bilaterally authenticated key exchange mechanism, allowing both sides to verify their identities and derive a common secret for encryption.
…Don’t We Want Passwords?
Obviously, 3DH doesn’t work with passwords. But what we want is a Password-Authenticated Key Exchange protocol! It’s fine for the server to keep using a private/public key construction, but how do we get the client to generate the private and public keys to use in 3DH from a password?
A fairly obvious approach would be just to take the user’s password, pass it through a KDF5 to generate the client’s private key , and generate the public key . We then send the public key to the server for use in 3DH. What’s wrong with this? Well, since the client public key is (by definition) public, anyone can see what is. A malicious attacker could record down the value of , precompute all possible public keys, and then seeing which password matches. This rainbow table attack means that anyone who obtains the public key of the client (which is everyone) could try and crack the client’s password without first breaching the server.
What we want is to somehow make this key derivation process rely on a secret value that the server (and only the server) has. But the server can’t send this value to the client (as the channel is still not secure and thus could leak this secret value) and the client can’t send its password to the server for processing (as that’ll just leak the client’s password). We need a way to protect the client’s password, send it to the server to include its secret value, before sending it back to the client for use in a KDF.
What could we use to achieve this?
Oblivious Pseudorandom Functions (OPRFs)
Introducing OPRFs! These functions help us solve the problem of trying to mix a server secret into the password-based key derivation process.
Before we look into the “oblivious” part of OPRFs, what is a Pseudorandom Function (PRF)? Essentially, a PRF looks like a function that generates random outputs, except that the same input generates the same output. Except, it is also fed a secret key (or seed) so that the outputs of the PRF are the same for the same inputs.
To put it more precisely,
A Pseudorandom Function (PRF) is a function taking a seed and input such that, for any chosen from a uniformly random distribution, the function is indistinguishable from a function sampling uniformly from the image of .
In practice, encryption functions such as AES could be used as PRFs: just define (where is the AES key) and you’ve got a PRF. However, that’s not how we are going to create an Oblivious PRF. For an OPRF, the seed is held by one party (e.g. the server) while the input is held by another (e.g., the client). So we can’t use the AES trick to compute the OPRF output as that’ll involve one party needing both the seed and the input. We need to do something cleverer.
Let’s go back to our good friend: the cyclic groups. Due to the Discrete Logarithm Problem, we know that finding given is hard. Assuming that we choose uniformly randomly, then would also be uniformly random. Also, if we make the size of the cyclic group a prime, we can utilise the following interesting fact6:
Let denote the set of integers . Then for any non-zero there exists , known as the multiplicative inverse of , such that . We may abuse notation and write to denote the multiplicative inverse of .
This insight allows us to conceptually create an OPRF with seed and input within a cyclic group of prime size as follows:
- Convert into a group element .
- Randomly take a non-zero , called the blind.
- Generate the blinded element and send it to the server.
- On the server, assuming is an integer, compute and send it back to the client. Note that .
- Unblind the blinded element by finding and computing . Note that . This can then be converted into a more ‘suitable’ form (e.g., by converting into bytes) for further processing.
Let’s see an OPRF in action using the group of integers under addition modulo 23. In such a group the standard notation like means (i.e., multiplied by ). Let’s again use Python for demonstration; note that
- the expression
randint(1, p-1)means to generate a random integer from 1 to inclusive; - an expression like
(b * x) % pmeans ; and - the expression
pow(b, -1, p)is to find , i.e. the multiplicative inverse of modulo .
In the end you can see that the client’s OPRF output is the same as someone who has both the OPRF input and seed.
Also observe that, throughout this process, the server never sees the input (or ) and the client never sees the seed . Thus this is, in theory, an OPRF. In fact, this is largely how RFC9497 defines the OPRF on cyclic groups with a prime size (like the group of integers under addition modulo 23). Of course, there are some nuances to make this an actual, standard worthy OPRF, which are covered in the aforementioned RFC.
OPAQUE
Now that we understand how the 3DH key exchange mechanism works and how an OPRF could be used to include a server secret into a key derivation operation, we can finally take a look at the OPAQUE protocol. Essentially, it is an aPAKE that uses an OPRF, hence the name OPAQUE7.
Remember how 3DH almost worked except for the part of generating client private/public keys? Now, with an OPRF, we can solve this problem! The core idea is simple:
- Client converts password into a key, say , using a KDF.
- Use the OPRF to incorporate the server’s OPRF seed alongside to generate the new key .
- Use to generate the client’s private key and public key .
- Perform 3DH key exchange, bilaterally authenticating each party.
- Use derived shared key to encrypt communications.
This is almost everything that is in OPAQUE, except that we still have to solve two problems:
- How does the server get the client’s public key?
- How does the client get the server’s public key?
The first problem is solvable if we send the client’s public key during registration8, but the second isn’t so simple. Remember, the throughout the entire authentication process, the client and server are talking in an insecure channel. That means that if the server sends its public key to the client in the clear, a malicious actor could swap out that public key with their own. The solution is to use a masking key that allows the client and server to encrypt the server public key. This masking key is based off the OPRF output and is sent to the server during registration alongside the client’s public key.
Let’s walk through how the OPAQUE login flow works in its entirety. Suppose our Carol is the client and our Steve is the server. They agree to use a cyclic group of prime size9 with generator and to use the 3DH key exchange mechanism. This means that they are using the OPAQUE-3DH protocol. How does Carol log in?
Client Requests Login (Key Exchange Message 1)
Carol first passes her password into a special cryptographic hash function that converts her password into a group element, say . We now generate the OPRF blind and get as our blinded element10. Then a cryptographic nonce for Carol, , as well as the 3DH keyshares and are generated. We’ll see what is used for in a while, but for now just understand that it is a number that should never be used more than once (i.e., it should not be repeated in another login attempt).
Carol sends the blinded password , her nonce , and her public keyshare to Steve in a structure called KE1 (Key Exchange 1). Now because Steve also needs to correctly retrieve Carol’s public key and masking key from his own records, thus Carol also needs to send her username to Steve alongside KE1.
Server Responds (Key Exchange Message 2)
Steve receives Carol’s KE1 message and her username. He uses her username to retrieve her registration record, which includes Carol’s public key , masking key, and an envelope containing a nonce and a Message Authentication Code (MAC) (or authentication tag) . What’s this MAC used for? Well, it is to ensure that Carol’s public key , masking key, and the envelope itself has not changed during storage. Later, this MAC will also be used to check whether Carol provided the correct password, but more on that in a bit.
Like with Carol, Steve also generates a random nonce alongside his 3DH keyshares and . Now Steve already has his own private key and public key , so he can already complete the 3DH key exchange on his end. He computes , , and , using them to generate the session key as well as two MACs: a server MAC and an expected client MAC .
But Carol still needs the output of the OPRF to complete her side of the key exchange! Steve has the OPRF seed, but has not applied it to the blinded password yet. He thus transforms that seed into a key using a KDF and then uses it to evaluate the blinded password , obtaining .
Steve is almost ready to send these values back to Carol, but he also needs to send his public key to Carol. But remember how that the channel is still insecure? Thus, Steve needs to use the masking key stored in the registration record of Carol and a masking nonce to encrypt11 of as well as the envelope. Providing the public key and the envelope to Carol allows her to check if (a) her password is correct and (b) whether Steve has the correct envelope (or if someone is pretending to be Steve).
So, at the end of all that, Steve bundles the evaluated blind , the masking nonce , and the encrypted and envelope into a CredentialResponse structure, and packs the server nonce , his public keyshare , and server MAC into a AuthResponse structure. These two structures are bundled together and sent to Carol as the KE2 message.
Client Proves Itself (Key Exchange Message 3)
Now, back to Carol. She receives the KE2 message from Steve and unpacks it.
The first thing she does is to take the evaluated blind and unblind it. As previously discussed, this is done by finding the multiplicative inverse of the blind (i.e., ) and then computing , since
The OPRF output, , is used as input to a KDF to generate a random password. This random password is the aforementioned output of the OPRF that was used to create the masking key, which Carol can now obtain. So she can use that key to recover Steve’s public key as well as the envelope from the CredentialResponse in the KE2 message. Typically, now Carol would check if , along with the envelope, produces the same envelope MAC as what was found in the envelope itself. If not, it might be the case that Carol got her password wrong.
Assuming it is correct, this random password is also used to (re)generate the client private key and client public key . She can now complete the 3DH key exchange on her end by computing , , and , using them to generate the session key , the expected server MAC , and the client MAC . Carol checks Steve’s server MAC against her own and verifies that they are the same. If it is, she sends Steve her client MAC as the KE3 message.
At this point, Carol has completed authenticating Steve and is ready to begin encrypted communications using the session key . However, Steve still needs to do one more thing.
Server Finalizes
Steve receives Carol’s client MAC and compares it with his expected MAC . If they do not match, he drops the connection. Otherwise, he would have verified Carol’s identity and can begin encrypted communications using the same session key .
Phew! That was a lot. As you can see, the OPAQUE protocol is quite dense. But understanding the two main aspects of OPAQUE, namely the key exchange mechanism and the inclusion of server secrets into the password-based key derivation process, it becomes more manageable and understandable as a whole.
The Modularity of OPAQUE and Post-Quantum Cryptography
An obvious surface-level improvement of OPAQUE is that it takes less round trips than SRP, as seen in the diagram below.
Another improvement that OPAQUE has over SRP is that it is very modular. OPAQUE is made up of two main components: the key exchange mechanism and the OPRF. In RFC9807, they specified 3DH as the key exchange mechanism and the cyclic group-based OPRF as the OPRF implementation. But these can be swapped out for others!
An exciting opportunity is to implement a post-quantum secure OPAQUE protocol by swapping out the key exchange mechanism and the OPRF with ones that are post-quantum secure. So far, the National Institute of Standards and Technology (NIST) have standardized several post-quantum cryptographic protocols, including a module lattice-based key encapsulation mechanism in FIPS203 which, when paired with the corresponding digital signature algorithm in FIPS204, could replace 3DH as a post-quantum secure key exchange mechanism. A post-quantum secure OPRF remains elusive, though. But the modularity of OPAQUE means that, when a post-quantum OPRF become provably secure, they can be swapped with the existing OPRF to make a fully post-quantum secure aPAKE protocol built upon OPAQUE.
Disclosure
No artificial intelligence technologies or tools were used in the creation of this blog post.
Image Credits
Photo by Compare Fibre on Unsplash. Resized from the original.
Footnotes
To be a little clearer, ‘exponentiation’ by means that we apply the operation of a group on an element times. In the case of the multiplicative group of integers modulo , that is the same as raising the power of an integer to before taking the remainder modulo . ↩
It is actually not known how difficult it is to recover ; we only have empirical experiments showing that it is hard. ↩
A typical approach may be to rely on Public Key Infrastructure (PKI) with the use of Certificate Authorities (CAs). In OPAQUE, however, we’ll do something cleverer, as we will see. ↩
There are many types of KDFs, each with different properties and uses. In this description of 3DH the KDF can just be thought of as combining these inputs into a single, fixed-length key. ↩
This naive approach is incredibly insecure. A cryptographic salt is typically included as another input to the KDF to prevent trivial brute force attacks. In this hypothetical scenario, however, this doesn’t matter. ↩
This fact is due to being a field, in which all non-zero elements in it must have a multiplicative inverse. Specifically, is the Galois Field of order . ↩
To quote RFC9807: “The name “OPAQUE” is a homonym of O-PAKE, where O is for Oblivious. The name “OPAKE” was taken.” ↩
Like with most PAKEs, the registration phase of the protocol is assumed to be performed in some secure channel. So sending the masking key during the registration phase is okay, but sending it during the login phase is not. ↩
In RFC9807 they use an Elliptic Curve Group such as Ristretto255, which is a cyclic group of prime size. However, introducing elliptic curves here would just muddy the explanation. ↩
In RFC9807 the elliptic curve groups are additive groups, meaning that the fundamental group operation is expressed as instead of , and consequently the ‘exponentiation’ of group elements is written as instead of as we have been doing here. For consistency of notation within this blog post, however, we’ll stick to using ‘exponentiation’. ↩
Although the word “encrypt” might make it seem like this is a computationally expensive process, what is actually used is a one-time pad based on Carol’s masking key and the masking nonce. In particular, the masking key and masking nonce is expanded using a KDF to the length of the message, and then used to XOR the message. ↩