The OPAQUE protocol is a secure asymmetric password authenticated key exchange (aPAKE). This allows mutual authentication between a client and a server, and without using PKI. There is no secret salt values stored, which makes it free from pre-computation attacks [article].
OPAQUE - Asymmetric PAKE Protocol |
Method
Below is the basic method used, and where Bob generates a hash of his password to produce \(x\). Alice and Bob agree on a generator value (g) and where they will use discrete logs. Bob then has a user ID (IDU), and generates a key pair (PrivU, PubU). He passes IDU, PubU, and \(g^x\) to Alice. Alice also has an ID (IDS), and a unqiue user key for Bob (y). She then create a key pair (PrivS, PubS). She then sends IDS, PubS, and \(g^y\):
Bob computes \(d\), and which is the hash of \(g^x\) and IDS, and Alice computes \(e\), and which is the hash of \(g^y\) and IDU. Finally they compute the same key. Here is the working out:
Bob (the Client) passes:
IDU, \(g^x\) and PubU
Alice (the Server) passes:
IDS, \(g^y\) and PubS
Bob computes \(d=H(g^x,IDS)\), and calculates the key as:
\(K=H({ (g^y g^{e}) }^{x+d.PrivU} )\)
Alice computes \(e=H(g^y,IDU)\), calculates the key as:
\(K=H({ (g^x g^{d}) }^{y+e.PrivS} )\)
and they should be equal.
Coding
The coding is here:
// https://github.com/frekui/opaque/blob/master/auth_test.go package main import ( "fmt" "github.com/frekui/opaque" "crypto/rsa" "crypto/rand" "bytes" ) func main() { username := "user" password := "password" // First create the server's private RSA key. privS, _ := rsa.GenerateKey(rand.Reader, 512) // Register the user. clientSession, msg1, _ := opaque.PwRegInit(username, password, 512) serverSession, msg2, _ := opaque.PwReg1(privS, msg1) msg3, _ := opaque.PwReg2(clientSession, msg2) user := opaque.PwReg3(serverSession, msg3) fmt.Printf("User: %s\nPassword: %s\n",username,password) fmt.Printf("Private key: %x\n",privS) fmt.Printf("Client session: %x\n",clientSession) fmt.Printf("Session session: %x\n",serverSession) err := authenticate(privS, user, password, nil, nil, nil, false) if (err!=nil) { fmt.Printf("\nUser (%s) has NOT authenticated with %s\n",username,password) } else { fmt.Printf("\nUser (%s) has authenticated with %s\n",username,password) } password="fred" err = authenticate(privS, user, password, nil, nil, nil, true) if (err!=nil) { fmt.Printf("\nUser (%s) has NOT authenticated with %s\n",username,password) } else { fmt.Printf("\nUser (%s) has authenticated with %s\n",username,password) } } func authenticate(privS *rsa.PrivateKey, user *opaque.User, password string, msg1Mod func(*opaque.AuthMsg1), msg2Mod func(*opaque.AuthMsg2), msg3Mod func(*opaque.AuthMsg3), skipMsg2Error bool) error { cAuthSession, amsg1, err := opaque.AuthInit(user.Username, password) if err != nil { return err } if msg1Mod != nil { msg1Mod(&amsg1) } sAuthSession, amsg2, err := opaque.Auth1(privS, user, amsg1) if err != nil { return fmt.Errorf("server: %s", err) } if msg2Mod != nil { msg2Mod(&amsg2) } cSharedSecret, amsg3, err := opaque.Auth2(cAuthSession, amsg2) if !skipMsg2Error && err != nil { return fmt.Errorf("client: %s", err) } if msg3Mod != nil { msg3Mod(&amsg3) } sSharedSecret, err := opaque.Auth3(sAuthSession, amsg3) if err != nil { return fmt.Errorf("server: %s", err) } if !bytes.Equal(cSharedSecret, sSharedSecret) { return fmt.Errorf("Shared secrets differ") } return nil }
A sample run:
User: user Password: password Private key: &{{cc4b7141398968db8156cad817aaef3f393daef269b86290034cb490f106099303e6f260f6 45e421fe688691cf26fffd106c2eb7ab794b30fc7697f5d8e39011 10001} 8a7af92245662775e38e79a15129 c5530df20a0cb82d0c62a259f46beb7534370405171472d46ac508980b12edf588f102571c58c5de1cd32040ca 56ae85d201 [cf8339f609a56d1b18ffc4603f70a5d09b29c8f9106b0809138780669c1f1d09 fc07bb095cf87 09c4a6f0da4e69076a0623808408191736aae68b12bf9fca4c9] {7c884226774f8194bb81f2b5d81ca045fa78 2f15d9568232003857443cf92711 bf5aa340ddace7db528c750091ea7797185bd0b021702d007fc18f5b8367b 639 1e769cdf16bc8626c94da6fd523137db266b34b6d8ef0854dab31234da63de07 []}} Client session: &{c00000e400 c00000e3c0 70617373776f7264 200} Session session: &{75736572 c00000e420 c00000e440} User (user) has authenticated with password User (user) has NOT authenticated with fred