Skip to content

Using SRP 6a protocol

Alexey Yakovlev edited this page May 23, 2018 · 7 revisions

Quoting the Wikipedia article:

The Secure Remote Password protocol (SRP) is an augmented password-authenticated key agreement (PAKE) protocol, specifically designed to work around existing patents.

Like all PAKE protocols, an eavesdropper or man in the middle cannot obtain enough information to be able to brute force guess a password without further interactions with the parties for each guess. This means that strong security can be obtained using weak passwords. Furthermore, being an augmented PAKE protocol, the server does not store password-equivalent data. This means that an attacker who steals the server data cannot masquerade as the client unless they first perform a brute force search for the password.

In layman's terms, during SRP (or any other PAKE protocol) authentication, one party (the "client" or "user") demonstrates to another party (the "server") that they know the password, without sending the password itself, nor any other information from which the password can be broken. The password never leaves the client and is unknown to the server.

Zyan implementation notes

Zyan SRP-6a implementation is based on (and is compatible with) secure-remote-password npm module by Linus Unnebäck. This means you can use credentials generated by the npm module to authenticate your Zyan application (and vice versa).

To enable SRP-6a authentication, use the following classes:

  • SrpCredentials — on the client side
  • SrpAuthenticationProvider — on the server side

Client-side code

var url = "tcpex://localhost:8090/MyServer";
var protocol = new TcpDuplexClientProtocolSetup(true);
var credentials = new SrpCredentials("[email protected]", "secret");

using (var connection = new ZyanConnection(url, protocol, credentials, true, true))
{
	var proxy = connection.CreateProxy<ISampleServer>();
	...
}

Note that while you still provide user name and password to the SrpCredentials constructor, the password is never sent across the wire (neither plain-text, nor encrypted). Only user name is sent to the server.

Server-side code

var userAccountRepository = new UserAccountRepository(); // implement this class
var authProvider = new SrpAuthenticationProvider(accounts);
var protocol = new TcpDuplexServerProtocolSetup(8090, provider, true);
var host = new ZyanComponentHost("MyServer", protocol);
host.RegisterComponent<ISampleServer, SampleServer>();

Server-side authentication provider for SRP-6a protocol takes a user account repository instance. It's a custom class used to abstract away the user account storage from the authentication provider. Account repository is a component that implements the following interface:

/// <summary>
/// Account repository for the SRP-6a protocol implementation.
/// </summary>
public interface ISrpAccountRepository
{
	/// <summary>
	/// Finds the user account data by the given username.
	/// </summary>
	/// <param name="userName">Name of the user.</param>
	ISrpAccount FindByName(string userName);
}

/// <summary>
/// SRP-6a account data.
/// </summary>
public interface ISrpAccount
{
	/// <summary>
	/// Gets the name of the user.
	/// </summary>
	string UserName { get; }

	/// <summary>
	/// Gets the salt.
	/// </summary>
	string Salt { get; }

	/// <summary>
	/// Gets the password verifier.
	/// </summary>
	string Verifier { get; }
}

Storing and retrieving the user accounts is up to the application developer. The typical storage for the user accounts is a database table.

Registering a new user account

SRP user accounts have three attributes: UserName, Salt and Verifier (called I, s and V in the original paper). To register a new account, use the SrpClient class to generate a salt and derive the private key and the verifier as shown below:

// ask the user for his userName and password
var srp = new SrpClient();
var salt = srp.GenerateSalt();
var privateKey = srp.DerivePrivateKey(salt, userName, password);
var verifier = srp.DeriveVerifier(privateKey);

// save userName, salt and verifier in the database

Note: password is never sent to the server and is not saved to the database in any form.

A note on case sensitivity

SRP-6a protocol user name and password are both case-sensitive. If your application requires case-insensitive user names and/or passwords, make sure to convert them to the lower case before processing them.

Adjustable parameters

This implementation of the SRP-6a protocol has the following adjustable parameters:

Name Description Default value
N Large safe prime 0xAC6BDB41... (2048 bits)
g Generator 2
H Hash function SHA256

The parameters are represented by SrpParameters class. The default constructor produces the default parameters:

var defaultParams = new SrpParameters(); // sha256, 2048-bit safe prime, g=2

To change the parameters, use SrpParameters.Create<T> static method where T is a hashing algorithm type defined in the System.Security.Cryptography namespace:

using System.Security.Cryptography;

var sha512 = SrpParameters.Create<SHA512>();
var sha384 = SrpParameters.Create<SHA384>();

N and g parameters are also adjustable:

var tmp = SrpParameters.Create<MD5>("23", "5"); // not safe, just an example

RFC5054 specifies a few groups for the SRP implementations based on 1024-, 1536-, 2048-, 3072-, 4096-, 6144-, and 8192-bit safe prime numbers. These standard groups are accessible through the SrpParameters.CreateXxxx<T> methods, for example:

  • SrpParameters.Create1024() — 1024-bit group, SHA1 hash
  • SrpParameters.Create4096() — 4096-bit group, SHA256 hash
  • SrpParameters.Create8192() — 8192-bit group, SHA512 hash, etc.

Using custom SRP parameters

SrpCredentials and SrpAuthenticationProvider classes both accept optional SrpParameters argument. Make sure to use the same parameters on both sides of the wire:

// interfaces
public static class Setup
{
    // sha384, 512-bit prime number (faster, but less safe), g=5
    public static SrpParameters MySrpParameters { get; } = SrpParameters.Create<SHA384>(
        "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", "05");
}

// server
var authProvider = new SrpAuthenticationProvider(userRepository, Setup.MySrpParameters);

// client
var credentials = new SrpCredentials(userName, password);

Note: saved credentials depend on the SRP parameters used. Adjusting the SRP parameters will require changing all the user's passwords.