{"id":19039590,"url":"https://github.com/windwalker-io/srp","last_synced_at":"2025-10-27T07:05:59.086Z","repository":{"id":206791695,"uuid":"717711741","full_name":"windwalker-io/srp","owner":"windwalker-io","description":"A modern PHP/JS package of SRP-6a (RFC5054). Contains server and client part to help developer use on any cases.","archived":false,"fork":false,"pushed_at":"2025-01-27T16:16:48.000Z","size":289,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-18T05:53:45.810Z","etag":null,"topics":["password","passwordless","rfc-5054","secure-remote-password","security","srp","srp-6a"],"latest_commit_sha":null,"homepage":"https://packagist.org/packages/windwalker/srp","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/windwalker-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-11-12T10:50:12.000Z","updated_at":"2025-03-13T19:40:22.000Z","dependencies_parsed_at":"2023-11-12T11:30:19.026Z","dependency_job_id":"666fa5f0-d663-4b4e-be17-02e99b46970c","html_url":"https://github.com/windwalker-io/srp","commit_stats":null,"previous_names":["windwalker-io/srp"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fsrp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fsrp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fsrp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/windwalker-io%2Fsrp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/windwalker-io","download_url":"https://codeload.github.com/windwalker-io/srp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250513615,"owners_count":21443202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["password","passwordless","rfc-5054","secure-remote-password","security","srp","srp-6a"],"created_at":"2024-11-08T22:17:44.652Z","updated_at":"2025-10-27T07:05:54.054Z","avatar_url":"https://github.com/windwalker-io.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP SRP (Secure Remote Password) [PHP/JS]\n\n\u003cp align=\"center\"\u003e\n    \u003cbr/\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/1639206/151679867-8df93936-e4af-4677-a6f3-eb33d27e038b.svg\" alt=\"Windwalker\"\n        height=\"75\"\u003e\n    \u003cbr/\u003e\n\u003c/p\u003e\n\n\u003ch2 align=\"center\"\u003eSRP Package (PHP)\u003c/h2\u003e\n\n\u003cp align=\"center\"\u003e\n    Windwalker SRP Package \u003ca href=\"https://github.com/windwalker-io/srp\"\u003ePHP\u003c/a\u003e | \u003ca href=\"https://github.com/windwalker-io/srp/tree/main/assets\"\u003eJS\u003c/a\u003e\n\u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/windwalker-io/srp?style=flat-square\"\u003e\n    \u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/windwalker-io/srp/test-php.yml?label=test\u0026style=flat-square\"\u003e\n    \u003cimg alt=\"Packagist Downloads\" src=\"https://img.shields.io/packagist/dt/windwalker/srp?style=flat-square\"\u003e\n    \u003ca href=\"https://packagist.org/packages/windwalker/srp\"\u003e\n        \u003cimg alt=\"Packagist Version\" src=\"https://img.shields.io/packagist/v/windwalker/srp?style=flat-square\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\nThis is a modern PHP/JS package which provides an implementation of SRP-6a ([RFC5054](https://datatracker.ietf.org/doc/html/rfc5054)). The PHP / JS side both have server and client part to help developer use on any cases.\n\nThis package passed the srptools [Test Vectors](https://github.com/secure-remote-password/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.\n\nWe 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.\n\n\n\n## Installation\n\nPHP\n\n```shell\ncomposer require windwalker/srp\n```\n\nJS\n\n```shell\nnpm i @windwalker-io/srp --save\n\n# OR\n\nyarn add @windwalker-io/srp\n```\n\nSee the JS package [documentation here](./assets/README.md).\n\n## Getting Started\n\nYou 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.\n\n```php\nuse Windwalker\\SRP\\SRPServer;\nuse Windwalker\\SRP\\SRPClient;\n\n$server = new SRPServer(\n    SRPServer::DEFAULT_PRIME, // 217661744586174357731910088918027537819...\n    SRPServer::DEFAULT_GENERATOR, // 02\n    SRPServer::DEFAULT_KEY, // 5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300\n);\n```\n\nthese value can be BigInteger format, we use [brick/math](https://github.com/brick/math) as the BigInteger library.\n\n```php\nuse Brick\\Math\\BigInteger;\nuse Windwalker\\SRP\\SRPServer;\nuse Windwalker\\SRP\\SRPClient;\n\n$server = new SRPServer(\n    BigInteger::of(SRPServer::DEFAULT_PRIME),\n    BigInteger::of(SRPServer::DEFAULT_GENERATOR),\n    BigInteger::fromBase(SRPServer::DEFAULT_KEY, 16),\n);\n```\n\nUse `createFromConfig()` if you set the config in an array.\n\n```php\nuse Windwalker\\SRP\\SRPServer;\n\n$config = [\n    'prime' =\u003e SRPServer::DEFAULT_PRIME, // 217661744586174357731910088918027537819...\n    'generator' =\u003e SRPServer::DEFAULT_GENERATOR, // 02\n    'key' =\u003e SRPServer::DEFAULT_KEY, // 5b9e8ef059c6b32ea59fc1d322d37f04aa30bae5aa9003b8321e21ddb04e300\n];\n```\n\nUse `create()` to ignore all parameters, this package will use the prepared default secure config.\n\n```php\nuse Windwalker\\SRP\\SRPServer;\nuse Windwalker\\SRP\\SRPClient;\n\n$server = SRPServer::create();\n$client = SRPClient::create();\n```\n\nThere has some more configure options:\n\n```php\nuse Windwalker\\SRP\\SRPServer;\n\n// Set the secret size\n$server-\u003esetSize(512); // Default is 256\n// Same as\n$server-\u003esetLength(64);\n\n\n// Set Hash algo,  default is `sha256`\n$server-\u003esetHaser('sha1');\n$server-\u003esetHaser('sha256');\n$server-\u003esetHaser('sha384');\n$server-\u003esetHaser('sha512');\n\n// Blake2b will use sodium ext to hash it.\n$server-\u003esetHaser('blake2b-256');\n$server-\u003esetHaser('blake2b-224');\n$server-\u003esetHaser('blake2b-384');\n$server-\u003esetHaser('blake2b-512');\n\n// Set custom hash logic\n$server-\u003esetHaser(fn(string $str) =\u003e ...);\n\n\n// Disable padding\n$server-\u003eenablePad(false);\n```\n\n### Sample Code\n\nHere 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-\u003etoBase(16)` for storing to DB or transfer by HTTP request.\n\nThe full description of this flow is under the next chapter.\n\n```php\nuse Windwalker\\SRP\\SRPServer;\nuse Windwalker\\SRP\\SRPClient;\n\n$server = SRPServer::create();\n$client = SRPClient::create();\n\n// Register page: User input identify and password.\n$identity = '...';\n$password = '...';\n\n// Register: generate new salt \u0026 verifier\n$pf = $client-\u003eregister($identity, $password);\n$salt = $pf-\u003esalt; // BigInteger object\n$verifier = $pf-\u003everifier; // BigInteger object\n\n// Use toBase(16) convert to hex string\n$salt-\u003etoBase(16);\n$verifier-\u003etoBase(16);\n\n// Send to Server store\n\n// Login start\n// AJAX:hello?{identity} - Server step (1)\n// salt \u0026 verifier has already stored on user data, server can get it from DB\n// b \u0026 B must remember on session, we will use it at following steps.\n$r = $server-\u003estep1($identity, $salt, $verifier);\n$b = $r-\u003esecret; // BigInteger object\n$B = $r-\u003epublic; // BigInteger object\n\n// Server hello: returns B \u0026 salt to client\n\n// Client step (1) \u0026 (2)\n$pr = $client-\u003estep1($identity, $password, $salt);\n$a = $pr-\u003esecret;\n$A = $pr-\u003epublic;\n$x = $pr-\u003ehash;\n\n$pr = $client-\u003estep2($identity, $salt, $A, $a, $B, $x);\n$K = $pr-\u003ekey;\n$M1 = $pr-\u003eproof;\n\n// AJAX:authenticate?{identity,A,M1} - Server step (2)\n// Send identity \u0026 A \u0026 M1 to server and compare it.\n// The salt \u0026 verifier stored on user data, get it from DB.\n// The b, B stored in session state, get and clear them.\n$pr = $server-\u003estep2($identity, $salt, $verifier, $A, $B, $b, $M1);\n$M2 = $pr-\u003eproof;\n\n// Server returns M2 to Client\n// Client step (3) (optional)\n$client-\u003estep3($A, $K, $M1, $M2);\n\n// If all passed, should not throw any exceptions.\n```\n\n## The SRP Flow\n\nThe definitions and processes of SRP-6a are dispersed in [RFC 2945](https://datatracker.ietf.org/doc/html/rfc2945) and [RFC 5054](https://datatracker.ietf.org/doc/html/rfc5054); 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.\n\n## Definition\n\n| Variable        | Name                                                                | Send | Calc                                           |\n|-----------------|---------------------------------------------------------------------|------|------------------------------------------------|\n| `I`, `identity` | The main identity (username or email).                              | C=\u003eS |                                                |\n| `N`             | A large safe prime, All arithmetic is done modulo N.                | X    |                                                |\n| `g`             | A generator modulo N                                                | X    |                                                |\n| `k`             | Multiplier parameter                                                | X    | `SHA1(N \\| PAD(g))`                            |\n| `s`             | The user salt.                                                      | C\u003c=S | `random()`                                     |\n| `v`             | Password Verifier                                                   | X    | `g^x % N`                                      |\n| `x`             | The hash of salt + identity + password.                             | X    | `SHA1(s \\| SHA1(I \\| \":\" \\| P))`               |\n| `a`, `b`        | Client \u0026 server secret key                                          | X    | `random()`                                     |\n| `A`             | Client public key                                                   | C=\u003eS | `g^a % N`                                      |\n| `B`             | Server public key                                                   | C\u003c=S | `k*v + g^b % N`                                |\n| `u`             | The value to prevent attacker who learns a user's verifier          | X    | `H(PAD(A) \\| PAD(B))`                          |\n| `S` (client)    | Pre master secret (The secure common session key)                   | X    | `(B - (k * g^x)) ^ (a + (u * x)) % N`          |\n| `S` (server)    | Pre master secret (The secure common session key)                   | X    | `(A * v^u) ^ b % N`                            |\n| `K`             | The session key hash for used to generate M                         | X    | `H(S)`                                         |\n| `M1`            | Evidence message 1, To verify both side generated same session key. | C=\u003eS | `H(H(N) XOR H(g) \\| H(U) \\| s \\| A \\| B \\| K)` |\n| `M2`            | Evidence message 2, To verify both side generated same session key. | C\u003c=S | `H(A \\| M \\| K)`                               |\n\n## Registration\n\n![Registration](https://github.com/windwalker-io/srp/assets/1639206/9cfe047e-6baa-4208-bfde-59cbe501303a)\n\nWhen 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.\n\nThen 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.\n\nYou can save the user info and `salt`, `verifier` to DB when server receive the registration request. It is optional if you want to encrypt the salt and verifier before saving, make sure you encrypt it by a key which is only known by server.\n\n## Login\n\n![Login](https://github.com/windwalker-io/srp/assets/1639206/a01b4d82-2c28-4d95-8615-dca38408c2d5)\n\n### Hello and Server step1\n\nWhen a user start login process, they may enter their identity and password on form fields, and click the login button. The SRP client will send a Hello request with `identity` to server. Server should check user exists by this identity, and get `salt` and `verifier` from user data. Next, server will generate a random private `b` and a public `B`, and remember them by DB, session or cache storage that we will need them in the further steps, then, return the `salt`, `B` back to client (Server Hello). This process is similar to a handshake, to create a connecting session for both side.\n\n\u003e Some package calls client Hello as `challenge` action, and the `B` is server challenge value.\n\n### Client step 1 \u0026 2\n\nAfter receiving the `B` and `salt`, Client runs step 1 to generate `a` and `A`, and then , runs step2 to use all of the above values to generate a client proof `M1`. It will be sent to server with `A` (authenticate action). Server side also use all the generated values to generate a `M1` and compare it. If compare failure, server will report an error, and if compare success, server will generate a server proof `M2` and back to client. To this step, the authenticate actions is done, you can simply redirect user to login success page. \n\nThere is an optional Client step3 is that you can verify the `M2` to authority server is trusted and make sure both side generate a same session key (`S`). If you done this step3, it means you complete the authenticate handshake and did a two-way authentication. If you want to run the step3 to complete all the process, you can redirect user after step3 done.\n\n### About the `S` and `M`\n\nWhen client and server generating `M`, they will both generate a premaster secret (`S`). The `S` should be same, even if the 2 sides did not send `S` to another. The `M1` and `M2` is a verifier to make sure both side have a same `S`. So, `S` can be a trusted session key or encryption key if you want to do some other cryptography behavior in the future.\n\n## Some Important Notes\n\n- You don't need to use AJAX to implement SRP flow. You can simply use form post to do all the steps. For example, you may separate username and password into 2 steps on your website, and store values in hidden inputs. Make sure you stored `a` and `b` in your browser and server cache that can use them cross steps and do not accendently send them to remote side.\n- The `verifier` is generated from identity and password, which means you must re-create a new verifier to replace old one if user changes either of `identity` or `password`.\n- Always make sure you don't send any unnecessary values to each side, even if server or client ignore them, it is considered as a protocol violation and security bug. Also, the [MITM attacker](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) is able to use these sensitive data.\n- Always clear values when restart an authenticate process. Generally, you can reload page so that all values and JS object will be reset. If you are developing a SPA app, wrap whole process in a function and do not cache values to object properties, and create a new client JS object every time.\n- SRP should not replace HTTPS, you should always use SSL/TLS on your app and enable the Cookies HttpOnly and secure settings.\n\n## Contribution\n\nIf you want to report bug, simply open an issue. Any PR is welcome and can speed up the fixing process.\n\nIf you have a security issue, please send to `asika32764@gmail.com` (Simon).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwalker-io%2Fsrp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwindwalker-io%2Fsrp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwindwalker-io%2Fsrp/lists"}