Secure Remote Password 6a (SRP6a)

The storing of passwords is an obvious attack point on any system. The method to store these, such as with a hash of the password, is often…

Secure Remote Password 6a (SRP6a)

The storing of passwords is an obvious attack point on any system. The method to store these, such as with a hash of the password, is often open to dictionary attacks and brute force.

At the root of the problem is that they normally have a username and a password and want to prove that we have knowledge of these but do not reveal them. For this, we can use a Password-based Authenticated Key-Exchange (PAKE) protocol, and where a client knows a secret, and the server stores a verifier of the secret. This could be done over a Diffie-Hellman method, but this does not mutually authenticate each of the sides and is thus open to Eve-in-the-Middle attacks. An improved method of mutual authentication is SRP6a (Secure Remote Password 6a), and where Bob authenticates himself to Trent, and without revealing his password. Also, Trent authenticates himself back to Bob, too — all based on some initial random salt values.

Overall, SRP6a is standardized in RFC 5054 [here]:

SRP6a method

First the client and server agree on a large prime number of N. Bob then has a clear password of P, and a username of u. He then generates a salt value (s) and takes a hash of his passsword to get:

and where Bob has provided the salt value (s) and the password (P). He also stores a password verifier of:

We now have u,s,Xp stored on the client (Trent) for Bob’s password.

Login

Bob wants to log onto the server and generates a random number of Ws

and sends:

Trent then generates a new salt value (s) and sends along with Ys=g^s

and computes:

Trent then sends:

Bob then takes his password (P) and then new salt value and computes:

and:

and:

Computing the secret

Bob computes the shared secret (K) with:

Trent computes the shared secret (K) with:

Comparing secrets

To check they have the same secrets, Bob sends:

H(Z,K)

and Trent sends:

H(WP,K)

Both Bob and Trent can compute these values to check.

Overview

Here is an overview of the process:

Code

First we install the Bouncy Castle library:

dotnet add package BouncyCastle.Cryptography

Next some code [here]:

namespace SRP6a
{
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Crypto.Agreement.Srp;
using System.Text;
class Program
{

static void Main(string[] args)
{



var t="rfc5054_1024";
var username="Admin";
var password="test";
if (args.Length >0) username=args[0];
if (args.Length >1) password=args[1];
if (args.Length >2) t=args[2];



try {
byte[] I = Encoding.UTF8.GetBytes(username);
byte[] P = Encoding.UTF8.GetBytes(password);
var random = new SecureRandom();
byte[] s = new byte[16];
random.NextBytes(s);
Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
BigInteger N= Srp6StandardGroups.rfc5054_1024.N;
BigInteger G= Srp6StandardGroups.rfc5054_1024.G;

if (t.Equals("rfc5054_1536")) {
N= Srp6StandardGroups.rfc5054_1536.N;
G= Srp6StandardGroups.rfc5054_1536.G;
} else if (t.Equals("rfc5054_2048")) {
N= Srp6StandardGroups.rfc5054_2048.N;
G= Srp6StandardGroups.rfc5054_2048.G;
}
Srp6GroupParameters group = new Srp6GroupParameters(N,G);
gen.Init(group, new Sha256Digest());
BigInteger v = gen.GenerateVerifier(s, I, P);
Srp6Client client = new Srp6Client();
client.Init(group, new Sha256Digest(), random);
Srp6Server server = new Srp6Server();
server.Init(group, v, new Sha256Digest(), random);
BigInteger A = client.GenerateClientCredentials(s, I, P);
BigInteger B = server.GenerateServerCredentials();
BigInteger clientS = client.CalculateSecret(B);
BigInteger serverS = server.CalculateSecret(A);
Console.WriteLine("User: {0}", username);
Console.WriteLine("Password: {0}", password);
Console.WriteLine("== Bob and Trent agree on == ");
Console.WriteLine("N = {0}",N);
Console.WriteLine("G = {0}",G);
Console.WriteLine("== Bob computes == ") ;
Console.WriteLine("s = {0}",Convert.ToHexString(s));
Console.WriteLine("I = {0}",Convert.ToHexString(I));
Console.WriteLine("P = {0}",Convert.ToHexString(P));
Console.WriteLine("v = {0}",v);
Console.WriteLine("A = {0}",A);
Console.WriteLine("B = {0}",B);
Console.WriteLine("Client secret = {0}",clientS);
Console.WriteLine("Server secret = {0}",serverS);
if (!clientS.Equals(serverS))
{
Console.WriteLine("SRP agreement failed - client/server calculated different secrets");
}



} catch (Exception e) {
Console.WriteLine("Error: {0}",e.Message);
}
}
}

}

The parameters used come from the RFC 5054 standard. In the following, we see that G=2, and N is defined as a standard integer value [here]:

A sample run with RFC 5054 with a 1,024-bit prime number is [here]:

User: Bob
Password: Qwerty123
Method: rfc5054_1024
== Bob and Trent agree on ==
N = 167609434410335061345139523764350090260135525329813904557420930309800865859473551531551523800013916573891864789934747039010546328480848979516637673776605610374669426214776197828492691384519453218253702788022233205683635831626913357154941914129985489522629902540768368409482248290641036967659389658897350067939
G = 2
== Bob computes ==
s = EE3F125AB0359106EDDA22A953D916B9
I = 426F62
P = 517765727479313233
v = 106863960403028565230396707621798705514952381680385812237027464649252480857979865603565771830755833886323961115121377760509397322781066047553869366298482345914987815709164065951342725433404372540352485989957168527616809798551146507187185045501576059965652969204200261275964572376020295233694791099708674655146
A = 137299929583118070623779718209130265193736698070855142108781488510225005186894217738795047384692867180293480974658979880701851064599331901433137871802034309702973158421704874638930562566922638196948409465905232553700139634003563875277538558468225604036607702469312776392807282399823436572088998738626849364844
B = 77764999500277804875651661463714082863694222456806723110463284434901177321036602372650131928474328427576166940211838132577528311947322821515587602542277504508438949343931388751097227892827356036272147004715136532003568976998416400258822686593249346466227043845981711397299114513524323626493563007372214718301
Client secret = 55758144061619012207740134032872104036748887540288828558379259237760082204851833190795755165909664371081175502838714780254832608301417450256861926632437835755704053310145003311618771181524689576321757599419199245162780026462564229493554726682181012460408513850942905820693577308367151461852450731064351709494
Server secret = 55758144061619012207740134032872104036748887540288828558379259237760082204851833190795755165909664371081175502838714780254832608301417450256861926632437835755704053310145003311618771181524689576321757599419199245162780026462564229493554726682181012460408513850942905820693577308367151461852450731064351709494

If we convert the value of N here to hex, we see it matches with the RFC specification:

>>> hex(167609434410335061345139523764350090260135525329813904557420930309800865859473551531551523800013916573891864789934747039010546328480848979516637673776605610374669426214776197828492691384519453218253702788022233205683635831626913357154941914129985489522629902540768368409482248290641036967659389658897350067939)
'0xeeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3'

A sample run with RFC 5054 with a 2,048-bit prime number is:

User: Bob
Password: Qwerty123
Method: rfc5054_2048
== Bob and Trent agree on ==
N = 21766174458617435773191008891802753781907668374255538511144643224689886235383840957210909013086056401571399717235807266581649606472148410291413364152197364477180887395655483738115072677402235101762521901569820740293149529620419333266262073471054548368736039519702486226506248861060256971802984953561121442680157668000761429988222457090413873973970171927093992114751765168063614761119615476233422096442783117971236371647333871414335895773474667308967050807005509320424799678417036867928316761272274230314067548291133582479583061439577559347101961771406173684378522703483495337037655006751328447510550299250924469288819
G = 2
== Bob computes ==
s = 5396268FE355930EB37AA2574720D8C1
I = 426F62
P = 517765727479313233
v = 19959963541851260727502546845826316265844324247151721313692289403433638227468022313234604516603672789402606959761994827405872833616421888234002673176029364339022265409570323263448460000159014081793602234011730368139881432004220317745506836458004603308469037608574853682886802634141930704224610414710873307177928414038110653420057771057408660039878206005292180962739614804083658717248347028807158822571368946900591508675480349386032725057357786701779495349501207696401006421701614249076319797975820714696165323547898208063471061374459550412735688190377637218999114845206712399859239451536518471490682314115294009374332
A = 3092808917874033053073672551944132542062000594271267743839367502283422272061689499798843958237768641191675769619894964936105714822494188719294588456596023135724542556824691443157314695645251723727185474782580502415925894117140867843506627208380931069778866715427141644716881260984940435976594408330958025060939069989154313123295548968311910426644652886197060748867204660322236360101970240933665492734822411434125304745068988160428791854119209085402057414866328563300603590027429486014136043199544272429999499767127580184694225426078267749566690847586383541188031551952765581310981376897721537985703979856134235442541
B = 2469028235209861157427176252900004578940563577199810356831697876843019571520228285602835046420413993018376212353992693388068349276322948928685270953950759549773967224149082817645192806475022648183092219084547685274219171368201462553551906528833940275513411987016443502192286797697457837066092374660008376391969813084717399735952571743546968928781517495161000309297282543618324727960398734408104997262847063033761399702293658059483256964978275660749613019471270986816194956257714159649614612400720290121380970957006911016571986564412799511290291857238452887914338734314701080138187120908976640456953561160149570584276
Client secret = 16341785036682692227793685966534513488966442878763766823571023742345191114327808943590763937347578894746322985091517838721648555074852868448615850618748214770318567738709417890676616283960930804500542442408453837749249862839431615561763318421216673632104452014114148924095705087582042450147769760929742377965102222316219800332614903634786563635047917061707271122392220816517205928103729097457975053257652074330130243494385113957813502450068681912239505525308886391788718696494531317789682386169579069840946507239255959300874395397195728742542909855495013007689780078300614019573313588089056433143602557694596788957124
Server secret = 16341785036682692227793685966534513488966442878763766823571023742345191114327808943590763937347578894746322985091517838721648555074852868448615850618748214770318567738709417890676616283960930804500542442408453837749249862839431615561763318421216673632104452014114148924095705087582042450147769760929742377965102222316219800332614903634786563635047917061707271122392220816517205928103729097457975053257652074330130243494385113957813502450068681912239505525308886391788718696494531317789682386169579069840946507239255959300874395397195728742542909855495013007689780078300614019573313588089056433143602557694596788957124

Conclusions

SRP6a is a good starting point in understanding PAKE methods. You can try it here:

https://asecuritysite.com/bouncy/bc_srp6a