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.A t 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.
SRP6a Mutual Authentication using Bouncy Castle and C# |
Storing the password
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:
\(X_s = s.H(P) \pmod N\)
and where Bob has provided the salt value (\(s\)) and the password (\(P\)). He also stores a password verifier of:
\(X_p = g ^ {X_s} \pmod N\)
We now have \(u,s,X_p\) stored on the client (Trent) for Bob's password.
Login
Bob wants to log onto the server and generates a random number of \(W_s\) and sends:
\(u, W_p = g ^ {W_s} \pmod N\)
Trent then generates a new salt value (\(s\)) and send along with \(Y_s=g^s\) and computes:
\(Y_p = g ^ {Y_s} \pmod N\)
Trent then sends:
\(s, Z = X_p + Y_p \pmod N\)
Bob then takes his password (\(P\)) and then new salt value and computes:
\(X_s =s.H(P) \pmod N \)
and:
\(X_P = g^{X_s} \pmod N\)
and:
\(Y_p=Z-X_p \pmod N\)
Computing the secret
Bob computes the shared secret (\(K\)) with:
\(S={(Y_P)}^{W_s+X_s} \pmod N\)
Trent computes the shared secret (\(K\)) with:
\(S={(W_P.X_P)}^{Y_s} \pmod N \)
Comparing secrets
To check they have the same secrets, Bob takes the value that Trent sent (\(Z\)) and sends back:
\(H(Z,K)\)
and Trent takes the value that Bob generated (\(W_P\)) sends back:
\(H(W_P,K)\)
Both Bob and Trent can compute these values to check.
Overview
Code
First we install the Bouncy Castle library:
dotnet add package BouncyCastle.Cryptography
Next some code:
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]:
Appendix A. SRP Group Parameters The 1024-, 1536-, and 2048-bit groups are taken from software developed by Tom Wu and Eugene Jhong for the Stanford SRP distribution, and subsequently proven to be prime. The larger primes are taken from [MODP], but generators have been calculated that are primitive roots of N, unlike the generators in [MODP]. The 1024-bit and 1536-bit groups MUST be supported. 1. 1024-bit Group The hexadecimal value for the prime is: EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C 9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4 8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29 7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A FD5138FE 8376435B 9FC61D2F C0EB06E3 The generator is: 2. 2. 1536-bit Group The hexadecimal value for the prime is: 9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961 4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843 80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5 6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E 8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB The generator is: 2.
A sample run with RFC 5054 with a 1,024-bit prime number is:
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