CPace provides secure mutual authentication based on a pre-shared secret or password.
This package implements https://datatracker.ietf.org/doc/draft-irtf-cfrg-cpace.
!!! WARNING: THIS IMPLEMENTATION IS PROOF OF CONCEPT AND BASED ON THE LATEST INTERNET-DRAFT. THERE ARE ABSOLUTELY NO WARRANTIES. !!!
CPace allows two parties sharing a common secret or password to securely agree on a session key for secure communication. It's a dead-simple protocol with only two messages, yet a state of the art key exchange based on a shared secret.
Note: The registration of the secret password is not in the scope of the protocol or this implementation.
go get github.com/bytemare/cpace
The API is minimal and very easy to use.
First, define the cryptographic parameters you want to use:
package cpace
import (
"github.com/bytemare/cpace"
"github.com/bytemare/crypto/group"
"github.com/bytemare/crypto/hash"
)
params := &cpace.Parameters{
Group: ciphersuite.Ristretto255Sha512,
Hash: hash.SHA512,
}
Then initialize it with peer information. Note that you can then store this structure offline and (re)use it for the sessions between these peers.
params.Init(clientID, serverID, nil).New(Initiator)
// If needed
encoded := params.Serialize()
decoded, err := cpace.DeserializeParameters(encoded)
On protocol execution, derive a peer by specifying its role:
client := params.New(Initiator)
// or
server := params.New(Responder)
Start()
returns the peer's public share to be transmitted. If no sid is given to the initiator, this function generates a new sid and returns it. If no sid is given to the responder, it returns an error. The sid must be the same for both peers.Finish()
takes the peer's public share and returns the session key.
If and only if the peers use the correct values they derive the same session key. If not, nothing about the password leaks.
Full Example:
package cpace
import (
"github.com/bytemare/cpace"
"github.com/bytemare/crypto/group"
"github.com/bytemare/crypto/hash"
)
clientID := []byte("client")
serverID := []byte("server")
password := []byte("password")
var ad []byte = nil // this can securely be nil
// Set cryptographic parameters
params := &cpace.Parameters{
Group: ciphersuite.Ristretto255Sha512,
Hash: hash.SHA512,
}
// Prepare common communication info
info := params.Init(clientID, serverID, ad)
// Get a client and a server
client := info.Initiator()
server := info.Responder()
// Client starts. If no sid is given for the client, the function returns a new sid.
epku, sid, err := client.Start(password, nil)
if err != nil {
panic(err)
}
// The server receives sends back its own epks.
// The sid should be the same as from the client, and can even be the one the client sent.
epks, _, err := server.Start(password, sid)
if err != nil {
panic(err)
}
// The session key can already be derived by the server using the client's epku.
// If they differ, one of the peers used the wrong password.
serverSK, err := server.Finish(epku)
if err != nil {
panic(err)
}
// The client receives the server epks, and can derive the session key.
clientSK, err := client.Finish(epks)
if err != nil {
panic(err)
}
// The protocol is finished, and both parties now share the same secret session key
All cryptographic operations can be found in the crypto package, which itself uses either the standard library or tested and proved external libraries.
Don't, yet.