The SRP Authentication and Key Exchange System
Extracted elements (19)
N must be a safe prime of the form N = 2q+1 (where q is also prime) for maximum security, and g must be a generator modulo N. This ensures that for any X where 0 < X < N, there exists x such that g^x % N == X, providing the full Diffie-Hellman security guarantees.
SHA_Interleave is used instead of plain SHA to produce a 320-bit session key rather than the 160-bit output of SHA1. This doubles the key length for the derived session key without requiring a different hash function.
SRP accomplishes mutual authentication and key exchange in a single Diffie-Hellman-style round of computation, making it feasible for both interactive and non-interactive authentication across a wide range of Internet protocols without requiring trusted key servers or certificate infrastructures.
SRP stores password verifiers rather than passwords or simple hashes, so even if the host's password database is publicly revealed, an attacker still requires an expensive dictionary search using exponentiation (much more costly than the hash-based checks used by most UNIX systems).
SRP can be used with hash functions other than SHA-1, including MD5 (used in earlier versions) or HMAC constructions. Any hash function used should produce at least 16 bytes of output and exhibit significant nonlinear avalanche on small input changes.
The values of N and g must be agreed upon by both parties; they can be set in advance or the host can supply them in the first message along with the salt. The specific format of messages (including padding) is left to the protocol implementor.
If the client responded correctly, the server MUST respond with its hash value H(A | M | K) to allow mutual authentication.
The client MUST abort authentication if B % N is zero.
The host MUST abort the authentication attempt if A % N is zero. The host MUST send B after receiving A from the client, never before.
The server MUST abort and signal an error if the client's proof M does not match before it attempts to answer the client's challenge. Not doing so could compromise the security of the user's password.
Authentication proof is computed as M = H(H(N) XOR H(g) | H(U) | s | A | B | K). The server responds with H(A | M | K) to prove knowledge of the same session key K.
SHA_Interleave generates a 320-bit session key from the shared secret S. Leading zero bytes are stripped; if the result has odd length, the first byte is also removed. Even-indexed bytes are hashed with SHA1 to get G, odd-indexed bytes to get H; the output interleaves G and H byte by byte.
The host stores user passwords as triplets of the form { <username>, <password verifier>, <salt> }. The verifier is computed as v = g^x % N where x = SHA(<salt> | SHA(<username> | ':' | <raw password>)). This ensures the host never stores the raw password.
The parameter u is a 32-bit unsigned integer derived from the first 32 bits of the SHA1 hash of B (MSB first). It is used in the key computation as S = (B - g^x)^(a + u*x) % N on the client side.
The SRP-SHA1 exchange proceeds in two round trips: first the client sends username U and the host responds with salt s; then the client sends A = g^a % N and the host responds with B = (v + g^b) % N. Both sides derive the shared session key S independently.
Even if the host's password database is revealed, an attacker still needs an expensive dictionary search using modular exponentiation to recover passwords, making offline attacks significantly harder than against traditional password storage schemes.
SRP protects against both passive eavesdropping and active dictionary attacks by never transmitting the password or any value that directly reveals it. The private values a and b have constraints similar to Diffie-Hellman private exponents; u should not be shorter than 32 bits.
SRP resists active network attacks; the securely exchanged session key K can be used to protect the session against hijacking and provide confidentiality, enabling security layers (privacy and/or integrity protection) during the session.
Integers are encoded as big-endian byte strings (base 256, most significant digit first). An n-byte string S converts to integer i = S[n-1] + 256*S[n-2] + ... + 256^(n-1)*S[0]. Leading zero bytes are stripped on conversion back to integer.