A high-performance Secure Remote Password (SRP-6a) protocol implementation for Node.js, powered by Rust.
This library provides a blazing-fast implementation of the Secure Remote Password (SRP-6a) protocol using Rust with N-API bindings for Node.js.
SRP is a password-authenticated key exchange protocol that allows secure authentication without sending passwords over the network.
- 🚀 High Performance: Up to 15x faster than JavaScript implementations
- 🔒 Full SRP-6a Protocol: Complete implementation of the SRP-6a protocol
- 💻 Native Node.js Bindings: Seamless integration with Node.js applications
- 🔄 API Compatible: Drop-in replacement for
secure-remote-password
- 🧪 Thoroughly Tested: Comprehensive test suite ensures correctness
- 🔐 RFC 5054 Support: All five parameter groups (1024, 1536, 2048, 3072, 4096 bits)
Install using your preferred package manager:
# npm
npm install @ruc-cheese/node-srp-rs
# yarn
yarn add @ruc-cheese/node-srp-rs
# pnpm
pnpm add @ruc-cheese/node-srp-rs
import { Client, Server, SrpGroup } from '@ruc-cheese/node-srp-rs';
// Create client and server instances with default parameters (2048-bit group)
const client = new Client();
const server = new Server();
// Or specify a different parameter group
const client4096 = new Client(SrpGroup.RFC5054_4096);
const server4096 = new Server(SrpGroup.RFC5054_4096);
// Registration phase
const salt = client.generateSalt();
const privateKey = client.derivePrivateKey(salt, username, password);
const verifier = client.deriveVerifier(privateKey);
// Store salt and verifier on server
// ...
// Authentication phase
// 1. Client generates ephemeral key pair
const clientEphemeral = client.generateEphemeral();
// 2. Server generates ephemeral key pair using stored verifier
const serverEphemeral = server.generateEphemeral(verifier);
// 3. Client computes session and proof
const clientSession = client.deriveSession(
clientEphemeral.secret,
serverEphemeral.public,
salt,
username,
privateKey
);
// 4. Server verifies client proof and generates server proof
const serverSession = server.deriveSession(
serverEphemeral.secret,
clientEphemeral.public,
salt,
username,
verifier,
clientSession.proof
);
// 5. Client verifies server proof
client.verifySession(
clientEphemeral.public,
clientSession,
serverSession.proof
);
// Both client and server now have matching session keys
console.log(clientSession.key === serverSession.key); // true
const { Client, Server, SrpGroup } = require('@ruc-cheese/node-srp-rs');
// Same usage as above
-
new Client(group?)
: Creates a new client instance with optional parameter group -
client.generateSalt()
: Generates a random salt for password hashing -
client.derivePrivateKey(salt, username, password)
: Derives private key from credentials -
client.deriveVerifier(privateKey)
: Generates a password verifier from private key -
client.generateEphemeral()
: Creates client ephemeral key pair -
client.deriveSession(secret, serverPublic, salt, username, privateKey, [clientPublic])
: Computes session key and proof -
client.verifySession(clientPublic, clientSession, serverProof)
: Verifies server session proof
-
new Server(group?)
: Creates a new server instance with optional parameter group -
server.generateEphemeral(verifier)
: Creates server ephemeral key pair -
server.deriveSession(secret, clientPublic, salt, username, verifier, clientProof)
: Verifies client proof and generates server proof
The library supports all five parameter groups defined in RFC 5054:
-
SrpGroup.RFC5054_1024
: 1024-bit group -
SrpGroup.RFC5054_1536
: 1536-bit group -
SrpGroup.RFC5054_2048
: 2048-bit group (default) -
SrpGroup.RFC5054_3072
: 3072-bit group -
SrpGroup.RFC5054_4096
: 4096-bit group
You can also create a group from a bit size using the utility function:
const { srp_group_from_value } = require('@ruc-cheese/node-srp-rs');
try {
const group = srp_group_from_value(4096);
const client = new Client(group);
// ...
} catch (err) {
console.error('Invalid SRP group size');
}
This Rust implementation significantly outperforms JavaScript SRP implementations:
Operation | JS (jsbn) Time/Op | JS (BigInt) Time/Op | Rust Time/Op | BigInt vs jsbn | Rust vs jsbn |
---|---|---|---|---|---|
Salt Generation | 0.013 ms | 0.006 ms | 0.001 ms | 2.07x | 10.60x |
Private Key Derivation | 0.015 ms | 0.009 ms | 0.003 ms | 1.55x | 5.14x |
Verifier Generation | 8.890 ms | 2.217 ms | 0.575 ms | 4.01x | 15.45x |
Client Ephemeral Generation | 9.221 ms | 2.600 ms | 0.654 ms | 3.55x | 14.11x |
Server Ephemeral Generation | 9.063 ms | 2.238 ms | 0.730 ms | 4.05x | 12.41x |
Client Session Derivation | 36.613 ms | 9.382 ms | 2.638 ms | 3.90x | 13.88x |
Server Session Derivation | 27.177 ms | 6.852 ms | 1.855 ms | 3.97x | 14.65x |
Complete Authentication Flow | 84.076 ms | 19.956 ms | 5.273 ms | 4.21x | 15.95x |
Implementation Comparison:
- JS (jsbn): Original JavaScript implementation using the jsbn library for BigInt operations
- JS (BigInt): Improved JavaScript implementation using native BigInt in Node.js
- Rust: This library's Rust implementation with NAPI bindings
The Rust implementation is on average 12.77x faster than the original JavaScript implementation and 3.75x faster than the BigInt-based JavaScript implementation.
MIT
- Based on the work of secure-remote-password
Contributions are welcome! Please feel free to submit a Pull Request.