OPAQUE PAKE
Meanwhile as I’m stuck at trying to factorize polynomials in GF(2^128), see previous post, I decided to take a look at a new PAKE protocol I saw on twitter.
PAKE
PAKE Protocols is a authenticated key exchange protocol. An example of a unauthenticated key exchange protocol is Diffie-Hellman. I’ve written code on a very basic MITM-attack on Diffie-Hellman here.
In a PAKE protocol you authenticate through a password - and what makes it really great is that the password is also protected. After a failed attempt the client and server should only know if the attempt failed or not if the password matched the server’s expected value.
Why OPAQUE?
If you’ve heard of Secure Remote Password, or SRP, you may know that it’s like OPAQUE, a PAKE protocol. SRP has some issues, for instance it allows an attacker to run an offline-dictionary attack, since it exposes the salt to the user. I’ve written some code on the attack here
So, OPAQUE… OPAQUE does not reveal the salt to the user, nor does it reveal the password to the server. It does this by having the server and the client run an oblivious PRF.
It looks like this:
H = hashfunction
H' = maps a hash into the group psuedorandomly.
x = password, or password hash.
k = server salt
g = generator in group
r = random element in group
# Client runs this
A = H'(x)*g^r
# Server calculates
v = g^k
b = A^k
# Client calculates the random-looking password
rwdU = H(x,v, b*v^{-r})
*Note*
b*v^{-r} = A^k * v^{-r} = (H'(x)*g^r)^k * (g^k)^{-r} = (H'(x)*g^rk) * (g^{-r}k} = H'(x)
# Which means that the correct password generates the same rwdU for every randomly selected r and correct server salt, k.
So the client is never exposed to the salt, and the server never learns the password.
So how do we authenticate?
First a user needs to do a “password registration” at the server.
pwdU = Client's password
privU, pubU = Client keys
kU = Server OPRF salt, random and independant of each user.
vU = g^kU
privS,pubS = Server keys. Can be the same for all users
# Client
rwdU = oprf(pwdU)
# Server also runs oprf but ignores the result.
oprf()
# Client
envelope = AuthEnc(rwdU, privU, pubU, pubS, vU)
# Client sends envelope and deletes rwdU, pwdU and keys.
# Server saves envelope
So the server has the encrypted envelope and kU. And the client has nothing saved.
Authentication can look like this:
xU, yU = Client DH keys
xS, yS = Server DH keys
# Client
rwdU = oprf()
# Server
oprf()
signature = sign(privS, H(yU, yS))
# Client
decrypt envelope with rwdU
verified = verify(envelope(pubS), H(yU,yS))
signature = sign(envelope(privU), H(HKDF(dh_shared), envelope(pubU)))
# Server
verified = verify(pubU, H(HKDF(dh_shared), pub))
If the last verified check passes the client is authenticated, since it proves that the user knows the password.
PoC
I’ve implemented a simple server/client PoC here
This was a quick hack over two days, do not use this for anything important :-)
Next project is looking at security in some IoT-devices were given by a sponsor from BTH_CTF…
Until next time!