Documentation
¶
Overview ¶
Package srp is an implementation of the Secure Remote Password protocol as defined in RFC5054 and RFC2945.
Index ¶
- Constants
- Variables
- func NFKD(str string) string
- func NewSalt() []byte
- func RFC5054KDF(username, password string, salt []byte) ([]byte, error)deprecated
- type Client
- type Group
- type KDF
- type Params
- type Server
- func (s *Server) B() []byte
- func (s *Server) CheckM1(M1 []byte) (bool, error)
- func (s *Server) ComputeM2() ([]byte, error)
- func (s *Server) MarshalJSON() ([]byte, error)
- func (s *Server) Reset(params *Params, username string, salt, verifier []byte) error
- func (s *Server) Save() ([]byte, error)
- func (s *Server) SessionKey() ([]byte, error)
- func (s *Server) SetA(public []byte) error
- func (s *Server) UnmarshalJSON(data []byte) error
- type Triplet
Examples ¶
Constants ¶
const SaltLength = 12
SaltLength represents the default length for a salt created with NewSalt.
Variables ¶
var ( RFC5054Group2048 = &Group{ ID: "14", Generator: big.NewInt(2), N: mustParseHex(hex2048), ExponentSize: 27, } RFC5054Group3072 = &Group{ ID: "15", Generator: big.NewInt(5), N: mustParseHex(hex3072), ExponentSize: 32, } RFC5054Group4096 = &Group{ ID: "16", Generator: big.NewInt(5), N: mustParseHex(hex4096), ExponentSize: 38, } RFC5054Group6144 = &Group{ ID: "17", Generator: big.NewInt(5), N: mustParseHex(hex6144), ExponentSize: 43, } RFC5054Group8192 = &Group{ ID: "18", Generator: big.NewInt(19), N: mustParseHex(hex8192), ExponentSize: 48, } )
Diffie-Hellman group 14, 15, 16, 17 and 18 defined in RFC5054.
var ErrClientNotReady = errors.New("server's public ephemeral key (B) must be set first")
ErrClientNotReady is returned when the client is not ready for the invoked action.
var ErrServerNoReady = errors.New("client's public ephemeral key (A) must be set first")
ErrServerNoReady is returned when the server is not ready for the invoked action.
var RFC5054Group1024 = &Group{ ID: "2", Generator: big.NewInt(2), N: mustParseHex(hex1024), ExponentSize: 32, }
Diffie-Hellman group 2.
Deprecated: This group is not recommended for production-use.
var RFC5054Group1536 = &Group{ ID: "5", Generator: big.NewInt(2), N: mustParseHex(hex1536), ExponentSize: 23, }
Diffie-Hellman group 5.
Deprecated: This group is not recommended for production-use.
Functions ¶
func NFKD ¶
NFKD returns str as a NFKD-normalized unicode string, stripped of all leading and trailing spaces.
func RFC5054KDF
deprecated
RFC5054KDF is the KDF defined in RFC5054.
x = SHA(s | SHA(U | ":" | p))
Deprecated: This KDF function is only provided for compatibility with RFC5054 and for testing purposes. It is not recommended for production use. Instead, use a key derivation function KDF designed for password hashing such as Argon2, Scrypt or PBKDF2.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client represents the client-side perspective of an SRP session.
Example ¶
Example of a client session.
var ( username = "alice@example.com" password = "some-password" ) // Request the user's salt from the server. // The server should send it to whoever asks. salt := Receive() // Create a client, specifying the same params used on the server. client, err := NewClient(params, username, password, salt) if err != nil { log.Fatal(err) } // Send A to the server. A := client.A() Send(A) // Receive B from the server, // then configure it on the client. B := Receive() if err := client.SetB(B); err != nil { log.Fatalf("invalid B received from the server: %v", err) } // Compute the proof M1, // then send it to the server. M1, err := client.ComputeM1() if err != nil { log.Fatalf("failed to compute M1: %v", err) } Send(M1) // If the server accepts the client's proof (M1), it will // send a proof of their own (M2). M2 := Receive() valid, err := client.CheckM2(M2) if err != nil { log.Fatalf("failed to verify server proof M2: %v", err) } if valid == false { log.Fatalf("server is not authentic: %v", err) } // At this stage, the client and the server // have proved to each other that they know // the same secret. // // They can both consider each other as authentic // and legitimate. // They also share a common key they both derived independently // from the process K, err := client.SessionKey() if err != nil { log.Fatalf("failed to access shared session key: %v", err) } // K can optionally be used to encrypt/decrypt all exchanges between // them moving forward. SendEncrypted(K, []byte("hello, world!"))
Output:
func (*Client) SessionKey ¶
SessionKey returns the session key that will be shared with the server.
type Params ¶
Params represents the DH group, the hash and key derivation function that a client and server jointly agreed to use.
import ( "runtime" "github.com/posterity/srp" "golang.org/x/crypto/argon2" _ "crypto/sha256" ) func KDFArgon2(username, password string, salt []byte) ([]byte, error) { p := []byte(username + ":" + password) key := argon2.IDKey(p, salt, 3, 256 * 1048576, runtime.NumCPU(), 32) return key, nil } var params = &srp.Params{ Name: "DH16–SHA256–Argon2", Group: srp.RFC5054Group4096, Hash: crypto.SHA256, KDF: KDFArgon2, }
Example ¶
This example shows how to create a Params instance that ensures strict compatibility with RFC5054 (Not recommended in production).
params := &Params{ Group: RFC5054Group1024, Hash: crypto.SHA1, KDF: RFC5054KDF, } _, err := NewClient(params, "username", "p@$$w0rd", NewSalt()) if err != nil { log.Fatal(err) }
Output:
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server represents the server-side perspective of an SRP session.
Example ¶
Example of a server session.
// Load the user's Triplet from the persistent // storage. var user Triplet = Query("alice@example.com") // Create a server, specifying the same params used on the client. server, err := NewServer(params, user.Username(), user.Salt(), user.Verifier()) if err != nil { log.Fatal(err) } // Send B to the client. B := server.B() Send(B) // Receive A from the client, // then configure it on the server. A := Receive() if err := server.SetA(A); err != nil { log.Fatalf("invalid A received from the client: %v", err) } // The server needs to verify the client's proof first, // so it must wait to receive M1. M1 := Receive() // Verify the client proof M1. // The process must be interrupted if valid is false, or // an error occurred. valid, err := server.CheckM1(M1) if err != nil { log.Fatalf("failed to verify client proof M1: %v", err) } if valid == false { log.Fatalf("client is not authentic: %v", err) } // The client proved they're authentic, so it's safe // to compute the server proof (M2) and send it over. M2, err := server.ComputeM2() if err != nil { log.Fatalf("failed to compute M1: %v", err) } Send(M2) // At this stage, the server should consider // the client as authentic and requests from it // should be fulfilled. // They both share a common key they both derived // independently from the process, K, err := server.SessionKey() if err != nil { log.Fatalf("failed to access session key: %v", err) } // K can optionally be used to encrypt/decrypt all exchanges between // them moving forward. SendEncrypted(K, []byte("hello, world!"))
Output:
func RestoreServer ¶
RestoreServer restores a server from a previous state obtained with Server.Save.
func (*Server) ComputeM2 ¶
ComputeM2 returns the proof (M2) which should be sent to the client.
An error is returned if the client's proof (M1) has not been checked by calling the s.CheckM1 method first.
func (*Server) MarshalJSON ¶
MarshalJSON returns a JSON object representing the current state of s.
func (*Server) Save ¶
Save encodes the current state of s in a JSON object. Use RestoreServer to restore a previously saved state.
func (*Server) SessionKey ¶
SessionKey returns the session key that will be shared with the client.
An error is returned if the client's proof (M1) has not been checked by calling the s.CheckM1 method first.
func (*Server) UnmarshalJSON ¶
UnmarshalJSON restores from an existing state object obtained with MarshalJSON.
type Triplet ¶
type Triplet []byte
Triplet holds the parameters the server should store in a single byte array.
A triplet is structured as following:
+------------------------+ | usernameLen (1) | +------------------------+ | username (usernameLen) | +------------------------+ | saltLen (1) | +------------------------+ | salt (saltLen) | +------------------------+ | verifier | +------------------------+
func ComputeVerifier ¶
ComputeVerifier computes a verifier value from the user's username, password and salt.
The function is called client-side to generate a triplet containing the information that should be sent to the server over a secure connection (TLS), and stored in a secure persistent-storage (e.g. database).
Example ¶
The verifier is calculated on the client, and sent to the server for storage along with the username and salt used to compute it as a triplet.
const ( username = "bob@example.com" password = "p@$$w0rd" ) tp, err := ComputeVerifier(params, username, password, NewSalt()) if err != nil { log.Fatalf("failed to compute verifier: %v", err) } // The verifier can be accessed via the returned triplet tp // as tp.Verifier(). // On the server, it's recommended to store the verifier along with // the username and the salt used to compute it, so sending the whole // triplet ([]byte) is more appropriate. Send(tp)
Output:
func NewTriplet ¶
NewTriplet returns a new Triplet instance from the given username, verifier and salt.
NewTriplet panics if the length of username or salt exceeds math.MaxUint8.
func (Triplet) MarshalJSON ¶
MarshalJSON returns a JSON representation of t that includes the username and the salt, but not the verifier.
{ "username": "alice", "salt": "EzDH8afmICl6Xxsv", }