Giter Club home page Giter Club logo

srp.net's Introduction

Secure Remote Password for .NET

Appveyor Coverage Tests NuGet

A modern SRP-6a implementation for .NET Standard 1.6+ and .NET Framework 3.5+.
Based on and is compatible with secure-remote-password npm package by Linus Unnebäck (see Compatibility).

Installation

dotnet add package srp

Usage

Signing up

To create an account, a client provides the following three values:

  • Identifier (username or email)
  • Salt
  • Verifier

The salt and verifier are calculated as follows:

using SecureRemotePassword;

// a user enters his name and password
var userName = "alice";
var password = "password123";

var client = new SrpClient();
var salt = client.GenerateSalt();
var privateKey = client.DerivePrivateKey(salt, userName, password);
var verifier = client.DeriveVerifier(privateKey);

// send userName, salt and verifier to server

Logging in

Authentication involves several steps:

1. Client → Server: I, A

The client generates an ephemeral secret/public value pair and sends the public value and user name to server:

using SecureRemotePassword;

// a user enters his name
var userName = "alice";

var client = new SrpClient();
var clientEphemeral = client.GenerateEphemeral(verifier);

// send userName and clientEphemeral.Public to server

2. Server → Client: s, B

The server retrieves salt and verifier from the database using the client-provided userName. Then it generates its own ephemeral secret/public value pair:

using SecureRemotePassword;

// retrieved from the database
var salt = "beb25379...";
var verifier = "7e273de8...";

var server = new SrpServer();
var serverEphemeral = server.GenerateEphemeral();

// store serverEphemeral.Secret for later use
// send salt and serverEphemeral.Public to the client

3. Client → Server: M1

The client derives the shared session key and a proof of it to provide to the server:

using SecureRemotePassword;

// a user enters his password
var password = "password123";

var client = new SrpClient();
var privateKey = client.DerivePrivateKey(salt, userName, password);
var clientSession = client.DeriveSession(clientEphemeral.Secret,
    serverPublicEphemeral, salt, userName, privateKey);

// send clientSession.Proof to the server

4. Server → Client: M2

The server derives the shared session key and verifies that the client has the same key using the provided proof value:

using SecureRemotePassword;

// get the serverEphemeral.Secret stored in step 2
var serverSecretEphemeral = "e487cb59...";

var server = new SrpServer();
var serverSession = server.DeriveSession(serverSecretEphemeral,
    clientPublicEphemeral, salt, userName, verifier, clientSessionProof);

// send serverSession.Proof to the client

5. Client verifies M2

Finally, the client verifies that the server has derived the same session key using the server's proof value:

using SecureRemotePassword;

var client = new SrpClient();
client.VerifySession(clientEphemeral.Public, clientSession, serverSessionProof);

Authentication at a glance

using SecureRemotePassword;

var client = new SrpClient();
var server = new SrpServer();

// sign up
var salt = client.GenerateSalt();
var privateKey = client.DerivePrivateKey(salt, username, password);
var verifier = client.DeriveVerifier(privateKey);

// authenticate
var clientEphemeral = client.GenerateEphemeral();
var serverEphemeral = server.GenerateEphemeral(verifier);
var clientSession = client.DeriveSession(clientEphemeral.Secret, serverEphemeral.Public, salt, username, privateKey);
var serverSession = server.DeriveSession(serverEphemeral.Secret, clientEphemeral.Public, salt, username, verifier, clientSession.Proof);
client.VerifySession(clientEphemeral.Public, clientSession, serverSession.Proof);

// both the client and the server have the same session key
Assert.AreEqual(clientSession.Key, serverSession.Key);

Custom protocol parameters

This SRP-6a implementation uses sha256 hash function and 2048-bit group values by default. Any class derived from HashAlgorithm can be used as H. Customizing the parameters is easy:

using System.Security.Cryptography;
using SecureRemotePassword;

// use predefined 4096-bit group with SHA512 hash function
var customParams = SrpParameters.Create4096<SHA512>();

SrpParameters has helper methods for all predefined groups from RFC5054: Create1024<SHA1>(), etc.

It's also possible to specify custom values of N and g:

var N = "D4C7F8A2B32C11B8FBA9581EC4BA...";
var customParams = SrpParameters.Create<SHA1>(N, "02");

Custom SRP parameters are then passed to SrpClient and SrpServer constructors. Make sure to use the same parameters on both sides:

var client = new SrpClient(customParams);
var server = new SrpServer(customParams);

Compatibility with other implementations

srp.net is designed to be compatible with other implementations hosted in secure-remote-password organization.

At the time of writing, the secure-remote-password npm package is incompatible with this implementation because it does not pad values according to RFC5054.

  • If you have control over both client and server, it is recommended to upgrade both to this version, as outlined here.
  • If you are forced to maintain compatibility with an existing server, you can disable padding by initializing the client with new SrpClient(new SrpParameters { PaddedLength = 0 }). This is not recommended, as the resulting behavior is incompatible with libraries that follow the standard.

Other compatible libraries are listed here.

References

srp.net's People

Contributors

danielbrauer avatar dependabot[bot] avatar jarnevanaerde avatar jdluzen avatar justarchi avatar mrhappyasthma avatar yallie 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

srp.net's Issues

Feature request: Methods to validate salt/verifier on sign up

Right now, I have code looking like the following when processing sign-ups on the server:

string salt = ..., verifier = ...;

var param = SrpParameters.Create8192<SHA512>();
var valid = salt.Length == param.HashSizeBytes * 2 && verifier.Length == param.PaddedLength;

try
{
    _ = new SrpInteger(salt);
    _ = new SrpInteger(verifier);
}
catch (Exception)
{
    valid = false;
}

if (valid)
{
    // Save to DB...
}

It would be convenient if the library exposed methods to do this kind of validation.

using the srp.net

Hello can you give example for world of warcraft how to be implemented this for create account?

Compatibility instructions with 'secure-remote-password' npm package

Thanks for the hard work on this library.

I noticed this line from the README file:

Based on and is compatible with secure-remote-password npm package by Linus Unnebäck.

We could update the README to explain how to do this (or maybe just having this issue is enough for people to find).

The answer is to do the following:

new SrpServer(new SrpParameters { PaddedLength = 0 })

Intended lifetime of SrpClient and SrpServer classes

Are SrpClient and SrpServer classes intended to be newly instantiated for each authentication roundtrip, or it is better to instantiate once at the start of the application (both client and server) and keep reuising the same instance?

Is there any security problem in reusing SrpServer for each authentication roundtrip from different clients?

I've verified that it is not possible to instantiate different SrpClient for each authentication phases (M1 and M2 must share the same SrpClient instance) but it works as intended if the same instance is shared for all the authentication roundtrip, so by extension, I'm wondering which is the most secure approach, if to keep the instance for future authentication as well (a singleton both on client and server) or to instantiate new one when it is needed (Basically with a "session" lifetime) or any working combination of this.

Expert help required! :)

Parlo.Docker

Hey!
Parlo.Docker is practical example of how to implement and use this library in a networked application. I'd appreciate it if you could leave a link in the README.

Thanks!

How to verify the client again?

Registration and authentication I understand but when the client calls an API and sends it's proof again do I have to drive a new session from it to check that he is authorized or is there some method to validate on the server that his proof matches mine?

On the client side I can validate the server proof like this: new SrpClient().VerifySession(Ephermal.Public, Session, proof);

How can I do the same on the server? And is it correct to send the proof on each request as you do with a jwt token?

SrpParameters thread-safety issue

Looks like SrpParameters aren't thread safe because HashAlgorithm.ComputeHash isn't thread safe.
The issue is easily reproduced like this:

[Test]
public async Task ParallelAuthenticationTest()
{
	var username = "demo";
	var password = "insecure";
	var parameters = new SrpParameters();
	var server = new SrpServer(parameters);

	// spawn multiple threads reusing the same SrpParameters instance
	var tasks = Enumerable.Range(0, 10).Select(i => Task.Run(() =>
	{
		var client = new SrpClient(parameters);

		// sign up
		var salt = client.GenerateSalt();
		var privateKey = client.DerivePrivateKey(salt, username, password);
		var verifier = client.DeriveVerifier(privateKey);

		// authenticate
		var clientEphemeral = client.GenerateEphemeral();
		var serverEphemeral = server.GenerateEphemeral(verifier);
		var clientSession = client.DeriveSession(clientEphemeral.Secret, serverEphemeral.Public, salt, username, privateKey);

		var serverSession = server.DeriveSession(serverEphemeral.Secret, clientEphemeral.Public, salt, username, verifier, clientSession.Proof);
		client.VerifySession(clientEphemeral.Public, clientSession, serverSession.Proof);

		// make sure both the client and the server have the same session key
		Assert.AreEqual(clientSession.Key, serverSession.Key);
	}));

	await Task.WhenAll(tasks);
}

Expected string to be an even number of characters

Sometimes the Javascript client reports this error:

RangeError: Expected string to be an even number of characters
    at hexToArrayBuffer (:3000/static/js/1.chunk.js:59422)
    at :3000/static/js/1.chunk.js:168052
    at Array.map (<anonymous>)
    at sha256 (:3000/static/js/1.chunk.js:168050)
    at client.js.exports.deriveSession (:3000/static/js/1.chunk.js:168160)
...

Looks like C# server can return an unpadded integer somewhere.

SRP implementation on iOS swift and dotnet core webapi

I am trying to implement Secure Remote Password (SRP-6a) protocol using iOS client and dot net core server. I am using https://github.com/Bouke/SRP at iOS client side and https://github.com/secure-remote-password/srp.net at dot net server side.

Here is my registration code at ios side.

        let n = "ac6bdb41324a9a9bf166de5e1389582faf72b6651987ee07fc3192943db56050a37329cbb4a099ed8193e0757767a13dd52312ab4b03310dcd7f48a9da04fd50e8083969edb767b0cf6095179a163ab3661a05fbd5faaae82918a9962f0b93b855f97993ec975eeaa80d740adbf4ff747359d041d5c33ea71d281e446b14773bca97b43a23fb801676bd207a436c6481f1d2b9078717461a5b9d32e688f87748544523b524b0d57d5ea77a2775d2ecfa032cfbdbf52fb3786160279004e57ae6af874e7303ce53299ccc041c7bc308d82a5698f3a8d0c38271ae35f8e9dbfbb694b5c803d89f7ae435de236d525f54759b65e372fcd68ef20fa7111f9e4aff73";
        let g = "02";
        let userName = "alice";
        
        let group = Group(prime: n, generator: g)!;
    
        let password = "myPassword";
        let regRequest = createSaltedVerificationKey(username: userName, password: password, group: group, algorithm: .sha512);

        let salt = String(BigUInt(regRequest.salt), radix: 16, uppercase: false);
        let verifier = String(BigUInt(regRequest.verificationKey), radix: 16, uppercase: false);

        print("\n Salt: \(salt)\n Verifier: \(verifier)\n")

Server stores userName, slat and verifier.

During user authentication the process is as follows:

Step 1: client sends the username to server and server returns the salt.
Step 2: client generates its public key and send it to server

        let client = Client(username: userName, password: password, group: group, algorithm: .sha512);

        let (username, clientPublicKey) = client.startAuthentication();

        let clientPubKey = String(BigUInt(clientPublicKey), radix: 16, uppercase: false);
        print("\n clientPubKey: \(clientPubKey)\n");

Step 3: In response to client public key server shares it's own public key

            var customParams = SrpParameters.Create<SHA512>(n, g);

            var server = new SrpServer(customParams);

            var serverEphemeral = server.GenerateEphemeral(verifier);

            Console.WriteLine("Publickey: " + serverEphemeral.Public);
            
             // server stores client public key, userName, and serverEphemeral.Secret

Step 4: Client calculates verifier

        let saltString = "salt shared by server at Step 1";
        let saltBigInt = BigUInt(saltString, radix: 16)!;
        let salt = saltBigInt.serialize();

        let serverPubKeyString = "Server public key in Step 3";
        let serverPubKeyBigInt = BigUInt(serverPubKeyString, radix: 16)!;
        let serverPublicKey = serverPubKeyBigInt.serialize();

        do {
            let clientKeyProof = try client.processChallenge(salt: salt, publicKey: serverPublicKey);

            let clientKeyProofString = String(BigUInt(clientKeyProof), radix: 16, uppercase: false);
            print("\n clientKeyProof: \(clientKeyProofString)\n");

        } catch {

        }

Step 5: client shares clientKeyProofString with server and server verifies it.

            var customParams = SrpParameters.Create<SHA512>(n, g);
            var server2 = new SrpServer(customParams);
            var serverSession = server2.DeriveSession(serverEphemeralSecret, clientPubKey, salt, userName, verifier, clientKeyProofString);

            Console.WriteLine("ServerProof: " + serverSession.Proof);

In this step I am getting System.Security.SecurityException: Client provided session proof is invalid.

Step 6: Client will verify serverProof

I am not able to proceed to step 6 as the exception on step 5. Any suggestion on what's going wrong here.

Should the `Generator` (`g`) be padded in the `M` calculation for SrpServer?

I'm trying to port an SRP implementation over to C# from python. The client is legacy and I cannot change it, so I'm just implementing a compatible server side of the protocol.

It was failing using this library until I noticed that the library doesn't Pad the g value here:
https://github.com/secure-remote-password/srp.net/blob/master/src/srp/SrpServer.cs#L130

(It doesn't pad in the SrcClient either. So it's consistent. But I'm wonder if we should update both?)

The python SRP library does pad this value when enabling support for rfc5054:
https://github.com/cocagne/pysrp/blob/master/srp/_pysrp.py#L205-L208

I tried jumping through the rfc5054 docs, but it doesn't even mention the M proof calculation so it's unclear if the padding should also apply there. It mentions padding k and u (which this library does) but doesn't claim one way or the other for M:
https://datatracker.ietf.org/doc/html/rfc5054

I was able to workaround this issue by doing the following:

SrpParameters parameters = SrpParameters.Create<SHA256>(N, g);
parameters.Generator = parameters.Pad(parameters.Generator);  // WORKAROUND
SrpServer srpServer = new SrpServer(parameters);
SrpSession serverSession = srpServer.DeriveSession((.........);

It works for my needs. But I guess the larger question for this issue is: Should this library actually be padding g in the M calculation? Or is the python srp implementation incorrect?

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.