Giter Site home page Giter Site logo

jsrp's Introduction

Synopsis

JSRP is a pure JavaScript implementation of SRP-6A, the Secure Remote Password protocol, as defined in RFC 2945. It can be used in Node.js or the browser via Browserify. It uses SHA-256 by default for hashing, although it will support any of Node's hashing functions. It currently supports 2048 and 4096 bit parameters.

Motivation

JSRP was written to make SRP simple to implement and work with from the browser and on the server. All high-level functions return hex strings that are easy to pass between server and client, as well as save. No need to waste time serializing and unserializing objects just to transmit them over the network.

Installation

To use JSRP in the browser, first add the script: (it can be downloaded from the Releases page.)

<script type="text/javascript" src="jsrp-browser.js"></script>

To use JSRP in Node, simply run:

npm install jsrp

Usage

Browser

JSRP will be available from the jsrp global.

Node

Just require the module:

var jsrp = require('jsrp');

Example

The example will run in Node or the browser, JSRP is completely compatible with both.

Example Registration Process:

var client = new jsrp.client();

client.init({ username: 'testUser', password: 'password123' }, function () {
	client.createVerifier(function(err, result) {
		// result will contain the necessary values the server needs to
		// authenticate this user in the future.
		sendSaltToServer(result.salt);
		sendVerifierToServer(result.verifier);
	});
});

Example Login Process: (normally client and server wouldn't be in the same code, but the example is this way for the sake of brevity)

var client = new jsrp.client();
var server = new jsrp.server();

client.init({ username: 'username', password: 'password123' }, function() {
	// Client instance is ready to be used here.
});

server.init({ salt: 'LONG_HEX_VALUE', verifier: 'EVEN_LONGER_HEX_VALUE' }, function () {
	// Server instance is ready to be used here.
});

// Remember, both client and server must have called their
// init() callback before you can continue using them. The
// following functions would normally be called inside that
// callback.

cPubKey = client.getPublicKey();
server.setClientPublicKey(cPubKey);

salt = server.getSalt();
client.setSalt(salt);

sPubKey = server.getPublicKey();
client.setServerPublicKey(sPubKey);

client.getSharedKey() === server.getSharedKey() // will be true

API Reference

Client methods:

  • init(options, callback)
    • options should be an object containing a username and a password. { username: 'username', password: 'password' }. You may also pass a length property, which will allow you to select the size of your parameters. It defaults to 4096.
    • callback will be called when the client instance is ready to use.
  • getPublicKey() -> Hex A value
    • Return the hex representation of the client's A value, suitable for sending over the network.
  • setSalt(salt)
    • salt should be the hex string obtained from Server.getSalt(), this sets the client's internal salt value for later computations.
  • setServerPublicKey(serverPublicKey)
    • serverPublicKey should be the hex representation of the server's B value, as returned from Server.getPublicKey(). When this function is called, provided the publicKey is valid, the client instance will compute the rest of the values needed internally to complete authentication. This will throw an error if the server provides an incorrect value, authentication MUST be aborted here.
  • getSharedKey() -> Hex K value
    • The hex representation of the computed secret shared key, suitable for external crypto usage.
  • getProof() -> Hex M1 value
    • Client's M1 value as a hex string, suitable for transmission to the server.
  • checkServerProof(serverProof) -> Boolean
    • Returns true if serverProof matches the client's own proof computation, false if it doesn't. serverProof can be obtained from Server.getProof(). This can only be called after getProof().
  • getSalt() -> Hex salt
    • The hex value of the salt generated from createVerifier() (see next item), or the salt that was passed via setSalt()
  • createVerifier(callback) -> Hex V value
    • Generate v and salt from the values passed to init()
    • callback will be called once the verifier has been created, with two values, err, and object, where object looks like { verifier: HEX_STRING, salt: HEX_STRING } and is suitable for transmission to the server.

Server methods:

  • init(options, callback)
    • options should be an object containing the hex representations of verifier and salt. These should be the values received from the initial client registration using Client.createVerifier(). You may also pass length, which allows you to select the size of your parameters.
    • callback will be invoked once the server instance is ready to use.
  • getPublicKey() -> Hex B value
    • Return the server's B value in hex format, suitable for transmission to the client.
  • getSalt() -> Hex salt value
    • Return the salt value (this will be the same value passed to init())
  • setClientPublicKey(clientPublicKey)
    • clientPublicKey should be the hex value returned from Client.getPublicKey(). Assuming it's valid, the server will then compute the values necessary to complete authentication internally. This will throw an error if the client provides an incorrect value, authentication MUST be aborted here.
  • getSharedKey() -> Hex K value
    • The secret shared key suitable for further crypto operations.
  • checkClientProof(clientProof) -> Boolean
    • Returns true if clientProof matches the server's own proof computation, false if it doesn't. If this value is true, then the client has provided the correct password, and can be considered authenticated. If it's false, the client used the wrong password. clientProof is the hex string obtained from Client.getProof()
  • getProof() -> Hex M2 value
    • The server's M2 value as a hex string, suitable for transmission to the client. This can only be called after checkClientProof().

In either scenario, if you'd like to interact with the SRP protocol implementation directly, the SRP object will be available on the client/server object after running init(). You can access it using clientObj.srp or serverObj.srp. The intermediate values calculated by the client and server are also available on the objects themselves as well.

Testing

First, install the dependencies:

npm install

Also, you will need Mocha and CoffeeScript if you don't have them already:

npm install -g mocha coffee-script

Then simply run:

npm test

Browser Builds

To build JSRP for the browser, you will need Browserify and CoffeeScript:

npm install -g browserify coffee-script

Then run the following commands inside the JSRP directory:

coffee --compile --output lib src
browserify jsrp.js --standalone jsrp > jsrp-browser.js

Credits

JSRP would not exist if it wasn't for Node-SRP: https://github.com/mozilla/node-srp. They provided a solid reference implementation, but JSRP was born out of wanting a reliable browser implementation as well as server implementation.

jsrp's People

Contributors

alax avatar grempe avatar kind3r avatar poursal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

jsrp's Issues

Compatible Golang implementation with JSRP?

Hi there!

We're building a platform using this package on the backend. We're working on an accompanying CLI written in Go and wanted something that's compatible but haven't been able to find any compatible implementations in Go โ€” Is there any that you could recommend?

Also, does this package only conform to the specification in RFC 2945 or also RFC 5054?

Thanks!

M1 getProof() not to RFC

M = H(H(N) XOR H(g) | H(U) | s | A | B | K)

Detailed on page 4 of RFC 2945.

This library appears to use the alternate proof listed on Wikipedia (I didn't see it in the RFC).

I'll see about implementing it tomorrow, no promises though. Trying to interface with Python srp and the M values weren't matching up.

Project isn't SRP-3 or SRP-6a

SRP-6a is RFC 5054. This implementation uses the older SRP-3 K calculation (RFC 2945). It also uses the alternative M proof which I can't find on either RFC and is citation-needed on Wikipedia.

To comply with SRP-3 it should implement the correct M calculation. I've included an implementation of M1 calculation to the spec. I haven't verified it as the Python library uses the newer K generation algorithm, so the output values do not line up, however, all the input values appear correct.

The only tricky thing is N_sbyte contains a leading zero, this is removed by starting the counter at 1, [1..(N_sbyte.length-1)]. Might not even be necessary to convert the bytes to unsigned, as I did that before noticing the leading zero.

M1Spec: (options) ->
        N = @params.N
        g = @params.g
        s = options.s # Buffer
        I = options.I # Buffer
        A = options.A # Buffer
        B = options.B # Buffer
        K = options.K # Buffer

        N_sbyte = N.toByteArray()
        N_ubyte = []
        for i in [1..(N_sbyte.length-1)]
            N_ubyte[i-1] = N_sbyte[i] & 0xFF

        H_N = crypto
            .createHash(@params.hash)
            .update(N_ubyte)
            .digest()

        H_g = crypto
            .createHash(@params.hash)
            .update(g.toByteArray())
            .digest()

        H_I = crypto
            .createHash(@params.hash)
            .update(I)
            .digest()

        HN_xor_Hg = []
        for i in [0..(H_I.length-1)]
            HN_xor_Hg[i] = H_N[i] ^ H_g[i]

        result = crypto
            .createHash(@params.hash)
            .update(HN_xor_Hg)
            .update(H_I)
            .update(s)
            .update(A)
            .update(B)
            .update(K)
            .digest()

        return result

To comply with SRP-6a it should update the K generation, as it is different now. See 2.6. Calculating the Premaster Secret (quoted below). The major change seems to be the introduction of lowercase k, which acts a multiplier for g^x.

The premaster secret is calculated by the client as follows:

        I, P = <read from user>
        N, g, s, B = <read from server>
        a = random()
        A = g^a % N
        u = SHA1(PAD(A) | PAD(B))
        k = SHA1(N | PAD(g))
        x = SHA1(s | SHA1(I | ":" | P))
        <premaster secret> = (B - (k * g^x)) ^ (a + (u * x)) % N

issues for 1024 bits

when 4096 bits is used, the client and the server can authenticate each other,
then I just change the length from 4096 (the default) to 1024, both claimed the other party is fake (not authentic).

demo project creation

Hello there i created a demo project to assist people needing help on implementing this library to their projects.

The repo is here .

M1 calculation differs from Wikipedia

Hi there, I've found a discrepancy with how the server library I'm using calculates M1.

Wikipedia defines M1 as:

Alternatively, in a password-only proof the calculation of K can be skipped and the shared S proven with:

M1 = H(A | B | SCarol)

which seems to be what my library is using.

However, this library seems to be using M1 = H(A | B | K), where K = H(S)

I would like to know where this discrepancy comes from, is it because of different versions of SRP? Thanks!

Related to #15

JSBN required for build

$ browserify jsrp.js --standalone jsrp > jsrp-browser.js
Error: Cannot find module 'jsbn' from '/path/to/jsrp/lib'
    at /usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:46:17
    at process (/usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:173:43)
    at ondir (/usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:188:17)
    at load (/usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:69:43)
    at onex (/usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:92:31)
    at /usr/lib/node_modules/browserify/node_modules/resolve/lib/async.js:22:47
    at FSReqWrap.oncomplete (fs.js:117:15)
$ npm install jsbn
$ browserify jsrp.js --standalone jsrp > jsrp-browser.js

New SiRP Ruby Gem Review Requested, tested with jsrp

Hello,

I've just published a pre-release version of a new Ruby Gem which implements SRP. I needed this code to be interoperable with Ruby clients and also at least one JavaScript client. I chose to use your jsrp lib as the initial test case to implement against.

I would be grateful if you could take a look at it and maybe kick the tires a bit.

You can find it here:

https://github.com/grempe/sirp/blob/master/README.md

And the example server and client are in the examples dir.

https://github.com/grempe/sirp/tree/master/examples

Once you start the server you should be able to just open the index.html file to run the client with your jsrp code against the server.

Thanks again, and please feel free to share any issues you run into or suggestions.

https://github.com/grempe/sirp/issues

Glenn

the length of hex string is required to be even number

In the buffer module, the hexWrite throws "Invalid hex string" if the length of the hex string is not an even number. In java, for example BigInteger(15).toString(16) will gives "f", its length is odd(1), so hex string should be checked before being passed into jsrp, and in turn to buffer.

==============================
function hexWrite (buf, string, offset, length) {
offset = Number(offset) || 0
var remaining = buf.length - offset
if (!length) {
length = remaining
} else {
length = Number(length)
if (length > remaining) {
length = remaining
}
}

// must be an even number of digits
var strLen = string.length
if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')

browserify throwing error while bundling

Trying to browserify the release script and getting the following error:

Error: Can't walk dependency graph: Cannot find module './_stream_writable' from '/Users/wahal/go/src/github.com/envsecrets/jsrp/scripts/jsrp-browser.js'
    required by /Users/wahal/go/src/github.com/envsecrets/jsrp/scripts/jsrp-browser.js
    at /Users/wahal/.nvm/versions/node/v15.14.0/lib/node_modules/browserify/node_modules/resolve/lib/async.js:167:35
    at load (/Users/wahal/.nvm/versions/node/v15.14.0/lib/node_modules/browserify/node_modules/resolve/lib/async.js:186:43)
    at onex (/Users/wahal/.nvm/versions/node/v15.14.0/lib/node_modules/browserify/node_modules/resolve/lib/async.js:211:17)
    at /Users/wahal/.nvm/versions/node/v15.14.0/lib/node_modules/browserify/node_modules/resolve/lib/async.js:24:69
    at FSReqCallback.oncomplete (node:fs:193:21)

Command that I'm running:

browserify jsrp-browser.js -o bundle.js

TypeError: this.ABuf is undefined

Running jsrp in the browser in a Nuxt2 project. Using release package v0.2.4.

Getting the following error in console of my browser:

TypeError: this.ABuf is undefined

Running the following code:

cPubKey = client.getPublicKey();

Getting the error from the following line:

getPublicKey() {
  return this.ABuf.toString('hex'); // This line
}

Any solutions to this, @alax?

Server instance init

If I describe it correctly, the client side needs to communicate with server side for at least 3 times

  1. Send the identifier and get the salt
  2. Send the cPubKey and get the sPubKey
  3. Send the M1 value to server and get the checking result

My issue here is that on server side each time when I get a request from client app, I need to init the jsrp server instance, then seems the instance is always different from previous one. So when I s.checkClientProof(M1), the result is always not true.

How do you persist the server instance? Sorry I am pretty new on this. Thanks in advance for your guidance.

A couple questions about JSRP

We have been doing a bunch of work creating security libraries in Javascript, for example :

  • node-webcrypto-ossl, a webcrypto polyfill for Node, based on OpenSSL.
  • node-webcrypto-p11, a webcrypto polyfill for Node, based on PKCS#11.
  • PKIjs, an implementation of all X.509 related data structures and formats.
  • XADESjs, an implementation of XMLDSIG and XADES-BES.

Anyway I have been interested in playing with SRP and stumbled across your project, I have a questions:

  1. What other SRP implementations has this been tested with?

  2. Do you imagine it would be easy to retool to be based on WebCrypto?

  3. Is there a sample client/server somewhere I could start with?

Thanks for your time.

Support for Additional N and g values

I have software that requires a custom set of N and g values as part of the parameters.

Providing an API for overriding these would allow me to use this library for my implementation.

For now I guess I can fork this and introduce my own method of importing them.

issue with length 1024

I found that when i set length 1024, the authentication test failed.
if changed to 4096, then the same test code gives both parties are authenticated.

TypeError: Cannot read property 'toString' of undefined

TypeError: Cannot read property 'toString' of undefined
at TransBuffer.toHex (http://127.0.0.1:3000/build/init.js:1074:25)
at TransBuffer.toBigInteger (http://127.0.0.1:3000/build/init.js:1079:30)
at http://127.0.0.1:3000/build/init.js:915:35 at Object.exports.randomBytes (http://127.0.0.1:3000/build/init.js:3805:21)
at SRP.a (http://127.0.0.1:3000/build/init.js:913:21)
at Client.init (http://127.0.0.1:3000/build/init.js:463:23)
...

Can't use your build. Is it broken?

The code in question effectively is this:

let srpclient = new jsrp.client();

srpclient.init({
    username: 'testUser',
    password: 'password123'
}, () => {
    srpclient.createVerifier((err, result) => {
        console.log(result.salt, result.verifier);
        this.io.emit('login', seed);
    });
});

It might be worth noting that a build through browserify works as expected.

createVerifier() does not use salt given in setSalt()

I noticed that in Client.createVerifier() the saltBuf is always set to the salt value provided in the srp.generateSalt() callback. This consequently means that any salt set using setSalt() is not used when creating the verifier. This looks like a bug.

about M1 and M2

The M1 calculation is much different from what described here: http://srp.stanford.edu/design.html

And M2 calculation, I have seen other implementations use H(A|M1|S) rather than H(A|M1|K).

I guess it will be better to describe the formula in the readme page, so people can have an idea on the exact formula.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.