Srp
A modern PHP/JS (Typescript) package of SRP-6a (RFC5054). Contains server and client part to help developer use on any cases.
Install / Use
/learn @windwalker-io/SrpREADME
PHP SRP (Secure Remote Password) [PHP/JS]
<p align="center"> <br/> <img src="https://user-images.githubusercontent.com/1639206/151679867-8df93936-e4af-4677-a6f3-eb33d27e038b.svg" alt="Windwalker" height="75"> <br/> </p> <h2 align="center">SRP Package (PHP)</h2> <p align="center"> Windwalker SRP Package <a href="https://github.com/windwalker-io/srp">PHP</a> | <a href="https://github.com/windwalker-io/srp/tree/main/assets">JS</a> </p> <p align="center"> <img alt="GitHub" src="https://img.shields.io/github/license/windwalker-io/srp?style=flat-square"> <img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/windwalker-io/srp/test-php.yml?label=test&style=flat-square"> <img alt="Packagist Downloads" src="https://img.shields.io/packagist/dt/windwalker/srp?style=flat-square"> <a href="https://packagist.org/packages/windwalker/srp"> <img alt="Packagist Version" src="https://img.shields.io/packagist/v/windwalker/srp?style=flat-square"> </a> </p>This is a modern PHP/JS package which provides an implementation of SRP-6a (RFC5054). The PHP / JS side both have server and client part to help developer use on any cases.
This package passed the srptools Test Vectors, it means that this package is fully implement the RFC5054 spec, and you can use this package to work with any other packages which is also fully adheres the RFC spec. The main difference is that this package will pad value to fit the length of g (prime) value before hash, however, most of the SRP packages will probably not pad them before hashing.
We also provide a way to disable the padding behavior if you want to use this package with another package that does not pad values before hashing.
Installation
PHP
composer require windwalker/srp
JS
npm i @windwalker-io/srp --save
# OR
yarn add @windwalker-io/srp
See the JS package documentation here.
Getting Started
You must prepare a large safe prime, a generator and a key, the prime and generator is base 10, and the key is hex (base16) format. This package also provides a default safe prime, you can directly use it.
use Windwalker\SRP\SRPServer;
use Windwalker\SRP\SRPClient;
$server = new SRPServer(
SRPServer::DEFAULT_PRIME, // 217661744586174357731910088918027537819...
SRPServer::DEFAULT_GENERATOR, // 02
SRPServer::DEFAULT_KEY, // 5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300
);
these value can be BigInteger format, we use brick/math as the BigInteger library.
use Brick\Math\BigInteger;
use Windwalker\SRP\SRPServer;
use Windwalker\SRP\SRPClient;
$server = new SRPServer(
BigInteger::of(SRPServer::DEFAULT_PRIME),
BigInteger::of(SRPServer::DEFAULT_GENERATOR),
BigInteger::fromBase(SRPServer::DEFAULT_KEY, 16),
);
Use createFromConfig() if you set the config in an array.
use Windwalker\SRP\SRPServer;
$config = [
'prime' => SRPServer::DEFAULT_PRIME, // 217661744586174357731910088918027537819...
'generator' => SRPServer::DEFAULT_GENERATOR, // 02
'key' => SRPServer::DEFAULT_KEY, // 5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300
];
Use create() to ignore all parameters, this package will use the prepared default secure config.
use Windwalker\SRP\SRPServer;
use Windwalker\SRP\SRPClient;
$server = SRPServer::create();
$client = SRPClient::create();
There has some more configure options:
use Windwalker\SRP\SRPServer;
// Set the secret size
$server->setSize(512); // Default is 256
// Same as
$server->setLength(64);
// Set Hash algo, default is `sha256`
$server->setHaser('sha1');
$server->setHaser('sha256');
$server->setHaser('sha384');
$server->setHaser('sha512');
// Blake2b will use sodium ext to hash it.
$server->setHaser('blake2b-256');
$server->setHaser('blake2b-224');
$server->setHaser('blake2b-384');
$server->setHaser('blake2b-512');
// Set custom hash logic
$server->setHaser(fn(string $str) => ...);
// Disable padding
$server->enablePad(false);
Sample Code
Here we use both PHP server and client to run a sample SRP flow. You can replace the client part as JS. All values generated by SRP package is a BigInteger object, you can convert it to hex by $v->toBase(16) for storing to DB or transfer by HTTP request.
The full description of this flow is under the next chapter.
use Windwalker\SRP\SRPServer;
use Windwalker\SRP\SRPClient;
$server = SRPServer::create();
$client = SRPClient::create();
// Register page: User input identify and password.
$identity = '...';
$password = '...';
// Register: generate new salt & verifier
$pf = $client->register($identity, $password);
$salt = $pf->salt; // BigInteger object
$verifier = $pf->verifier; // BigInteger object
// Use toBase(16) convert to hex string
$salt->toBase(16);
$verifier->toBase(16);
// Send to Server store
// Login start
// AJAX:hello?{identity} - Server step (1)
// salt & verifier has already stored on user data, server can get it from DB
// b & B must remember on session, we will use it at following steps.
$r = $server->step1($identity, $salt, $verifier);
$b = $r->secret; // BigInteger object
$B = $r->public; // BigInteger object
// Server hello: returns B & salt to client
// Client step (1) & (2)
$pr = $client->step1($identity, $password, $salt);
$a = $pr->secret;
$A = $pr->public;
$x = $pr->hash;
$pr = $client->step2($identity, $salt, $A, $a, $B, $x);
$K = $pr->key;
$M1 = $pr->proof;
// AJAX:authenticate?{identity,A,M1} - Server step (2)
// Send identity & A & M1 to server and compare it.
// The salt & verifier stored on user data, get it from DB.
// The b, B stored in session state, get and clear them.
$pr = $server->step2($identity, $salt, $verifier, $A, $B, $b, $M1);
$M2 = $pr->proof;
// Server returns M2 to Client
// Client step (3) (optional)
$client->step3($A, $K, $M1, $M2);
// If all passed, should not throw any exceptions.
The SRP Flow
The definitions and processes of SRP-6a are dispersed in RFC 2945 and RFC 5054; this is an attempt to integrate them for an overview. Please follow strictly to the RFC-specified procedures without custom modification, and do not transmit any variables unnecessarily to avoid security breaches.
Definition
| Variable | Name | Send | Calc |
|-----------------|---------------------------------------------------------------------|------|------------------------------------------------|
| I, identity | The main identity (username or email). | C=>S | |
| N | A large safe prime, All arithmetic is done modulo N. | X | |
| g | A generator modulo N | X | |
| k | Multiplier parameter | X | SHA1(N \| PAD(g)) |
| s | The user salt. | C<=S | random() |
| v | Password Verifier | X | g^x % N |
| x | The hash of salt + identity + password. | X | SHA1(s \| SHA1(I \| ":" \| P)) |
| a, b | Client & server secret key | X | random() |
| A | Client public key | C=>S | g^a % N |
| B | Server public key | C<=S | k*v + g^b % N |
| u | The value to prevent attacker who learns a user's verifier | X | H(PAD(A) \| PAD(B)) |
| S (client) | Pre master secret (The secure common session key) | X | (B - (k * g^x)) ^ (a + (u * x)) % N |
| S (server) | Pre master secret (The secure common session key) | X | (A * v^u) ^ b % N |
| K | The session key hash for used to generate M | X | H(S) |
| M1 | Evidence message 1, To verify both side generated same session key. | C=>S | H(H(N) XOR H(g) \| H(U) \| s \| A \| B \| K) |
| M2 | Evidence message 2, To verify both side generated same session key. | C<=S | H(A \| M \| K) |
Registration
When an app (web/mobile) start registration flow, it may display a identity (I) (username or email) and password (P) field to user. They entered their username and password, then click the register button. The SRP client will generate a random salt (s), and a password verifier (v) which is generated from salt, identity and password.
Then app will send only the salt, verifier and identity to server and do not send password. It is a protocol violation and security bug if the raw password is accidently transmitted to the server even if it is ignored by the server.
You can save the user info and salt, verifier to DB
