You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.7 KiB
168 lines
5.7 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Math; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security; |
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Agreement.Srp |
|
{ |
|
/** |
|
* Implements the client side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe. |
|
* This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper |
|
* "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002" |
|
*/ |
|
public class Srp6Client |
|
{ |
|
protected BigInteger N; |
|
protected BigInteger g; |
|
|
|
protected BigInteger privA; |
|
protected BigInteger pubA; |
|
|
|
protected BigInteger B; |
|
|
|
protected BigInteger x; |
|
protected BigInteger u; |
|
protected BigInteger S; |
|
|
|
protected BigInteger M1; |
|
protected BigInteger M2; |
|
protected BigInteger Key; |
|
|
|
protected IDigest digest; |
|
protected SecureRandom random; |
|
|
|
public Srp6Client() |
|
{ |
|
} |
|
|
|
/** |
|
* Initialises the client to begin new authentication attempt |
|
* @param N The safe prime associated with the client's verifier |
|
* @param g The group parameter associated with the client's verifier |
|
* @param digest The digest algorithm associated with the client's verifier |
|
* @param random For key generation |
|
*/ |
|
public virtual void Init(BigInteger N, BigInteger g, IDigest digest, SecureRandom random) |
|
{ |
|
this.N = N; |
|
this.g = g; |
|
this.digest = digest; |
|
this.random = random; |
|
} |
|
|
|
public virtual void Init(Srp6GroupParameters group, IDigest digest, SecureRandom random) |
|
{ |
|
Init(group.N, group.G, digest, random); |
|
} |
|
|
|
/** |
|
* Generates client's credentials given the client's salt, identity and password |
|
* @param salt The salt used in the client's verifier. |
|
* @param identity The user's identity (eg. username) |
|
* @param password The user's password |
|
* @return Client's public value to send to server |
|
*/ |
|
public virtual BigInteger GenerateClientCredentials(byte[] salt, byte[] identity, byte[] password) |
|
{ |
|
this.x = Srp6Utilities.CalculateX(digest, N, salt, identity, password); |
|
this.privA = SelectPrivateValue(); |
|
this.pubA = g.ModPow(privA, N); |
|
|
|
return pubA; |
|
} |
|
|
|
/** |
|
* Generates client's verification message given the server's credentials |
|
* @param serverB The server's credentials |
|
* @return Client's verification message for the server |
|
* @throws CryptoException If server's credentials are invalid |
|
*/ |
|
public virtual BigInteger CalculateSecret(BigInteger serverB) |
|
{ |
|
this.B = Srp6Utilities.ValidatePublicValue(N, serverB); |
|
this.u = Srp6Utilities.CalculateU(digest, N, pubA, B); |
|
this.S = CalculateS(); |
|
|
|
return S; |
|
} |
|
|
|
protected virtual BigInteger SelectPrivateValue() |
|
{ |
|
return Srp6Utilities.GeneratePrivateValue(digest, N, g, random); |
|
} |
|
|
|
private BigInteger CalculateS() |
|
{ |
|
BigInteger k = Srp6Utilities.CalculateK(digest, N, g); |
|
BigInteger exp = u.Multiply(x).Add(privA); |
|
BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N); |
|
return B.Subtract(tmp).Mod(N).ModPow(exp, N); |
|
} |
|
|
|
/** |
|
* Computes the client evidence message M1 using the previously received values. |
|
* To be called after calculating the secret S. |
|
* @return M1: the client side generated evidence message |
|
* @throws CryptoException |
|
*/ |
|
public virtual BigInteger CalculateClientEvidenceMessage() |
|
{ |
|
// Verify pre-requirements |
|
if (this.pubA == null || this.B == null || this.S == null) |
|
{ |
|
throw new CryptoException("Impossible to compute M1: " + |
|
"some data are missing from the previous operations (A,B,S)"); |
|
} |
|
// compute the client evidence message 'M1' |
|
this.M1 = Srp6Utilities.CalculateM1(digest, N, pubA, B, S); |
|
return M1; |
|
} |
|
|
|
/** Authenticates the server evidence message M2 received and saves it only if correct. |
|
* @param M2: the server side generated evidence message |
|
* @return A boolean indicating if the server message M2 was the expected one. |
|
* @throws CryptoException |
|
*/ |
|
public virtual bool VerifyServerEvidenceMessage(BigInteger serverM2) |
|
{ |
|
// Verify pre-requirements |
|
if (this.pubA == null || this.M1 == null || this.S == null) |
|
{ |
|
throw new CryptoException("Impossible to compute and verify M2: " + |
|
"some data are missing from the previous operations (A,M1,S)"); |
|
} |
|
|
|
// Compute the own server evidence message 'M2' |
|
BigInteger computedM2 = Srp6Utilities.CalculateM2(digest, N, pubA, M1, S); |
|
if (computedM2.Equals(serverM2)) |
|
{ |
|
this.M2 = serverM2; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Computes the final session key as a result of the SRP successful mutual authentication |
|
* To be called after verifying the server evidence message M2. |
|
* @return Key: the mutually authenticated symmetric session key |
|
* @throws CryptoException |
|
*/ |
|
public virtual BigInteger CalculateSessionKey() |
|
{ |
|
// Verify pre-requirements (here we enforce a previous calculation of M1 and M2) |
|
if (this.S == null || this.M1 == null || this.M2 == null) |
|
{ |
|
throw new CryptoException("Impossible to compute Key: " + |
|
"some data are missing from the previous operations (S,M1,M2)"); |
|
} |
|
this.Key = Srp6Utilities.CalculateKey(digest, N, S); |
|
return Key; |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|