public SRP6a(Account account) { this.Account = account; this.IdentitySalt = H.ComputeHash(Encoding.ASCII.GetBytes(this.Account.Email)).ToHexString(); // Identity salt that's hashed using account email. // calculate server's public ephemeral value. this.b = GetRandomBytes(128).ToBigInteger(); // server's secret ephemeral value. var gModb = BigInteger.ModPow(g, b, N); // pow(g, b, N) var k = H.ComputeHash(new byte[0].Concat(N.ToArray()).Concat(g.ToArray()).ToArray()).ToBigInteger(); // Multiplier parameter (k = H(N, g) in SRP-6a this.B = BigInteger.Remainder((this.Account.PasswordVerifier.ToBigInteger() * k) + gModb, N); // B = (k * v + pow(g, b, N)) % N // cook the logon challenge message this.LogonChallenge = new byte[0] .Concat(new byte[] { 0 }) // command = 0 .Concat(this.IdentitySalt.ToByteArray()) // identity-salt - generated by hashing account email. .Concat(this.Account.Salt) // account-salt - generated on account creation. .Concat(B.ToArray()) // server's public ephemeral value (B) .Concat(SecondChallenge.ToArray()) // second challenge .ToArray(); }
/// <summary> /// Verifies the account using srp6a session provided values by client. /// </summary> /// <param name="ABytes">Client's public ephemeral</param> /// <param name="M_client">Client M.</param> /// <param name="seed">Session seed.</param> /// <returns></returns> public bool Verify(byte[] ABytes, byte[] M_client, byte[] seed) { var A = ABytes.ToBigInteger(); // client's public ephemeral var u = H.ComputeHash(new byte[0].Concat(ABytes).Concat(B.ToArray()).ToArray()).ToBigInteger(); // Random scrambling parameter - u = H(A, B) var S_s = BigInteger.ModPow(A * BigInteger.ModPow(this.Account.PasswordVerifier.ToBigInteger(), u, N), b, N); // calculate server session key - S = (Av^u) ^ b this.SessionKey = Calc_K(S_s.ToArray()); // K = H(S) - Shared, strong session key. var K = this.SessionKey.ToBigInteger(); var hashgxorhashN = Hash_g_and_N_and_xor_them().ToBigInteger(); // H(N) ^ H(g) var hashedIdentitySalt = H.ComputeHash(Encoding.ASCII.GetBytes(this.IdentitySalt)); // H(I) var M = H.ComputeHash(new byte[0] // verify client M_client - H(H(N) ^ H(g), H(I), s, A, B, K_c) .Concat(hashgxorhashN.ToArray()) .Concat(hashedIdentitySalt) .Concat(this.Account.Salt.ToArray()) .Concat(ABytes) .Concat(B.ToArray()) .Concat(K.ToArray()) .ToArray()); // We can basically move m_server, secondproof and logonproof calculation behind the M.CompareTo(M_client) check, but as we have an option DisablePasswordChecks // which allows authentication without the correct password, they should be also calculated for wrong-passsword auths. /raist. // calculate server proof of session key var M_server = H.ComputeHash(new byte[0] // M_server = H(A, M_client, K) .Concat(ABytes) .Concat(M_client) .Concat(K.ToArray()) .ToArray()); var secondProof = GetSecondProof(Encoding.ASCII.GetBytes(this.Account.Email), seed, SecondChallenge.ToArray()); // cook logon proof message. LogonProof = new byte[0] .Concat(new byte[] { 3 }) // command = 3 - server sends proof of session key to client .Concat(M_server) // server's proof of session key .Concat(secondProof.ToArray()) // second proof .ToArray(); if (M.CompareTo(M_client)) // successful authentication session. { return(true); } else // authentication failed because of invalid credentals. { return(false); } }