srp.net

SRP-6a protocol implementation for .NET Standard 1.6+ and .NET Framework 3.5+

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