A modern SRP implementation for Node.js and Web Browsers.
npm install --save secure-remote-password
When creating an account with the server, the client will provide a salt and a verifier for the server to store. They are calculated by the client as follows:
const srp = require('secure-remote-password/client')
// These should come from the user signing up
const username = 'linus@folkdatorn.se'
const password = '$uper$ecure'
const salt = srp.generateSalt()
const privateKey = srp.derivePrivateKey(salt, username, password)
const verifier = srp.deriveVerifier(privateKey)
console.log(salt)
//=> FB95867E...
console.log(verifier)
//=> 9392093F...
// Send `username`, `salt` and `verifier` to the server
note: derivePrivateKey
is provided for completeness with the SRP 6a specification. It is however recommended to use some form of "slow hashing", like PBKDF2, to reduce the viability of a brute force attack against the verifier.
Authenticating with the server involves mutliple steps.
1 - The client generates a secret/public ephemeral value pair.
const srp = require('secure-remote-password/client')
// This should come from the user logging in
const username = 'linus@folkdatorn.se'
const clientEphemeral = srp.generateEphemeral()
console.log(clientEphemeral.public)
//=> DE63C51E...
// Send `username` and `clientEphemeral.public` to the server
2 - The server receives the client's public ephemeral value and username. Using the username we retrieve the salt
and verifier
from our user database. We then generate our own ephemeral value pair.
note: if no user can be found in the database, a bogus salt and ephemeral value should be returned, to avoid leaking which users have signed up
const srp = require('secure-remote-password/server')
// This should come from the user database
const salt = 'FB95867E...'
const verifier = '9392093F...'
const serverEphemeral = srp.generateEphemeral(verifier)
console.log(serverEphemeral.public)
//=> DA084F5C...
// Store `serverEphemeral.secret` for later use
// Send `salt` and `serverEphemeral.public` to the client
3 - The client can now derive the shared strong session key, and a proof of it to provide to the server.
const srp = require('secure-remote-password/client')
// This should come from the user logging in
const password = '$uper$ecret'
const privateKey = srp.derivePrivateKey(salt, username, password)
const clientSession = srp.deriveSession(clientEphemeral.secret, serverPublicEphemeral, salt, username, privateKey)
console.log(clientSession.key)
//=> 2A6FF04E...
console.log(clientSession.proof)
//=> 6F8F4AC3
// Send `clientSession.proof` to the server
4 - The server is also ready to derive the shared strong session key, and can verify that the client has the same key using the provided proof.
const srp = require('secure-remote-password/server')
// Previously stored `serverEphemeral.secret`
const serverSecretEphemeral = '784D6E83...'
const serverSession = srp.deriveSession(serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof)
console.log(serverSession.key)
//=> 2A6FF04E...
console.log(serverSession.proof)
//=> 92561B95
// Send `serverSession.proof` to the client
5 - Finally, the client can verify that the server have derived the correct strong session key, using the proof that the server sent back.
const srp = require('secure-remote-password/client')
srp.verifySession(clientEphemeral.public, clientSession, serverSessionProof)
// All done!
Client
const Client = require('secure-remote-password/client')
Client.generateSalt() => string
Generate a salt suitable for computing the verifier with.
Client.derivePrivateKey(salt, username, password) => string
Derives a private key suitable for computing the verifier with.
Client.deriveVerifier(privateKey) => string
Derive a verifier to be stored for subsequent authentication atempts.
Client.generateEphemeral() => { secret: string, public: string }
Generate ephemeral values used to initiate an authentication session.
Client.deriveSession(clientSecretEphemeral, serverPublicEphemeral, salt, username, privateKey) => { key: string, proof: string }
Comptue a session key and proof. The proof is to be sent to the server for verification.
Client.verifySession(clientPublicEphemeral, clientSession, serverSessionProof) => void
Verifies the server provided session proof. Throws an error if the session proof is invalid.
Server
const Server = require('secure-remote-password/server')
generateEphemeral(verifier)
Generate ephemeral values used to continue an authentication session.
deriveSession(serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof)
Comptue a session key and proof. The proof is to be sent to the client for verification.
Throws an error if the session proof from the client is invalid.