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 ¶
- Constants
- Variables
- func RandomBytes(length int) []byte
- type Client
- func (c *Client) GetConf() *internal.Configuration
- func (c *Client) LoginFinish(ke2 *message.KE2, options ...ClientLoginFinishOptions) (ke3 *message.KE3, exportKey []byte, err error)
- func (c *Client) LoginInit(password, username []byte, options ...ClientLoginInitOptions) *message.KE1
- func (c *Client) RegistrationFinalize(resp *message.RegistrationResponse, ...) (record *message.RegistrationRecord, exportKey []byte)
- func (c *Client) RegistrationInit(password []byte, username []byte, options ...ClientRegistrationInitOptions) *message.RegistrationRequest
- func (c *Client) SessionKey() []byte
- type ClientLoginFinishOptions
- type ClientLoginInitOptions
- type ClientRecord
- type ClientRegistrationFinalizeOptions
- type ClientRegistrationInitOptions
- type Configuration
- func (c *Configuration) Client() (*Client, error)
- func (c *Configuration) Deserializer() (*Deserializer, error)
- func (c *Configuration) Generate() (t1, t2, gs *group.Element)
- func (c *Configuration) GenerateOPRFSeed() []byte
- func (c *Configuration) GetFakeRecord(credentialIdentifier []byte) (*ClientRecord, error)
- func (c *Configuration) KeyGen() (secretKey, publicKey []byte)
- func (c *Configuration) Serialize() []byte
- func (c *Configuration) Server() (*Server, error)
- type Deserializer
- func (d *Deserializer) DecodeAkePrivateKey(encoded []byte) (*group.Scalar, error)
- func (d *Deserializer) DecodeAkePublicKey(encoded []byte) (*group.Element, error)
- func (d *Deserializer) KE1(ke1 []byte) (*message.KE1, error)
- func (d *Deserializer) KE2(ke2 []byte) (*message.KE2, error)
- func (d *Deserializer) KE3(ke3 []byte) (*message.KE3, error)
- func (d *Deserializer) RegistrationRecord(record []byte) (*message.RegistrationRecord, error)
- func (d *Deserializer) RegistrationRequest(registrationRequest []byte) (*message.RegistrationRequest, error)
- func (d *Deserializer) RegistrationResponse(registrationResponse []byte) (*message.RegistrationResponse, error)
- type Group
- type Server
- func (s *Server) ExpectedMAC() []byte
- func (s *Server) GetConf() *internal.Configuration
- func (s *Server) HpakeLoginInit(ke1 *message.KE1, record *ClientRecord, client *http.Client, ...) (*message.KE2, error)
- func (s *Server) HpakeRegistrationResponse(req *message.RegistrationRequest, serverPublicKey *group.Element, ...) *message.RegistrationResponse
- func (s *Server) HpakeSetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte, ...) error
- func (s *Server) LoginFinish(ke3 *message.KE3) error
- func (s *Server) LoginInit(ke1 *message.KE1, record *ClientRecord, options ...ServerLoginInitOptions) (*message.KE2, error)
- func (s *Server) RegistrationResponse(req *message.RegistrationRequest, serverPublicKey *group.Element, ...) *message.RegistrationResponse
- func (s *Server) SerializeState() []byte
- func (s *Server) SessionKey() []byte
- func (s *Server) SetAKEState(state []byte) error
- func (s *Server) SetKeyMaterial(serverIdentity, serverSecretKey, serverPublicKey, oprfSeed []byte) error
- func (s *Server) TopaqueLoginInit(ke1 *message.KE1, record *ClientRecord, threshold int, ...) (*message.KE2, error)
- func (s *Server) TopaqueRegistrationResponse(req *message.RegistrationRequest, serverPublicKey *group.Element, ...) *message.RegistrationResponse
- type ServerLoginInitOptions
Examples ¶
Constants ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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) 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 ¶
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 ¶
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 ¶
SerializeState returns the internal state of the AKE server serialized to bytes.
func (*Server) SessionKey ¶
SessionKey returns the session key if the previous call to LoginInit() was successful.
func (*Server) SetAKEState ¶
SetAKEState sets the internal state of the AKE server from the given bytes.
func (*Server) SetKeyMaterial ¶
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. |