npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

srp-bigint

v0.2.1

Published

Secure Remote Password (SRP) implemented with BigInts

Downloads

60

Readme

build status

SRP - Secure Remote Password

Implementation of the SRP Authentication and Key Exchange System and protocols in Secure Remote Password (SRP) Protocol for TLS Authentication

SRP is an interactive protocol which allows a server to confirm that some client knows a password, and to derive a strong shared session key, without revealing what the password is to an eavesdropper. In addition, the server does not hold the actual password: instead it stores a "verifier" created by the client. If the server's private data is revealed (by a server compromise), the verifier cannot be used directly to impersonate the client.

This module provides both client and server implementations of SRP-6a for node.js. They are interoperable with Mozilla Identity-Attached Services

This implementation was modified from mozilla/node-srp to use native BigInts instead of the bignum module. As such, it will only work on Node.js v10.4.0 or above.

Installation

npm install srp

or git clone this archive and run npm install in it.

Running Tests

Run npm test.

Tests include vectors from:

How to use it

First, you must decide on the "parameters". This module provides a variety of pre-packaged parameter sets, at various levels of security (more secure parameters take longer to run). The "2048"-bit parameters are probably fairly secure for the near future. Both client and server must use the same parameters.

let params = srp.params["2048"];

Each client will have a unique "identity" string. This is typically a username or email address. Clients will also use a unique "salt", which can be randomly generated during account creation. The salt is generally stored on the server, and must be provided to the client each time they try to connect.

Note that all APIs accept and return node.js Buffer objects, not strings.

Client Setup: Account Creation

The client feeds their identity string, password, and salt, into computeVerifier. This returns the Verifier buffer. The Verifier must be delivered to the server, typically during a "create account" process. Note that the Verifier can be used to mount a dictionary attack against the user's password, so it should be treated with care (and delivered securely to the server).

let verifier = srp.computeVerifier(params, salt, identity, password);
createAccount(identity, verifier);

The server should store the identity, salt, and verifier in a table, indexed by the identity for later access. The server will provide the salt to anyone who asks, but should never reveal the verifier to anybody.

Login

Later, when the client wants to connect to the server, it starts by submitting its identity string, and retrieving the salt.

Then the client needs to create a secret random string called secret1, using srp.genKey(). The protocol uses this to make sure that each instance of the protocol is unique.

Then, create a new srp.Client instance with parameters, identity, salt, password, and secret1.

The client must then ask this object for the derived srpA value, and deliver srpA to the server.

let secret1 = await srp.genKey();
let c = new srp.Client(params, salt, identity, password, secret1);
let srpA = c.computeA();
sendToServer(srpA);

Meanwhile, the server is doing something similar, except using the Verifier instead of the salt/identity/password, and using its own secret random string.

let secret2 = await srp.genKey();
let s = new srp.Server(params, verifier, secret2);
let srpB = s.computeB();
sendToClient(srpB);

When the client receives the server's srpB value, it stuffs it into the Client instance. This allows it to extract two values: M1 and K.

c.setB(srpB);
let M1 = c.computeM1();
sendToServer(M1);
let K = c.computeK();

M1 is a challenge value, created by the client and delivered to the server. After accepting the client's A value, the server can check M1 to determine whether or not the client really knew the password. The server can also obtain its own K value.

s.setA(srpA)
s.checkM1(M1); // throws error if wrong
let K = s.computeK();

If the password passed into srp.Client() is the same as the one passed into srp.computeVerifier(), then the server will accept M1, and the K on both sides will be the same.

K is a strong random string, suitable for use as a session key to encrypt or authenticate subsequent messages.

If the password was different, then s.checkM1() will throw an error, and the two K values will be unrelated random strings.

The overall conversation looks like this:

Client:                             Server:
 p = params["2048"]                  p = params["2048"]
 s1 = genKey()                       s2 = genKey()
 c = new Client(p,salt,id,pw,s1)     s = new Server(p,verifier,s2)
 A = c.computeA()         A----->    s.setA(A)
 c.setB(B)                <-----B    B = s.computeB()
 M1 = c.computeM1()       M1---->    s.checkM1(M1) // may throw error
 K = c.computeK()                    K = s.computeK()

What a "Session" Means

Basic login can be done by simply calling s.checkM1(): if it doesn't throw an exception, the client knew the right password. However, by itself, this does not bind knowledge of the password to anything else. If the A/B/M1 values were delivered over an insecure channel, controlled by an attacker, they could simply wait until M1 was accepted, and then take control of the channel.

Delivering these values over a secure channel, such as an HTTPS connection, is better. If the HTTP client correctly checks the server certificate, and the certificate was correctly issued, then you can exclude a man-in-the-middle attacker.

The safest approach is to create a secure channel with the generated session key K, using it to encrypt and authenticate all the messages which follow.

API Reference

Module contents:

  • params[]
    • table of parameter sets. Pass a property from this object into the Client and Server constructors.
  • genKey(numBytes = 32)
    • async function (returns Promise) to generate the ephemeral secrets passed into the Client and Server constructors.
  • computeVerifier(params, salt, identity, password) -> V
    • produces a Verifier, which should be given to the server during account creation. The Verifier will be passed into the Server constructor during login.
  • Client(params, salt, identity, password, secret1) -> c
    • constructor for the client-side of SRP. secret1 should come from genKey(). The Client object has the following methods:
  • Server(params, verifier, secret2) -> s
    • constructor for the server-side of SRP. secret2 should come from genKey(). If the Server object must be persisted (e.g. in a database) between protocol phases, simply store secret2 and re-construct the Server with the same values. The Server object has the following methods:

Client methods:

  • computeA() -> A
    • produce the A value that will be sent to the server.
  • setB(B)
    • this accepts the B value from the server. M1 and K cannot be accessed until setB() has been called.
  • computeM1() -> M1
    • produce the M1 key-confirmation message. This should be sent to the server, which can check it to make sure the client really knew the correct password. setB must be called before computeM1.
  • computeK() -> K
    • produce the shared key K. If the password and verifier matched, both client and server will get the same value for K. setB must be called before computeK.

Server methods:

  • computeB() -> B
    • produce the B value that will be sent to the client.
  • setA(A)
    • this accepts the A value from the client. checkM1 and computeK cannot be called until setA has been called.
  • checkM1(M1)
    • this checks the client's M1 key-confirmation message. If the client's password matched the server's verifier, checkM1() will complete without error. If they do not match, checkM1() will throw an error.
  • computeK() -> K
    • produce the shared key K. setA must be called before computeK.

Resources

License

MIT