opaque

package module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 12, 2024 License: MIT Imports: 19 Imported by: 0

README

OPAQUE

OPAQUE Go Reference codecov

  import "github.com/Jan-nku/opaque"

This package implements OPAQUE, an asymmetric password-authenticated key exchange protocol that is secure against pre-computation attacks. It enables a client to authenticate to a server without ever revealing its password to the server.

This implementation is developed by one of the authors of the RFC Internet Draft. The main branch is in sync with the latest developments of the draft, and the releases correspond to the official draft versions.

What is OPAQUE?

OPAQUE is a PKI-free secure aPAKE that is secure against pre-computation attacks. OPAQUE provides forward secrecy with respect to password leakage while also hiding the password from the server, even during password registration. OPAQUE allows applications to increase the difficulty of offline dictionary attacks via iterated hashing or other key stretching schemes. OPAQUE is also extensible, allowing clients to safely store and retrieve arbitrary application data on servers using only their password.

References

Documentation Go Reference

You can find the documentation and usage examples in the package doc and the project wiki .

Versioning

SemVer is used for versioning. For the versions available, see the tags on the repository.

Minor v0.x versions match the corresponding CFRG draft version, the master branch implements the latest changes of the draft development.

Contributing

Please read CONTRIBUTING.md for details on the code of conduct, and the process for submitting pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package opaque implements OPAQUE, an asymmetric password-authenticated key exchange protocol that is secure against pre-computation attacks. It enables a client to authenticate to a server without ever revealing its password to the server. Protocol details can be found on the IETF RFC page (https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque) and on the GitHub specification repository (https://github.com/cfrg/draft-irtf-cfrg-opaque).

Example (Configuration)

Example_Configuration shows how to instantiate a configuration, which is used to initialize clients and servers from. Configurations MUST remain the same for a given client between sessions, or the client won't be able to execute the protocol. Configurations can be serialized and deserialized, if you need to save, hardcode, or transmit it.

package main

import (
	"bytes"
	"crypto"
	"encoding/hex"
	"fmt"
	"log"

	"github.com/bytemare/ksf"

	"github.com/Jan-nku/opaque"
)

func isSameConf(a, b *opaque.Configuration) bool {
	if a.OPRF != b.OPRF ||
		a.KDF != b.KDF ||
		a.MAC != b.MAC ||
		a.Hash != b.Hash ||
		a.KSF != b.KSF ||
		a.AKE != b.AKE {
		return false
	}

	return bytes.Equal(a.Context, b.Context)
}

func main() {
	// You can compose your own configuration or choose a recommended default configuration.
	// The two following configuration setups are the same.
	defaultConf := opaque.DefaultConfiguration()

	customConf := &opaque.Configuration{
		OPRF:    opaque.RistrettoSha512,
		KDF:     crypto.SHA512,
		MAC:     crypto.SHA512,
		Hash:    crypto.SHA512,
		KSF:     ksf.Argon2id,
		AKE:     opaque.RistrettoSha512,
		Context: nil,
	}

	if !isSameConf(defaultConf, customConf) {
		// isSameConf() this is just a demo function to check equality.
		log.Fatalln("Oh no! Configurations differ!")
	}

	// A configuration can be saved encoded and saved, and later loaded and decoded at runtime.
	// Any additional 'Context' is also included.
	encoded := defaultConf.Serialize()
	fmt.Printf("Encoded Configuration: %s\n", hex.EncodeToString(encoded))

	// This how you decode that configuration.
	conf, err := opaque.DeserializeConfiguration(encoded)
	if err != nil {
		log.Fatalf("Oh no! Decoding the configurations failed! %v", err)
	}

	if !isSameConf(defaultConf, conf) {
		log.Fatalln("Oh no! Something went wrong in decoding the configuration!")
	}

	fmt.Println("OPAQUE configuration is easy!")

}
Output:

Encoded Configuration: 0107070701010000
OPAQUE configuration is easy!
Example (Deserialization)

Example_Deserialization demonstrates a couple of ways to deserialize OPAQUE protocol messages. Message interpretation depends on the configuration context it's exchanged in. Hence, we need the corresponding configuration. We can then directly deserialize messages from a Configuration or pass them to Client or Server instances which can do it as well. You must know in advance what message you are expecting, and call the appropriate deserialization function.

package main

import (
	"encoding/hex"
	"fmt"
	"log"
	"reflect"

	"github.com/Jan-nku/opaque"
)

func main() {
	// Let's say we have this RegistrationRequest message we received on the wire.
	registrationMessage, _ := hex.DecodeString("9857e1694af550c515e56a9103292ad07a014b020708d3df57ac4b151f58d323")

	// Pick your configuration.
	conf := opaque.DefaultConfiguration()

	// You can directly deserialize and test the message's validity in that configuration by getting a deserializer.
	deserializer, err := conf.Deserializer()
	if err != nil {
		log.Fatalln(err)
	}

	requestD, err := deserializer.RegistrationRequest(registrationMessage)
	if err != nil {
		log.Fatalln(err)
	}

	// Or if you already have a Server instance, you can use that also.
	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	requestS, err := server.Deserialize.RegistrationRequest(registrationMessage)
	if err != nil {
		// The error message will tell us what's wrong.
		log.Fatalln(err)
	}

	// Alternatively, a Client instance can do that as well.
	client, err := conf.Client()
	if err != nil {
		// The error message will tell us what's wrong.
		log.Fatalln(err)
	}

	requestC, err := client.Deserialize.RegistrationRequest(registrationMessage)
	if err != nil {
		// The error message will tell us what's wrong.
		log.Fatalln(err)
	}

	// All these yield the same message. The following is just a test to proof that point.
	{
		if !reflect.DeepEqual(requestD, requestS) ||
			!reflect.DeepEqual(requestD, requestC) ||
			!reflect.DeepEqual(requestS, requestC) {
			log.Fatalf("Unexpected divergent RegistrationMessages:\n\t- %v\n\t- %v\n\t- %v",
				hex.EncodeToString(requestD.Serialize()),
				hex.EncodeToString(requestS.Serialize()),
				hex.EncodeToString(requestC.Serialize()))
		}

		fmt.Println("OPAQUE messages deserialization is easy!")
	}

}
Output:

OPAQUE messages deserialization is easy!
Example (FakeResponse)

Example_FakeResponse shows how to counter some client enumeration attacks by faking an existing client entry. Precompute the fake client record, and return it when no valid record was found. Use this with the server's LoginInit function whenever a client wants to retrieve an envelope but a client entry does not exist. Failing to do so results in an attacker being able to enumerate users.

package main

import (
	"encoding/hex"
	"fmt"
	"log"

	"github.com/Jan-nku/opaque"
)

var secretOprfSeed, serverPrivateKey, serverPublicKey []byte

// Example_ServerSetup shows how to set up the long term values for the OPAQUE server.
// - The secret OPRF seed can be unique for each client or the same for all, but must be
// the same for a given client between registration and all login sessions.
// - The AKE key pair can also be the same for all clients or unique, but must be
// the same for a given client between registration and all login sessions.
func Example_serverSetup() {

	serverID := []byte("server-identity")
	conf := opaque.DefaultConfiguration()
	secretOprfSeed = conf.GenerateOPRFSeed()
	serverPrivateKey, serverPublicKey = conf.KeyGen()

	if serverPrivateKey == nil || serverPublicKey == nil || secretOprfSeed == nil {
		log.Fatalf("Oh no! Something went wrong setting up the server secrets!")
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("OPAQUE server initialized.")

}

func main() {
	// The server must have been set up with its long term values once. So we're calling this, here, for the demo.
	{
		Example_serverSetup()
	}

	// Precompute the fake client record, and return it when no valid record was found. The malicious client will
	// purposefully fail, but can't determine the difference with an existing client record. Choose the same
	// configuration as in your app.
	conf := opaque.DefaultConfiguration()
	fakeRecord, err := conf.GetFakeRecord([]byte("fake_client"))
	if err != nil {
		log.Fatalln(err)
	}

	// Later, during protocol execution, let's say this is the fraudulent login message we received,
	// for which no client entry exists.
	message1, _ := hex.DecodeString("b4d366645e7ae380f9d476e1319e67c1821f7a5d3dfbfc4e26c7898351979139" +
		"0ea528fc609b4393b0353e85fdbb20c6067c11919f40d93d8bb229967fc2878c" +
		"209786ef4b960bfbfe10481c1fd301300fc72dc4234a1e829b556c720f904d30")

	// Continue as usual, using the fake record in lieu of the (non-)existing one. The server the sends
	// back the serialized ke2 message message2.
	var message2 []byte
	{
		serverID := []byte("server")
		server, err := conf.Server()
		if err != nil {
			log.Fatalln(err)
		}

		if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
			log.Fatalln(err)
		}

		ke1, err := server.Deserialize.KE1(message1)
		if err != nil {
			log.Fatalln(err)
		}

		ke2, err := server.LoginInit(ke1, fakeRecord)
		if err != nil {
			log.Fatalln(err)
		}

		message2 = ke2.Serialize()
	}

	// The following is just a test to check everything went fine.
	{
		if len(message2) == 0 {
			log.Fatalln("Fake KE2 is unexpectedly empty.")
		}

		fmt.Println("Thwarting OPAQUE client enumeration is easy!")
	}

}
Output:

OPAQUE server values initialized.
Thwarting OPAQUE client enumeration is easy!
Example (LoginKeyExchange)

Example_LoginKeyExchange demonstrates in a single function the interactions between a client and a server for the login phase. This is of course a proof-of-concept demonstration, as client and server execute separately.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/Jan-nku/opaque"
)

var (
	exampleClientRecord                               *opaque.ClientRecord
	secretOprfSeed, serverPrivateKey, serverPublicKey []byte
)

// Example_ServerSetup shows how to set up the long term values for the OPAQUE server.
// - The secret OPRF seed can be unique for each client or the same for all, but must be
// the same for a given client between registration and all login sessions.
// - The AKE key pair can also be the same for all clients or unique, but must be
// the same for a given client between registration and all login sessions.
func Example_serverSetup() {

	serverID := []byte("server-identity")
	conf := opaque.DefaultConfiguration()
	secretOprfSeed = conf.GenerateOPRFSeed()
	serverPrivateKey, serverPublicKey = conf.KeyGen()

	if serverPrivateKey == nil || serverPublicKey == nil || secretOprfSeed == nil {
		log.Fatalf("Oh no! Something went wrong setting up the server secrets!")
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("OPAQUE server initialized.")

}

// Example_Registration demonstrates in a single function the interactions between a client and a server for the
// registration phase. This is of course a proof-of-concept demonstration, as client and server execute separately.
// The server outputs a ClientRecord and the credential identifier. The latter is a unique identifier for  a given
// client (e.g. database entry ID), and that must absolutely stay the same for the whole client existence and
// never be reused.
func Example_registration() {

	{
		Example_serverSetup()
	}

	password := []byte("password")

	serverID := []byte("server")
	clientID := []byte("username")
	conf := opaque.DefaultConfiguration()

	client, err := conf.Client()
	if err != nil {
		log.Fatalln(err)
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	var message1, message2, message3 []byte
	var credID []byte

	{
		c1 := client.RegistrationInit(password)
		message1 = c1.Serialize()
	}

	{
		request, err := server.Deserialize.RegistrationRequest(message1)
		if err != nil {
			log.Fatalln(err)
		}

		credID = opaque.RandomBytes(64)
		pks, err := server.Deserialize.DecodeAkePublicKey(serverPublicKey)
		if err != nil {
			log.Fatalln(err)
		}

		response := server.RegistrationResponse(request, pks, credID, secretOprfSeed)

		message2 = response.Serialize()
	}

	{
		response, err := client.Deserialize.RegistrationResponse(message2)
		if err != nil {
			log.Fatalln(err)
		}

		record, _ := client.RegistrationFinalize(response, opaque.ClientRegistrationFinalizeOptions{
			ClientIdentity: clientID,
			ServerIdentity: serverID,
		})
		message3 = record.Serialize()
	}

	{
		record, err := server.Deserialize.RegistrationRecord(message3)
		if err != nil {
			log.Fatalln(err)
		}

		exampleClientRecord = &opaque.ClientRecord{
			CredentialIdentifier: credID,
			ClientIdentity:       clientID,
			RegistrationRecord:   record,
		}

		fmt.Println("OPAQUE registration is easy!")
	}

}

func main() {
	// For the purpose of this demo, we consider the following registration has already happened.
	{
		Example_registration()
	}

	// Secret client information.
	password := []byte("password")

	// Information shared by both client and server.
	serverID := []byte("server")
	clientID := []byte("username")
	conf := opaque.DefaultConfiguration()

	// Runtime instantiation for the client and server.
	client, err := conf.Client()
	if err != nil {
		log.Fatalln(err)
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
		log.Fatalln(err)
	}

	// These are the 3 login messages that will be exchanged,
	// and the respective sessions keys for the client and server.
	var message1, message2, message3 []byte
	var clientSessionKey, serverSessionKey []byte

	// The client initiates the ball and sends the serialized ke1 to the server.
	{
		ke1 := client.LoginInit(password)
		message1 = ke1.Serialize()
	}

	// The server interprets ke1, and sends back ke2.
	{
		ke1, err := server.Deserialize.KE1(message1)
		if err != nil {
			log.Fatalln(err)
		}

		ke2, err := server.LoginInit(ke1, exampleClientRecord)
		if err != nil {
			log.Fatalln(err)
		}

		message2 = ke2.Serialize()
	}

	// The client interprets ke2. If everything went fine, the server is considered trustworthy and the client
	// can use the shared session key and secret export key.
	{
		ke2, err := client.Deserialize.KE2(message2)
		if err != nil {
			log.Fatalln(err)
		}

		// In this example, we don't use the secret export key. The client sends the serialized ke3 to the server.
		ke3, _, err := client.LoginFinish(ke2, opaque.ClientLoginFinishOptions{
			ClientIdentity: clientID,
			ServerIdentity: serverID,
		})
		if err != nil {
			log.Fatalln(err)
		}

		message3 = ke3.Serialize()

		// If no error occurred, the server can be trusted, and the client can use the session key.
		clientSessionKey = client.SessionKey()
	}

	// The server must absolutely validate this last message to authenticate the client and continue. If this message
	// does not return successfully, the server must not send any secret or sensitive information and immediately cease
	// the connection.
	{
		ke3, err := server.Deserialize.KE3(message3)
		if err != nil {
			log.Fatalln(err)
		}

		if err := server.LoginFinish(ke3); err != nil {
			log.Fatalln(err)
		}

		// If no error occurred at this point, the server can trust the client and safely extract the shared session key.
		serverSessionKey = server.SessionKey()
	}

	// The following test does not exist in the real world and simply proves the point that the keys match.
	if !bytes.Equal(clientSessionKey, serverSessionKey) {
		log.Fatalln("Oh no! Abort! The shared session keys don't match!")
	}

	fmt.Println("OPAQUE is much awesome!")
}
Output:

OPAQUE server values initialized.
OPAQUE registration is easy!
OPAQUE is much awesome!
Example (Registration)

Example_Registration demonstrates in a single function the interactions between a client and a server for the registration phase. This is of course a proof-of-concept demonstration, as client and server execute separately. The server outputs a ClientRecord and the credential identifier. The latter is a unique identifier for a given client (e.g. database entry ID), and that must absolutely stay the same for the whole client existence and never be reused.

package main

import (
	"fmt"
	"log"

	"github.com/Jan-nku/opaque"
)

var (
	exampleClientRecord                               *opaque.ClientRecord
	secretOprfSeed, serverPrivateKey, serverPublicKey []byte
)

// Example_ServerSetup shows how to set up the long term values for the OPAQUE server.
// - The secret OPRF seed can be unique for each client or the same for all, but must be
// the same for a given client between registration and all login sessions.
// - The AKE key pair can also be the same for all clients or unique, but must be
// the same for a given client between registration and all login sessions.
func Example_serverSetup() {

	serverID := []byte("server-identity")
	conf := opaque.DefaultConfiguration()
	secretOprfSeed = conf.GenerateOPRFSeed()
	serverPrivateKey, serverPublicKey = conf.KeyGen()

	if serverPrivateKey == nil || serverPublicKey == nil || secretOprfSeed == nil {
		log.Fatalf("Oh no! Something went wrong setting up the server secrets!")
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("OPAQUE server initialized.")

}

func main() {
	// The server must have been set up with its long term values once. So we're calling this, here, for the demo.
	{
		Example_serverSetup()
	}

	// Secret client information.
	password := []byte("password")

	// Information shared by both client and server.
	serverID := []byte("server")
	clientID := []byte("username")
	conf := opaque.DefaultConfiguration()

	// Runtime instantiation for the client and server.
	client, err := conf.Client()
	if err != nil {
		log.Fatalln(err)
	}

	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	// These are the 3 registration messages that will be exchanged.
	// The credential identifier credID is a unique identifier for a given client (e.g. database entry ID), and that
	// must absolutely stay the same for the whole client existence and never be reused.
	var message1, message2, message3 []byte
	var credID []byte

	// The client starts, serializes the message, and sends it to the server.
	{
		c1 := client.RegistrationInit(password)
		message1 = c1.Serialize()
	}

	// The server receives the encoded message, decodes it, interprets it, and returns its response.
	{
		request, err := server.Deserialize.RegistrationRequest(message1)
		if err != nil {
			log.Fatalln(err)
		}

		// The server creates a database entry for the client and creates a credential identifier that must absolutely
		// be unique among all clients.
		credID = opaque.RandomBytes(64)
		pks, err := server.Deserialize.DecodeAkePublicKey(serverPublicKey)
		if err != nil {
			log.Fatalln(err)
		}

		// The server uses its public key and secret OPRF seed created at the setup.
		response := server.RegistrationResponse(request, pks, credID, secretOprfSeed)

		// The server responds with its serialized response.
		message2 = response.Serialize()
	}

	// The client deserializes the responses, and sends back its final client record containing the envelope.
	{
		response, err := client.Deserialize.RegistrationResponse(message2)
		if err != nil {
			log.Fatalln(err)
		}

		// The client produces its record and a client-only-known secret export_key, that the client can use for other purposes (e.g. encrypt
		// information to store on the server, and that the server can't decrypt). We don't use in the example here.
		record, _ := client.RegistrationFinalize(response, opaque.ClientRegistrationFinalizeOptions{
			ClientIdentity: clientID,
			ServerIdentity: serverID,
		})
		message3 = record.Serialize()
	}

	// Server registers the client record.
	{
		record, err := server.Deserialize.RegistrationRecord(message3)
		if err != nil {
			log.Fatalln(err)
		}

		exampleClientRecord = &opaque.ClientRecord{
			CredentialIdentifier: credID,
			ClientIdentity:       clientID,
			RegistrationRecord:   record,
		}

		fmt.Println("OPAQUE registration is easy!")
	}

}
Output:

OPAQUE server values initialized.
OPAQUE registration is easy!
Example (ServerSetup)

Example_ServerSetup shows how to set up the long term values for the OPAQUE server. - The secret OPRF seed can be unique for each client or the same for all, but must be the same for a given client between registration and all login sessions. - The AKE key pair can also be the same for all clients or unique, but must be the same for a given client between registration and all login sessions.

package main

import (
	"fmt"
	"log"

	"github.com/Jan-nku/opaque"
)

var secretOprfSeed, serverPrivateKey, serverPublicKey []byte

func main() {
	// This a straightforward way to use a secure and efficient configuration.
	// They have to be run only once in the application's lifecycle, and the output values must be stored appropriately.
	serverID := []byte("server-identity")
	conf := opaque.DefaultConfiguration()
	secretOprfSeed = conf.GenerateOPRFSeed()
	serverPrivateKey, serverPublicKey = conf.KeyGen()

	if serverPrivateKey == nil || serverPublicKey == nil || secretOprfSeed == nil {
		log.Fatalf("Oh no! Something went wrong setting up the server secrets!")
	}

	// Server setup
	server, err := conf.Server()
	if err != nil {
		log.Fatalln(err)
	}

	if err := server.SetKeyMaterial(serverID, serverPrivateKey, serverPublicKey, secretOprfSeed); err != nil {
		log.Fatalln(err)
	}

	fmt.Println("OPAQUE server initialized.")

}
Output:

OPAQUE server initialized.

Index

Examples

Constants

View Source
const (
	// RistrettoSha512 identifies the Ristretto255 group and SHA-512.
	RistrettoSha512 = Group(group.Ristretto255Sha512)

	// P256Sha256 identifies the NIST P-256 group and SHA-256.
	P256Sha256 = Group(group.P256Sha256)

	// P384Sha512 identifies the NIST P-384 group and SHA-384.
	P384Sha512 = Group(group.P384Sha384)

	// P521Sha512 identifies the NIST P-512 group and SHA-512.
	P521Sha512 = Group(group.P521Sha512)
)

Variables

View Source
var (
	// ErrNoServerKeyMaterial indicates that the server's key material has not been set.
	ErrNoServerKeyMaterial = errors.New("key material not set: call SetKeyMaterial() to set values")

	// ErrAkeInvalidClientMac indicates that the MAC contained in the KE3 message is not valid in the given session.
	ErrAkeInvalidClientMac = errors.New("failed to authenticate client: invalid client mac")

	// ErrInvalidState indicates that the given state is not valid due to a wrong length.
	ErrInvalidState = errors.New("invalid state length")

	// ErrInvalidEnvelopeLength indicates the envelope contained in the record is of invalid length.
	ErrInvalidEnvelopeLength = errors.New("record has invalid envelope length")

	// ErrInvalidPksLength indicates the input public key is not of right length.
	ErrInvalidPksLength = errors.New("input server public key's length is invalid")

	// ErrInvalidOPRFSeedLength indicates that the OPRF seed is not of right length.
	ErrInvalidOPRFSeedLength = errors.New("input OPRF seed length is invalid (must be of hash output length)")

	// ErrZeroSKS indicates that the server's private key is a zero scalar.
	ErrZeroSKS = errors.New("server private key is zero")
)

Functions

func RandomBytes

func RandomBytes(length int) []byte

RandomBytes returns random bytes of length len (wrapper for crypto/rand).

Types

type Client

type Client struct {
	Deserialize *Deserializer
	OPRF        *oprf.Client
	Ake         *ake.Client
	// contains filtered or unexported fields
}

Client represents an OPAQUE Client, exposing its functions and holding its state.

func NewClient

func NewClient(c *Configuration) (*Client, error)

NewClient returns a new Client instantiation given the application Configuration.

func (*Client) GetConf

func (c *Client) GetConf() *internal.Configuration

GetConf returns the internal configuration.

func (*Client) LoginFinish

func (c *Client) LoginFinish(
	ke2 *message.KE2, options ...ClientLoginFinishOptions,
) (ke3 *message.KE3, exportKey []byte, err error)

LoginFinish returns a KE3 message given the server's KE2 response message and the identities. If the idc or ids parameters are nil, the client and server's public keys are taken as identities for both.

func (*Client) LoginInit

func (c *Client) LoginInit(password, username []byte, options ...ClientLoginInitOptions) *message.KE1

TODO: LoginInit(password) LoginInit initiates the authentication process, returning a KE1 message blinding the given password.

func (*Client) RegistrationFinalize

func (c *Client) RegistrationFinalize(
	resp *message.RegistrationResponse,
	options ...ClientRegistrationFinalizeOptions,
) (record *message.RegistrationRecord, exportKey []byte)

TODO: RegistrationFinalize RegistrationFinalize returns a RegistrationRecord message given the identities and the server's RegistrationResponse.

func (*Client) RegistrationInit

func (c *Client) RegistrationInit(
	password []byte,
	username []byte,
	options ...ClientRegistrationInitOptions,
) *message.RegistrationRequest

TODO: RegistrationInit RegistrationInit returns a RegistrationRequest message blinding the given password.

func (*Client) SessionKey

func (c *Client) SessionKey() []byte

SessionKey returns the session key if the previous call to LoginFinish() was successful.

type ClientLoginFinishOptions

type ClientLoginFinishOptions struct {
	// ClientIdentity: optional
	ClientIdentity []byte
	// ServerIdentity: optional
	ServerIdentity []byte
}

ClientLoginFinishOptions enables setting optional client values for the client registration.

type ClientLoginInitOptions

type ClientLoginInitOptions struct {
	// Blind: optional
	Blind *group.Scalar
	// EphemeralSecretKey: optional
	EphemeralSecretKey *group.Scalar
	// Nonce: optional
	Nonce []byte
	// NonceLength: optional
	NonceLength uint
}

ClientLoginInitOptions enables setting optional values for the session, which default to secure random values if not set.

type ClientRecord

type ClientRecord struct {
	CredentialIdentifier []byte
	ClientIdentity       []byte
	*message.RegistrationRecord

	// testing
	TestMaskNonce []byte
}

ClientRecord is a server-side structure enabling the storage of user relevant information.

type ClientRegistrationFinalizeOptions

type ClientRegistrationFinalizeOptions struct {
	// ClientIdentity: optional
	ClientIdentity []byte
	// ServerIdentity: optional
	ServerIdentity []byte
	// EnvelopeNonce : optional
	EnvelopeNonce []byte
}

TODO: ClientRegistrationFinalizeOptions contain ClientIdentity, ServerIdentity ClientRegistrationFinalizeOptions enables setting optional client values for the client registration.

type ClientRegistrationInitOptions

type ClientRegistrationInitOptions struct {
	// OPRFBlind: optional
	OPRFBlind *group.Scalar
}

ClientRegistrationInitOptions enables setting internal client values for the client registration.

type Configuration

type Configuration struct {
	Context []byte
	KDF     crypto.Hash    `json:"kdf"`
	MAC     crypto.Hash    `json:"mac"`
	Hash    crypto.Hash    `json:"hash"`
	OPRF    Group          `json:"oprf"`
	KSF     ksf.Identifier `json:"ksf"`
	AKE     Group          `json:"group"`
}

Configuration represents an OPAQUE configuration. Note that OprfGroup and AKEGroup are recommended to be the same, as well as KDF, MAC, Hash should be the same.

func DefaultConfiguration

func DefaultConfiguration() *Configuration

TODO: conf := opaque.DefaultConfiguration() DefaultConfiguration returns a default configuration with strong parameters.

func DeserializeConfiguration

func DeserializeConfiguration(encoded []byte) (*Configuration, error)

DeserializeConfiguration decodes the input and returns a Parameter structure.

func (*Configuration) Client

func (c *Configuration) Client() (*Client, error)

TODO: client, err := conf.Client() Client returns a newly instantiated Client from the Configuration.

func (*Configuration) Deserializer

func (c *Configuration) Deserializer() (*Deserializer, error)

Deserializer returns a pointer to a Deserializer structure allowing deserialization of messages in the given configuration.

func (*Configuration) Generate

func (c *Configuration) Generate() (t1, t2, gs *group.Element)

func (*Configuration) GenerateOPRFSeed

func (c *Configuration) GenerateOPRFSeed() []byte

GenerateOPRFSeed returns a OPRF seed valid in the given configuration.

func (*Configuration) GetFakeRecord

func (c *Configuration) GetFakeRecord(credentialIdentifier []byte) (*ClientRecord, error)

GetFakeRecord creates a fake Client record to be used when no existing client record exists, to defend against client enumeration techniques.

func (*Configuration) KeyGen

func (c *Configuration) KeyGen() (secretKey, publicKey []byte)

KeyGen returns a key pair in the AKE group.

func (*Configuration) Serialize

func (c *Configuration) Serialize() []byte

Serialize returns the byte encoding of the Configuration structure.

func (*Configuration) Server

func (c *Configuration) Server() (*Server, error)

TODO: server, err := conf.Server() Server returns a newly instantiated Server from the Configuration.

type Deserializer

type Deserializer struct {
	// contains filtered or unexported fields
}

Deserializer exposes the message deserialization functions.

func (*Deserializer) DecodeAkePrivateKey

func (d *Deserializer) DecodeAkePrivateKey(encoded []byte) (*group.Scalar, error)

DecodeAkePrivateKey takes a serialized private key (a scalar) and attempts to return it's decoded form.

func (*Deserializer) DecodeAkePublicKey

func (d *Deserializer) DecodeAkePublicKey(encoded []byte) (*group.Element, error)

DecodeAkePublicKey takes a serialized public key (a point) and attempts to return it's decoded form.

func (*Deserializer) KE1

func (d *Deserializer) KE1(ke1 []byte) (*message.KE1, error)

KE1 takes a serialized KE1 message and returns a deserialized KE1 structure.

func (*Deserializer) KE2

func (d *Deserializer) KE2(ke2 []byte) (*message.KE2, error)

KE2 takes a serialized KE2 message and returns a deserialized KE2 structure.

func (*Deserializer) KE3

func (d *Deserializer) KE3(ke3 []byte) (*message.KE3, error)

KE3 takes a serialized KE3 message and returns a deserialized KE3 structure.

func (*Deserializer) RegistrationRecord

func (d *Deserializer) RegistrationRecord(record []byte) (*message.RegistrationRecord, error)

RegistrationRecord takes a serialized RegistrationRecord message and returns a deserialized RegistrationRecord structure.

func (*Deserializer) RegistrationRequest

func (d *Deserializer) RegistrationRequest(registrationRequest []byte) (*message.RegistrationRequest, error)

RegistrationRequest takes a serialized RegistrationRequest message and returns a deserialized RegistrationRequest structure.

func (*Deserializer) RegistrationResponse

func (d *Deserializer) RegistrationResponse(registrationResponse []byte) (*message.RegistrationResponse, error)

RegistrationResponse takes a serialized RegistrationResponse message and returns a deserialized RegistrationResponse structure.

type Group

type Group byte

TODO: group Group identifies the prime-order group with hash-to-curve capability to use in OPRF and AKE.

func (Group) Available

func (g Group) Available() bool

Available returns whether the Group byte is recognized in this implementation. This allows to fail early when working with multiple versions not using the same configuration and Group.

func (Group) Group

func (g Group) Group() group.Group

Group returns the EC Group used in the Ciphersuite.

func (Group) OPRF

func (g Group) OPRF() oprf.Identifier

OPRF returns the OPRF Identifier used in the Ciphersuite.

type Server

type Server struct {
	Deserialize *Deserializer

	OPRF *oprf.Server
	Ake  *ake.Server
	// contains filtered or unexported fields
}

Server represents an OPAQUE Server, exposing its functions and holding its state.

func NewServer

func NewServer(c *Configuration) (*Server, error)

TODO: NewServer() NewServer returns a Server instantiation given the application Configuration.

func (*Server) ExpectedMAC

func (s *Server) ExpectedMAC() []byte

ExpectedMAC returns the expected client MAC if the previous call to LoginInit() was successful.

func (*Server) GetConf

func (s *Server) GetConf() *internal.Configuration

GetConf return the internal configuration.

func (*Server) HpakeLoginInit

func (s *Server) HpakeLoginInit(
	ke1 *message.KE1,
	record *ClientRecord,
	client *http.Client,
	options ...ServerLoginInitOptions,
) (*message.KE2, error)

TODO: Modify LoginInit(ke1, record) to support hpake LoginInit responds to a KE1 message with a KE2 message a client record.

func (*Server) HpakeRegistrationResponse

func (s *Server) HpakeRegistrationResponse(
	req *message.RegistrationRequest,
	serverPublicKey *group.Element,
	credentialIdentifier, oprfSeed []byte,
	client *http.Client,
) *message.RegistrationResponse

TODO: Modify RegistrationResponse func to support hpake RegistrationResponse returns a RegistrationResponse message to the input RegistrationRequest message and given identifiers.

func (*Server) HpakeSetKeyMaterial

func (s *Server) HpakeSetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte, t1, t2, gs *group.Element) error

SetKeyMaterial set the server's identity and mandatory key material to be used during LoginInit(). All these values must be the same as used during client registration and remain the same across protocol execution for a given registered client.

- serverIdentity can be nil, in which case it will be set to serverPublicKey. - serverSecretKey is the server's secret AKE key. - serverPublicKey is the server's public AKE key to the serverSecretKey. - oprfSeed is the long-term OPRF input seed.

func (*Server) LoginFinish

func (s *Server) LoginFinish(ke3 *message.KE3) error

LoginFinish returns an error if the KE3 received from the client holds an invalid mac, and nil if correct.

func (*Server) LoginInit

func (s *Server) LoginInit(
	ke1 *message.KE1,
	record *ClientRecord,
	options ...ServerLoginInitOptions,
) (*message.KE2, error)

LoginInit responds to a KE1 message with a KE2 message a client record.

func (*Server) RegistrationResponse

func (s *Server) RegistrationResponse(
	req *message.RegistrationRequest,
	serverPublicKey *group.Element,
	credentialIdentifier, oprfSeed []byte,
) *message.RegistrationResponse

RegistrationResponse returns a RegistrationResponse message to the input RegistrationRequest message and given identifiers.

func (*Server) SerializeState

func (s *Server) SerializeState() []byte

SerializeState returns the internal state of the AKE server serialized to bytes.

func (*Server) SessionKey

func (s *Server) SessionKey() []byte

SessionKey returns the session key if the previous call to LoginInit() was successful.

func (*Server) SetAKEState

func (s *Server) SetAKEState(state []byte) error

SetAKEState sets the internal state of the AKE server from the given bytes.

func (*Server) SetKeyMaterial

func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte) error

func (*Server) TopaqueLoginInit added in v0.4.0

func (s *Server) TopaqueLoginInit(
	ke1 *message.KE1,
	record *ClientRecord,
	threshold int,
	options ...ServerLoginInitOptions,
) (*message.KE2, error)

TODO: Modify LoginInit(ke1, record) to support topaque LoginInit responds to a KE1 message with a KE2 message a client record.

func (*Server) TopaqueRegistrationResponse added in v0.4.0

func (s *Server) TopaqueRegistrationResponse(
	req *message.RegistrationRequest,
	serverPublicKey *group.Element,
	credentialIdentifier []byte,
	threshold int,
) *message.RegistrationResponse

TODO: Modify RegistrationResponse func to support topaque RegistrationResponse returns a RegistrationResponse message to the input RegistrationRequest message and given identifiers.

type ServerLoginInitOptions

type ServerLoginInitOptions struct {
	// EphemeralSecretKey: optional
	EphemeralSecretKey *group.Scalar
	// Nonce: optional
	Nonce []byte
	// NonceLength: optional
	NonceLength uint
}

ServerLoginInitOptions enables setting optional values for the session, which default to secure random values if not set.

Directories

Path Synopsis
Package internal provides values, structures, and functions to operate OPAQUE that are not part of the public API.
Package internal provides values, structures, and functions to operate OPAQUE that are not part of the public API.
ake
Package ake provides high-level functions for the 3DH AKE.
Package ake provides high-level functions for the 3DH AKE.
encoding
Package encoding provides encoding utilities.
Package encoding provides encoding utilities.
keyrecovery
Package keyrecovery provides utility functions and structures allowing credential management.
Package keyrecovery provides utility functions and structures allowing credential management.
masking
Package masking provides the credential masking mechanism.
Package masking provides the credential masking mechanism.
oprf
Package oprf implements the Elliptic Curve Oblivious Pseudorandom Function (EC-OPRF) from https://tools.ietf.org/html/draft-irtf-cfrg-voprf.
Package oprf implements the Elliptic Curve Oblivious Pseudorandom Function (EC-OPRF) from https://tools.ietf.org/html/draft-irtf-cfrg-voprf.
tag
Package tag provides the static tag strings to OPAQUE.
Package tag provides the static tag strings to OPAQUE.
Package message provides message structures for the OPAQUE protocol.
Package message provides message structures for the OPAQUE protocol.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL