Help us shape the future of Go! Take the 2026 Go Developer Survey

srp

package
v1.78.2 Latest Latest
Warning

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

Go to latest
Published: Oct 24, 2026 License: MIT Imports: 15 Imported by: 0

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

View Source
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.

View Source
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.

View Source
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.

View Source
const PrivateBytes = 32

PrivateBytes is the size of the ephemeral private values a and b: 256 bits, the floor RFC 5054 asks for.

Variables

View Source
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.

View Source
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

func CheckPassword(password string) error

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.

func NewSalt

func NewSalt() []byte

NewSalt returns a random salt.

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) Key

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

Key is the session key, after Client.Respond.

func (*Client) Public

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

Public is A, to send first.

func (*Client) Respond

func (c *Client) Respond(salt, b []byte, rounds int) ([]byte, error)

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

func (c *Client) VerifyServer(m2 []byte) error

VerifyServer checks the server's proof M2.

type Credential

type Credential struct {
	Salt       []byte
	Verifier   []byte
	Iterations int
}

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 New

func New(n, g *big.Int, h func() hash.Hash) Params

New returns the parameters for the prime n, the generator g and the hash h.

func (Params) Decoy

func (p Params) Decoy(secret []byte, username string) (salt, verifier []byte)

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

func (p Params) Iterations() int

Iterations is the number of rounds new credentials are made with.

func (Params) Modulus

func (p Params) Modulus() []byte

Modulus is N, in its fixed-width form.

func (Params) NewClient

func (p Params) NewClient(username, password string, random io.Reader) (*Client, error)

NewClient starts a run for a username and password.

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

func (p Params) ResumeClient(username, password string, private []byte) *Client

ResumeClient is a client with a chosen private value: for the test vectors.

func (Params) ResumeServer

func (p Params) ResumeServer(username string, salt, verifier, private []byte) *Server

ResumeServer continues a run whose private value was kept.

func (Params) Size

func (p Params) Size() int

Size is the width, in bytes, of a group element on the wire.

func (Params) Verifier

func (p Params) Verifier(salt []byte, username, password string) []byte

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

func (p Params) WithIterations(n int) Params

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) Private

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

Private is b, the value to keep between the two requests of a run.

func (*Server) Public

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

Public is B, to send with the salt.

func (*Server) Verify

func (s *Server) Verify(a, m1 []byte) (m2, key []byte, err error)

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.

Jump to

Keyboard shortcuts

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