Documentation
¶
Overview ¶
Package srp is SRP-6a, the password-authenticated key exchange of RFC 2945 as RFC 5054 fixes its arithmetic, over the 3072-bit group of RFC 5054 Appendix A and SHA-256 (docs/decisions/0076).
The server stores a salt and a verifier and never the password. A client proves it knows the password without sending it, and the server proves it holds the verifier. A guess is one run of the protocol against the live server: the transcript of a failed run lets nobody test passwords offline, which a stolen verifier does (the verifier is a password hash with a fast function, so the password it stands for must be machine-generated).
Three things differ from RFC 5054 and are spelled out in the decision: the hash is SHA-256 where the RFC says SHA-1; the session key and the two proofs are RFC 2945's with fixed-width group elements; and the private key x is stretched with PBKDF2 (Iterations rounds, which the server tells the client with the salt). Params takes the hash, the group and the rounds (none is the RFC's own x), so the RFC's test vectors (SHA-1, the 1024-bit group) run through the same code the 3072-bit SHA-256 default does.
Every group element crosses the wire as lower-case hexadecimal of its fixed-width big-endian form (384 bytes for the default group).
Index ¶
- Constants
- Variables
- func CheckPassword(password string) error
- func NewSalt() []byte
- type Client
- type Credential
- type Params
- func (p Params) Decoy(secret []byte, username string) (salt, verifier []byte)
- func (p Params) Iterations() int
- func (p Params) Modulus() []byte
- func (p Params) NewClient(username, password string, random io.Reader) (*Client, error)
- func (p Params) NewCredential(salt []byte, username, password string) Credential
- func (p Params) NewServer(username string, salt, verifier []byte, random io.Reader) (*Server, error)
- func (p Params) ParseCredential(data []byte) (Credential, error)
- func (p Params) ResumeClient(username, password string, private []byte) *Client
- func (p Params) ResumeServer(username string, salt, verifier, private []byte) *Server
- func (p Params) Size() int
- func (p Params) Verifier(salt []byte, username, password string) []byte
- func (p Params) WithIterations(n int) Params
- type Server
Constants ¶
const ( MinIterations = 100_000 MaxIterations = 10_000_000 )
The bounds a client accepts for the number a server names: a server cannot turn the stretching off, and cannot make the page spin for minutes.
const Iterations = 600_000
Iterations is the number of PBKDF2-HMAC-SHA256 rounds the default parameters stretch the password with (OWASP's figure for SHA-256, 2023). A credential records the number it was made with, and the server hands it to the client with the salt, so it can be raised later without a flag day.
const MinPasswordBits = 128
MinPasswordBits is the strength a password must be estimated at to be given to CheckPassword's callers: a stolen verifier is an offline target, and only a long, random password survives that.
const PrivateBytes = 32
PrivateBytes is the size of the ephemeral private values a and b: 256 bits, the floor RFC 5054 asks for.
Variables ¶
var ( // ErrBadPublic is a public value (A or B) that is zero modulo N, which would // fix the shared secret to zero whatever the password is. ErrBadPublic = errors.New("srp: invalid public value") // ErrBadProof is a proof (M1 or M2) that does not match. ErrBadProof = errors.New("srp: proof does not match") )
Errors. A caller maps every one of them to one refusal: the party on the other end is not told which check failed.
var Default = func() Params { n, ok := new(big.Int).SetString(group3072N, 16) if !ok { panic("srp: the group constant does not parse") } return New(n, big.NewInt(group3072G), sha256.New).WithIterations(Iterations) }()
Default is what sluis uses: the 3072-bit group of RFC 5054 with SHA-256, and x stretched with Iterations rounds.
Functions ¶
func CheckPassword ¶
CheckPassword refuses a password that is too weak to sit behind a verifier: not printable ASCII (the page and the tools would have to agree on a Unicode normalisation), shorter than 20 characters, of fewer than 10 distinct characters, or estimated below MinPasswordBits. The estimate is the length times log2 of the pool of the character classes it uses (lower case, upper case, digits, the rest of printable ASCII), which over-rates a phrase a person chose: use a generated password. It is a floor, not a guarantee.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the client's half of one run. sluis's own client is the browser's script; this one is the reference the script is held to, and what `sluisctl` and the tests use.
func (*Client) Respond ¶
Respond takes the server's salt, B and the number of PBKDF2 rounds it names, and returns the proof M1. A server that names fewer than MinIterations (but not none, for parameters that stretch nothing) or more than MaxIterations is refused.
func (*Client) VerifyServer ¶
VerifyServer checks the server's proof M2.
type Credential ¶
Credential is an account as a server stores it: the salt, the verifier and the number of PBKDF2 rounds x was stretched with. It is not the password and cannot sign anybody in, but it lets whoever holds it test guesses offline (at Iterations hashes a guess), so it is kept as a secret. The salt is public: the server hands it to anyone who starts an exchange.
func (Credential) Document ¶
func (c Credential) Document() []byte
Document is the credential as it is stored: {"salt":"<hex>","verifier":"<hex>","iterations":<n>}.
type Params ¶
type Params struct {
// contains filtered or unexported fields
}
Params is a group, a hash and the stretching of x.
func (Params) Decoy ¶
Decoy returns the salt and verifier (with the default rounds) the server answers with for a username it has no account for: both are derived from a secret the server keeps and the username, so they are the same on every request, on every process that has the secret, and look like a real account's. A run against them ends in ErrBadProof, as a wrong password does.
func (Params) Iterations ¶
Iterations is the number of rounds new credentials are made with.
func (Params) NewCredential ¶
func (p Params) NewCredential(salt []byte, username, password string) Credential
NewCredential derives the credential of a username and password under a salt. The salt MUST be random (NewSalt) and independent of the password: it is served to anybody who starts an exchange, so a salt derived from the password would let that anybody test guesses against it with no verifier at all. Keep the salt that goes with a password (a program that recomputes the credential on every run keeps its salt with the password it belongs to).
func (Params) NewServer ¶
func (p Params) NewServer(username string, salt, verifier []byte, random io.Reader) (*Server, error)
NewServer starts a run for a stored salt and verifier.
func (Params) ParseCredential ¶
func (p Params) ParseCredential(data []byte) (Credential, error)
ParseCredential reads a stored credential and refuses one that is not for these parameters: a verifier of another width, a short salt, a verifier that is zero modulo N or not below it, or rounds outside [MinIterations, MaxIterations] (or none, for parameters that stretch nothing).
func (Params) ResumeClient ¶
ResumeClient is a client with a chosen private value: for the test vectors.
func (Params) ResumeServer ¶
ResumeServer continues a run whose private value was kept.
func (Params) Verifier ¶
Verifier is g^x mod N, in its fixed-width form: what a server stores beside the salt in place of the password. The salt is random and has nothing to do with the password: the server hands it to anyone who asks.
func (Params) WithIterations ¶
WithIterations returns the parameters stretching x with PBKDF2 for n rounds.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the server's half of one run. What has to outlive the first request is the private value b (Server.Private); Params.ResumeServer rebuilds the rest, so a run can continue on another process.
func (*Server) Verify ¶
Verify checks the client's A and proof M1. It returns the server's proof M2 and the session key, or an error: ErrBadPublic for A that is zero modulo N (checked first, before anything is computed from it) and ErrBadProof for an M1 that does not match. The comparison takes the same time whatever the first differing byte.