Documentation ¶
Overview ¶
Package gopaque implements the OPAQUE protocol. The OPAQUE protocol, described as of this writing in the RFC draft at https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-01, is a protocol that allows a user with a password to register and authenticate with a server without ever giving that server the password. It uses the OPAQUE password authenticated key exchange (PAKE) which uses derived keys for registration authentication. A high-level introduction to OPAQUE (and PAKEs in general) is available at https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/.
This implementation uses the https://github.com/dedis/kyber crypto library. The implementation is intentionally very extensible and exposed, but sensible default implementations are provided for every abstraction. The registration and authentication flows are below, followed by a couple of code examples clarifying usage.
Warning ¶
This was developed by a hobbyist, not a cryptographer. The code has not been reviewed for accuracy or security. No care was taken to obfuscate the errors or prevent timing attacks. Only use after reviewing the code and understanding the implications.
Registration Flow ¶
OPAQUE registration is a 3-message process starting with the user where a user registers with the server. The only input a user needs is the password and after registration, the server has the info to perform authentication.
The steps for a user are:
1 - Create a NewUserRegister with the user ID
2 - Call Init with the password and send the resulting UserRegisterInit to the server
3 - Receive the server's ServerRegisterInit
4 - Call Complete with the server's ServerRegisterInit and send the resulting UserRegisterComplete to the server
The steps for a server are:
1 - Receive the user's UserRegisterInit
2 - Create a NewServerRegister with a private key
3 - Call Init with the user's UserRegisterInit and send the resulting ServerRegisterInit to the user
4 - Receive the user's UserRegisterComplete
5 - Call Complete with the user's UserRegisterComplete and persist the resulting ServerRegisterComplete
Authentication Flow ¶
OPAQUE authentication is intended to be used in conjunction with a key exchange protocol to authenticate a user. Gopaque supports either an external key exchange protocol or one embedded into the auth process. The pure OPAQUE part of the flow is only a 2-message process, but validation with a key exchange often adds a third message. The steps below assume the key exchange is embedded in the auth process instead of being external.
The steps for a user are:
1 - Create a NewUserAuth with an embedded key exchange
2 - Call Init with the password and send the resulting UserAuthInit to the server
3 - Receive the server's ServerAuthComplete
4 - Call Complete with the server's ServerAuthComplete. The resulting UserAuthFinish has user and server key information. This would be the last step if we were not using an embedded key exchange. Since we are, take the resulting UserAuthComplete and send it to the server.
The steps for a server are:
1 - Receive the user's UserAuthInit
2 - Create a NewServerAuth with an embedded key exchange
3 - Call Complete with the user's UserAuthInit and persisted ServerRegisterComplete and send the resulting ServerAuthComplete to the user. This would be the last step if we were not using an embedded key exchange.
4 - Receive the user's UserAuthComplete
5 - Call Finish with the user's UserAuthComplete
Example (Simple) ¶
This simple example doesn't marshal the messages, it just sends them.
crypto := gopaque.CryptoDefault // Registration first...create user side and server side userReg := gopaque.NewUserRegister(crypto, []byte("user foo"), nil) serverReg := gopaque.NewServerRegister(crypto, crypto.NewKey(nil)) // Do the registration steps userRegInit := userReg.Init([]byte("password foo")) serverRegInit := serverReg.Init(userRegInit) userRegComplete := userReg.Complete(serverRegInit) serverRegComplete := serverReg.Complete(userRegComplete) // XXX: Here is where serverRegComplete would be persisted on the server. // Now that we are registered, do an auth. We are using an embedded key // exchange here instead of having our own externally which means we have // one extra "Finish" step. Note, in other cases we might hold on to the // created key exchange so we can get things like the shared secret. userAuth := gopaque.NewUserAuth(crypto, []byte("user foo"), gopaque.NewKeyExchangeSigma(crypto)) serverAuth := gopaque.NewServerAuth(crypto, gopaque.NewKeyExchangeSigma(crypto)) // Do the auth userAuthInit, err := userAuth.Init([]byte("password foo")) panicIfErr(err) // XXX: Here is where serverRegComplete would be looked up by userAuthInit.UserID. serverAuthComplete, err := serverAuth.Complete(userAuthInit, serverRegComplete) panicIfErr(err) userAuthFinish, userAuthComplete, err := userAuth.Complete(serverAuthComplete) panicIfErr(err) err = serverAuth.Finish(userAuthComplete) panicIfErr(err) // Might as well check that the user key is the same as orig if !userReg.PrivateKey().Equal(userAuthFinish.UserPrivateKey) { panic("Key mismatch") }
Output:
Example (WithConnPipe) ¶
This example is a more complex example showing marshalling and using separate user and server-side connections.
package main import ( "encoding/binary" "fmt" "io" "net" "github.com/cretz/gopaque/gopaque" "go.dedis.ch/kyber" ) // This example is a more complex example showing marshalling and using separate // user and server-side connections. func main() { // Create already-connected user/server pipe and a bool to tell when closed userConn, serverConn := net.Pipe() defer userConn.Close() serverClosed := false defer func() { serverClosed = true serverConn.Close() }() // Run the server go func() { if err := RunServer(serverConn); err != nil && !serverClosed { fmt.Printf("Server failed: %v\n", err) } }() // Register a user. The returned key is just for checking later for this // example. In general there is no reason to keep it around as it's sent // back on auth. key, err := UserSideRegister(userConn, "myuser", "mypass") if err != nil { panic(err) } // Now auth the user. We are ignoring the returned key exchange info here // but could capture it and use it's shared secret if we wanted. authInfo, _, err := UserSideAuth(userConn, "myuser", "mypass") if err != nil { panic(err) } // Confirm the key pair is what we registered with if !key.Equal(authInfo.UserPrivateKey) { panic("Invalid key") } } var crypto = gopaque.CryptoDefault func UserSideRegister(c net.Conn, username, password string) (kyber.Scalar, error) { // Create a registration session userReg := gopaque.NewUserRegister(crypto, []byte(username), nil) // Create init message and send it over if err := sendMessage(c, 'r', userReg.Init([]byte(password))); err != nil { return nil, err } // Receive the server message var serverInit gopaque.ServerRegisterInit if err := recvMessage(c, &serverInit); err != nil { return nil, err } // Create user complete message and send it over, then we're done return userReg.PrivateKey(), sendMessage(c, 'r', userReg.Complete(&serverInit)) } func UserSideAuth(c net.Conn, username, password string) (*gopaque.UserAuthFinish, *gopaque.KeyExchangeSigma, error) { // Create auth session w/ built in key exchange kex := gopaque.NewKeyExchangeSigma(crypto) userAuth := gopaque.NewUserAuth(crypto, []byte(username), kex) // Create init message and send it over if userInit, err := userAuth.Init([]byte(password)); err != nil { return nil, nil, err } else if err = sendMessage(c, 'a', userInit); err != nil { return nil, nil, err } // Receive the server message var serverComplete gopaque.ServerAuthComplete if err := recvMessage(c, &serverComplete); err != nil { return nil, nil, err } // Verify user side and since we embedded a key exchange, there is one last // message to send to the server. if userFinish, userComplete, err := userAuth.Complete(&serverComplete); err != nil { return nil, nil, err } else if err = sendMessage(c, 'a', userComplete); err != nil { return nil, nil, err } else { return userFinish, kex, nil } } func RunServer(c net.Conn) error { // This stores the registered users registeredUsers := map[string]*gopaque.ServerRegisterComplete{} // Create a key pair for our server key := crypto.NewKey(nil) // Run forever handling register and auth for { // Get the next user message msgType, msgBytes, err := recvMessageBytes(c) if err != nil { return err } // Handle different message types switch msgType { // Handle registration... case 'r': if regComplete, err := ServerSideRegister(c, key, msgBytes); err != nil { return err } else if username := string(regComplete.UserID); registeredUsers[username] != nil { return fmt.Errorf("Username '%v' already exists", username) } else { registeredUsers[username] = regComplete } // Handle auth... case 'a': if err := ServerSideAuth(c, msgBytes, registeredUsers); err != nil { return err } default: return fmt.Errorf("Unknown message type: %v", msgType) } } } func ServerSideRegister(c net.Conn, key kyber.Scalar, userInitBytes []byte) (*gopaque.ServerRegisterComplete, error) { // Create the registration session serverReg := gopaque.NewServerRegister(crypto, key) // Unmarshal user init, create server init, and send back var userInit gopaque.UserRegisterInit if err := userInit.FromBytes(crypto, userInitBytes); err != nil { return nil, err } else if err = sendMessage(c, 'r', serverReg.Init(&userInit)); err != nil { return nil, err } // Get back the user complete and complete things ourselves var userComplete gopaque.UserRegisterComplete if err := recvMessage(c, &userComplete); err != nil { return nil, err } return serverReg.Complete(&userComplete), nil } func ServerSideAuth(c net.Conn, userInitBytes []byte, registeredUsers map[string]*gopaque.ServerRegisterComplete) error { // Create the server auth w/ an embedded key exchange. We could store this // key exchange if we wanted access to the shared secret afterwards. serverAuth := gopaque.NewServerAuth(crypto, gopaque.NewKeyExchangeSigma(crypto)) // Parse the user init bytes var userInit gopaque.UserAuthInit if err := userInit.FromBytes(crypto, userInitBytes); err != nil { return err } // Load up the registration info regComplete := registeredUsers[string(userInit.UserID)] if regComplete == nil { return fmt.Errorf("Username not found") } // Complete the auth and send it back if serverComplete, err := serverAuth.Complete(&userInit, regComplete); err != nil { return err } else if err = sendMessage(c, 'a', serverComplete); err != nil { return err } // Since we are using an embedded key exchange, we have one more step which // receives another user message and validates it. var userComplete gopaque.UserAuthComplete if err := recvMessage(c, &userComplete); err != nil { return err } return serverAuth.Finish(&userComplete) } // Below is just a very simple, insecure conn messager func sendMessage(c net.Conn, msgType byte, msg gopaque.Marshaler) error { if msgBytes, err := msg.ToBytes(); err != nil { return err } else if _, err = c.Write([]byte{msgType}); err != nil { return err } else if err = binary.Write(c, binary.BigEndian, uint32(len(msgBytes))); err != nil { return err } else if _, err = c.Write(msgBytes); err != nil { return err } return nil } func recvMessage(c net.Conn, msg gopaque.Marshaler) error { if _, msgBytes, err := recvMessageBytes(c); err != nil { return err } else { return msg.FromBytes(crypto, msgBytes) } } func recvMessageBytes(c net.Conn) (msgType byte, msg []byte, err error) { typeAndSize := make([]byte, 5) if _, err = io.ReadFull(c, typeAndSize); err == nil { msgType = typeAndSize[0] msg = make([]byte, binary.BigEndian.Uint32(typeAndSize[1:])) _, err = io.ReadFull(c, msg) } return }
Output:
Index ¶
- Variables
- func DeriveKeyHKDF(c Crypto, priv kyber.Scalar, info []byte) kyber.Scalar
- func OPRFServerStep2(crypto Crypto, alpha kyber.Point, k kyber.Scalar) (v kyber.Point, beta kyber.Point)
- func OPRFUserStep1(crypto Crypto, x []byte) (r kyber.Scalar, alpha kyber.Point)
- func OPRFUserStep3(crypto Crypto, x []byte, r kyber.Scalar, v kyber.Point, beta kyber.Point) (out []byte)
- type AuthEncrypter
- type Crypto
- type CryptoStandard
- func (c *CryptoStandard) AuthDecrypt(priv kyber.Scalar, enc []byte) ([]byte, error)
- func (c *CryptoStandard) AuthEncrypt(priv kyber.Scalar, plain []byte) ([]byte, error)
- func (c *CryptoStandard) DeriveKey(priv kyber.Scalar, info []byte) kyber.Scalar
- func (c *CryptoStandard) HashToPoint(msg []byte) kyber.Point
- func (c *CryptoStandard) NewKey(stream cipher.Stream) kyber.Scalar
- func (c *CryptoStandard) NewKeyFromReader(r io.Reader) kyber.Scalar
- type DeriveKeyArgon
- type ErrUnmarshalMoreData
- type KeyDeriver
- type KeyExchange
- type KeyExchangeInfo
- type KeyExchangeSigma
- func (k *KeyExchangeSigma) NewKeyExchangeMessage(step int) (Marshaler, error)
- func (k *KeyExchangeSigma) ServerKeyExchange2(ke1 Marshaler, info *KeyExchangeInfo) (Marshaler, error)
- func (k *KeyExchangeSigma) ServerKeyExchange4(ke3 Marshaler) error
- func (k *KeyExchangeSigma) UserKeyExchange1() (Marshaler, error)
- func (k *KeyExchangeSigma) UserKeyExchange3(ke2 Marshaler, info *KeyExchangeInfo) (Marshaler, error)
- type KeyExchangeSigmaMsg1
- type KeyExchangeSigmaMsg2
- type KeyExchangeSigmaMsg3
- type KeyGenerator
- type Marshaler
- type PointHasher
- type ServerAuth
- type ServerAuthComplete
- type ServerRegister
- type ServerRegisterComplete
- type ServerRegisterInit
- type Signer
- type SignerSchnorr
- type UserAuth
- type UserAuthComplete
- type UserAuthFinish
- type UserAuthInit
- type UserRegister
- type UserRegisterComplete
- type UserRegisterInit
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var CryptoStandardDefault = &CryptoStandard{ Suite: KyberSuiteDefault, KeyDeriver: DeriveKeyHKDF, Signer: &SignerSchnorr{KyberSuiteDefault}, }
CryptoStandardDefault is the recommended default CryptoStandard impl.
var DeriveKeyArgonDefault = &DeriveKeyArgon{
Time: 1,
Memory: 64 * 1024,
Threads: 4,
}
DeriveKeyArgonDefault is the recommended DeriveKeyArgon impl.
var KyberSuiteDefault = suites.MustFind("Ed25519")
KyberSuiteDefault is the recommended default kyber.Suite impl. This uses the Ed25519 suite which, based on reading, should satisfy the prime-order requirement.
Functions ¶
func DeriveKeyHKDF ¶
DeriveKeyHKDF builds a key for the given parent key and info using HKDF (RFC 5869). This function can be set as the CryptoStandard.KeyDeriver field.
func OPRFServerStep2 ¶
func OPRFServerStep2(crypto Crypto, alpha kyber.Point, k kyber.Scalar) (v kyber.Point, beta kyber.Point)
OPRFServerStep2 is executed on the server side with alpha from user step 1 and a "k" value that's usually either randomly generated on registration or looked up on auth.
func OPRFUserStep1 ¶
OPRFUserStep1 is executed on the user side and builds values to pass to server from the given x (usually a password).
func OPRFUserStep3 ¶
func OPRFUserStep3(crypto Crypto, x []byte, r kyber.Scalar, v kyber.Point, beta kyber.Point) (out []byte)
OPRFUserStep3 is executed on the client side with the original x and r from user step 1. The v and beta values are from server step 2. The result is the PRF output that can be re-obtained deterministically going through the steps again. It is often used to derive keys from.
Types ¶
type AuthEncrypter ¶
type AuthEncrypter interface { // AuthEncrypt performs an encryption of the given plain bytes // authenticated with the given key. It satisfies the OPAQUE requirement // for "key committing" meaning it's infeasible to have the same result // able to be decrypted successfully by different keys. AuthEncrypt(priv kyber.Scalar, plain []byte) ([]byte, error) // AuthDecrypt decrypts what was encrypted with AuthEncrypt with the same // key. AuthDecrypt(priv kyber.Scalar, enc []byte) ([]byte, error) }
AuthEncrypter provides authenticated encryption/decryption that satisfies OPAQUE requirements.
type Crypto ¶
type Crypto interface { suites.Suite KeyGenerator PointHasher KeyDeriver AuthEncrypter Signer }
Crypto is the abstraction of all needed features for OPAQUE.
var CryptoDefault Crypto = CryptoStandardDefault
CryptoDefault is the recommended default Crypto impl.
type CryptoStandard ¶
type CryptoStandard struct { suites.Suite KeyDeriver func(c Crypto, priv kyber.Scalar, info []byte) kyber.Scalar Signer }
CryptoStandard implements Crypto with a given suite + key deriver + signer and using basic implementations of other features.
func (*CryptoStandard) AuthDecrypt ¶
AuthDecrypt implements Crypto.AuthDecrypt.
func (*CryptoStandard) AuthEncrypt ¶
AuthEncrypt implements Crypto.AuthEncrypt.
func (*CryptoStandard) DeriveKey ¶
DeriveKey implements KeyDeriver.DeriveKey. It just defers to the KeyDeriver function.
func (*CryptoStandard) HashToPoint ¶
func (c *CryptoStandard) HashToPoint(msg []byte) kyber.Point
HashToPoint implements PointHasher.HashToPoint.
func (*CryptoStandard) NewKey ¶
func (c *CryptoStandard) NewKey(stream cipher.Stream) kyber.Scalar
NewKey implements KeyGenerator.NewKey.
func (*CryptoStandard) NewKeyFromReader ¶
func (c *CryptoStandard) NewKeyFromReader(r io.Reader) kyber.Scalar
NewKeyFromReader implements KeyGenerator.NewKeyFromReader.
type DeriveKeyArgon ¶
DeriveKeyArgon holds parameters to use for Argon-based key derivation. See the DeriveKey method for more information.
type ErrUnmarshalMoreData ¶
type ErrUnmarshalMoreData struct { // Left is the number of bytes not handled. Left int }
ErrUnmarshalMoreData is when there is more data after unmarshalling.
func (*ErrUnmarshalMoreData) Error ¶
func (e *ErrUnmarshalMoreData) Error() string
type KeyDeriver ¶
type KeyDeriver interface { // DeriveKey creates a deterministic private key for the given parent // private key and an "info" discriminator. DeriveKey(priv kyber.Scalar, info []byte) kyber.Scalar }
KeyDeriver provides DeriveKey to create deterministic keys from other keys.
type KeyExchange ¶
type KeyExchange interface { // UserKeyExchange1 runs on the user side and returns the first key exchange // value to send to server (or nil if there is none). UserKeyExchange1() (Marshaler, error) // ServerKeyExchange2 runs on the server side and is given both the result // of UserKeyExchange1 (if any) and the server registration info for the // user. It returns the value to send back to the user (or nil if there is // none). ServerKeyExchange2(ke1 Marshaler, info *KeyExchangeInfo) (Marshaler, error) // UserKeyExchange3 runs on the user side and is given both the result of // ServerKeyExchange2 (if any) and the decoded user info. It returns the // value sent back to the server. If the result is nil, this is only a // 2-message key exchange instead of a 3-message one and no more steps are // done. UserKeyExchange3(ke2 Marshaler, info *KeyExchangeInfo) (Marshaler, error) // ServerKeyExchange4 runs on the server side and is given the result of // UserKeyExchange3. It is not called if there was no result from // UserKeyExchange3. ServerKeyExchange4(ke3 Marshaler) error // NewKeyExchangeMessage just instantiates the message instance for the // result of the given step number (1-3). NewKeyExchangeMessage(step int) (Marshaler, error) }
KeyExchange describes a 2-step or 3-step key exchange. Although implementations can be used manually, they can also be used embedded into OPAQUE authentication via NewUserAuth and NewServerAuth. Exchanges that are only 2-step can return nil from UserKeyExchange3, otherwise the exchange is considered 3-step.
While implementers will implement all methods, callers should only call the User* or Server* methods depending on which side they are on. The key exchange should be created for each use and never reused.
type KeyExchangeInfo ¶
KeyExchangeInfo is the info given from the OPAQUE process.
type KeyExchangeSigma ¶
type KeyExchangeSigma struct { // Info is the OPAQUE info given during either ServerKeyExchange2 or // UserKeyExchange3. Info *KeyExchangeInfo // ephemeral private key and the other side's ephemeral public key. It is // set during either ServerKeyExchange2 or UserKeyExchange3. SharedSecret kyber.Point // contains filtered or unexported fields }
KeyExchangeSigma is a KeyExchange implementation using the 3-step SIGMA-I protocol as mentioned in the OPAQUE RFC.
func NewKeyExchangeSigma ¶
func NewKeyExchangeSigma(crypto Crypto) *KeyExchangeSigma
NewKeyExchangeSigma creates the SIGMA KeyExchange with the given crypto.
func (*KeyExchangeSigma) NewKeyExchangeMessage ¶
func (k *KeyExchangeSigma) NewKeyExchangeMessage(step int) (Marshaler, error)
NewKeyExchangeMessage implements KeyExchange.NewKeyExchangeMessage.
func (*KeyExchangeSigma) ServerKeyExchange2 ¶
func (k *KeyExchangeSigma) ServerKeyExchange2(ke1 Marshaler, info *KeyExchangeInfo) (Marshaler, error)
ServerKeyExchange2 implements KeyExchange.ServerKeyExchange2.
func (*KeyExchangeSigma) ServerKeyExchange4 ¶
func (k *KeyExchangeSigma) ServerKeyExchange4(ke3 Marshaler) error
ServerKeyExchange4 implements KeyExchange.ServerKeyExchange4.
func (*KeyExchangeSigma) UserKeyExchange1 ¶
func (k *KeyExchangeSigma) UserKeyExchange1() (Marshaler, error)
UserKeyExchange1 implements KeyExchange.UserKeyExchange1.
func (*KeyExchangeSigma) UserKeyExchange3 ¶
func (k *KeyExchangeSigma) UserKeyExchange3(ke2 Marshaler, info *KeyExchangeInfo) (Marshaler, error)
UserKeyExchange3 implements KeyExchange.UserKeyExchange3.
type KeyExchangeSigmaMsg1 ¶
KeyExchangeSigmaMsg1 is the first exchange message sent from the user to the server. It implements Marshaler and is exposed only for debugging.
func (*KeyExchangeSigmaMsg1) FromBytes ¶
func (k *KeyExchangeSigmaMsg1) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes.
func (*KeyExchangeSigmaMsg1) ToBytes ¶
func (k *KeyExchangeSigmaMsg1) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type KeyExchangeSigmaMsg2 ¶
type KeyExchangeSigmaMsg2 struct { ServerExchangePublicKey kyber.Point ServerExchangeSig []byte ServerExchangeMac []byte }
KeyExchangeSigmaMsg2 is the second exchange message sent from the server to the server. It implements Marshaler and is exposed only for debugging.
func (*KeyExchangeSigmaMsg2) FromBytes ¶
func (k *KeyExchangeSigmaMsg2) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes.
func (*KeyExchangeSigmaMsg2) ToBytes ¶
func (k *KeyExchangeSigmaMsg2) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type KeyExchangeSigmaMsg3 ¶
KeyExchangeSigmaMsg3 is the third exchange message sent from the user to the server. It implements Marshaler and is exposed only for debugging.
func (*KeyExchangeSigmaMsg3) FromBytes ¶
func (k *KeyExchangeSigmaMsg3) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes.
func (*KeyExchangeSigmaMsg3) ToBytes ¶
func (k *KeyExchangeSigmaMsg3) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type KeyGenerator ¶
type KeyGenerator interface { // NewKey creates a new key from the given stream. If the stream is nil, the // underlying crypto random stream is used. NewKey(stream cipher.Stream) kyber.Scalar // NewKeyFromReader creates a new key for the given reader. This simply // wraps a io.Reader into a cipher.Stream and calls NewKey. If the reader is // nil, the crypto random stream is used. NewKeyFromReader(r io.Reader) kyber.Scalar }
KeyGenerator generates keys from a given stream or reader. It shares the NewKey signature with go.dedis.ch/kyber/util/key.Generator.
type Marshaler ¶
type Marshaler interface { // ToBytes converts this to a byte slice. If successful should always have // at least one byte. ToBytes() ([]byte, error) // FromBytes populates this from a byte slice. This can return // ErrUnmarshalMoreData if the data is too big. FromBytes(Crypto, []byte) error }
Marshaler is implemented by any message that can be marshaled to/from bytes.
type PointHasher ¶
type PointHasher interface { // HashToPoint hashes the given msg to a point. HashToPoint(msg []byte) kyber.Point }
PointHasher provides HashToPoint to create points from the hash of messages.
type ServerAuth ¶
type ServerAuth struct {
// contains filtered or unexported fields
}
ServerAuth is the server-side authentication session for a registered user. The Complete step takes a user message and gives a message that can be sent back. This should be interspersed with a key exchange which can be embedded or external, see NewServerAuth. If key exchange is embedded and there is a UserAuthComplete, it should be passed to Finish to complete with the embedded key exchange. Otherwise, Finish is not necessary.
This should be created each auth attempt and never reused.
func NewServerAuth ¶
func NewServerAuth(crypto Crypto, embeddedKeyExchange KeyExchange) *ServerAuth
NewServerAuth creates a new auth session for a user. The auth should run alongside a key exchange. If embeddedKeyExchange is nil, the key exchange is expected to be done by the caller with these messages embedded within. If there is an embeddedKeyExchange and it is a 3-step one (meaning there is a UserAuthComplete given back) then Finish should be called. Otherwise, Finish is not necessary.
func (*ServerAuth) Complete ¶
func (s *ServerAuth) Complete(u *UserAuthInit, regInfo *ServerRegisterComplete) (*ServerAuthComplete, error)
Complete combines the received UserAuthInit information with the stored ServerRegisterComplete information to produce the authentication info to give back to the user. If there is no embedded key exchange or it is only a 2-step exchange, this is the last call required. Otherwise, Finish must be called to complete the auth. If there is no embedded key exchange, error is always nil.
func (*ServerAuth) Finish ¶
func (s *ServerAuth) Finish(u *UserAuthComplete) error
Finish simply validates the given user auth complete. This can only be called if there is an embedded key exchange and it is a 3-step exchange. Otherwise, Complete is the last OPAQUE step and the caller can use it in an external key exchange.
type ServerAuthComplete ¶
type ServerAuthComplete struct { ServerPublicKey kyber.Point EnvU []byte V kyber.Point Beta kyber.Point EmbeddedKeyExchangeMessage2 []byte }
ServerAuthComplete is the resulting info from ServerAuth to send back to the user. It implements Marshaler.
func (*ServerAuthComplete) FromBytes ¶
func (s *ServerAuthComplete) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*ServerAuthComplete) ToBytes ¶
func (s *ServerAuthComplete) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type ServerRegister ¶
type ServerRegister struct {
// contains filtered or unexported fields
}
ServerRegister is the server-side session for registration with a user. This should be created for each user registration and never reused. Once created via NewServerRegister, Init should be called with the value from the user side and then the result should be passed back to the user. The user-side's next value should be passed to Complete and the results of Complete should be stored by the server.
func NewServerRegister ¶
func NewServerRegister(crypto Crypto, privateKey kyber.Scalar) *ServerRegister
NewServerRegister creates a ServerRegister with the given key. The key can be the same as used for other registrations.
func (*ServerRegister) Complete ¶
func (s *ServerRegister) Complete(u *UserRegisterComplete) *ServerRegisterComplete
Complete takes the last info from the user and returns a st of data that must be stored by the server.
func (*ServerRegister) Init ¶
func (s *ServerRegister) Init(u *UserRegisterInit) *ServerRegisterInit
Init is called with the first data received from the user side. The response should be sent back to the user.
type ServerRegisterComplete ¶
type ServerRegisterComplete struct { UserID []byte // Same as given originally, can be global ServerPrivateKey kyber.Scalar UserPublicKey kyber.Point EnvU []byte KU kyber.Scalar }
ServerRegisterComplete is the completed set of data that should be stored by the server on successful registration.
type ServerRegisterInit ¶
ServerRegisterInit is the result of Init to be passed to the user. It implements Marshaler.
func (*ServerRegisterInit) FromBytes ¶
func (s *ServerRegisterInit) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*ServerRegisterInit) ToBytes ¶
func (s *ServerRegisterInit) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type Signer ¶
type Signer interface { // Sign signs the given msg with the given priv key. Sign(priv kyber.Scalar, msg []byte) ([]byte, error) // Verify verifies the given sig for the given msg was signed by the private // key for the given pub key. If verification fails, the error is non-nil. Verify(pub kyber.Point, msg, sig []byte) error }
Signer supports signing and verification.
type SignerSchnorr ¶
SignerSchnorr implement Signer for Schnorr signatures.
type UserAuth ¶
type UserAuth struct {
// contains filtered or unexported fields
}
UserAuth is the user-side authentication session for a registered user. The Init step gives a message that can be sent to the server and the response can be given to Complete to complete the authentication. Both the message sent from Init and received from the server are should interspersed in an existing key exchange. The key exchange can be embedded or external, see NewUserAuth.
This should be created each auth attempt and never reused.
func NewUserAuth ¶
func NewUserAuth(crypto Crypto, userID []byte, embeddedKeyExchange KeyExchange) *UserAuth
NewUserAuth creates a new auth session for the given userID. The auth should run alongside a key exchange. If embeddedKeyExchange is nil, the key exchange is expected to be done by the caller with these messages embedded within. This means Complete will not return a message to send back to the server since OPAQUE only requires the two steps. If embeddedKeyExchange is not nil, the key exchange will be done as part of the auth here and shipped alongside the messages. If the key exchange is a 3-step then Complete will provide a message to send back to the server.
func (*UserAuth) Complete ¶
func (u *UserAuth) Complete(s *ServerAuthComplete) (*UserAuthFinish, *UserAuthComplete, error)
Complete takes the server's complete information and decrypts it and returns the user auth information as UserAuthFinish. If there is an embedded key exchange and it's a 3-step one (meaning KeyExchange.UserKeyExchange3 returns a value), then a UserAuthComplete record is returned to send back to the server for completion. Otherwise, the UserAuthComplete is nil and there are no more steps. If there is no embedded key exchange, the external key exchange performed by the caller may do more after this.
type UserAuthComplete ¶
type UserAuthComplete struct {
EmbeddedKeyExchangeMessage3 []byte
}
UserAuthComplete is sent back to the server to complete auth if needed. It implements Marshaler.
func (*UserAuthComplete) FromBytes ¶
func (u *UserAuthComplete) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*UserAuthComplete) ToBytes ¶
func (u *UserAuthComplete) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type UserAuthFinish ¶
UserAuthFinish is the completed information for use once auth is done.
type UserAuthInit ¶
UserAuthInit is the set of data to pass to the server after calling UserAuth.Init. It implements Marshaler.
func (*UserAuthInit) FromBytes ¶
func (u *UserAuthInit) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*UserAuthInit) ToBytes ¶
func (u *UserAuthInit) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type UserRegister ¶
type UserRegister struct {
// contains filtered or unexported fields
}
UserRegister is the user-side session for registration with a server. This should be created for each server registration and never reused. Once created via NewUserRegister, Init can be called with a password that will return a value that can be sent to the server. The value that the server returns can then be used for Complete. The resulting value from Complete is then passed back to the server to complete registration.
func NewUserRegister ¶
func NewUserRegister(crypto Crypto, userID []byte, privateKey kyber.Scalar) *UserRegister
NewUserRegister creates a registration session for the given userID. If privateKey is nil (recommended), it is generated. A key should never be reused on different registrations.
func (*UserRegister) Complete ¶
func (u *UserRegister) Complete(s *ServerRegisterInit) *UserRegisterComplete
Complete is called after receiving the server init results. The result of this call should be passed back to the server.
func (*UserRegister) Init ¶
func (u *UserRegister) Init(password []byte) *UserRegisterInit
Init creates an init message for the password.
func (*UserRegister) PrivateKey ¶
func (u *UserRegister) PrivateKey() kyber.Scalar
PrivateKey gives the key used during registration. This is often generated on NewUserRegister. It is rarely needed because it comes back on authenticate as well.
type UserRegisterComplete ¶
UserRegisterComplete is the set of data to pass to the server after calling Complete. It implements Marshaler.
func (*UserRegisterComplete) FromBytes ¶
func (u *UserRegisterComplete) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*UserRegisterComplete) ToBytes ¶
func (u *UserRegisterComplete) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.
type UserRegisterInit ¶
UserRegisterInit is the set of data to pass to the server after calling UserRegister.Init. It implements Marshaler.
func (*UserRegisterInit) FromBytes ¶
func (u *UserRegisterInit) FromBytes(c Crypto, data []byte) (err error)
FromBytes implements Marshaler.FromBytes. This can return ErrUnmarshalMoreData if the data is too big.
func (*UserRegisterInit) ToBytes ¶
func (u *UserRegisterInit) ToBytes() ([]byte, error)
ToBytes implements Marshaler.ToBytes.