public Task <SrpResult> SrpHandshake(BigInteger a, BigInteger m1) { if (!IsExists) { throw new AccountDoesNotExistException($"account {this.GetPrimaryKeyString()} does not exist"); } if (AuthState != AccountAuthState.ParametersGenerated) { throw new AccountStateException("cannot handshake without first generating SRP initial parameters"); } if (a.IsZero) { throw new SrpException("A cannot be zero"); } if (BPublic.IsZero) { throw new SrpException("B cannot be zero"); } using (var sha1 = new Digester(SHA1.Create())) { var u = BigIntegers.FromUnsignedByteArray(sha1.CalculateDigest(a, BPublic)); var s = BigInteger.ModPow(a * BigInteger.ModPow(State.Verifier, u, N), BPrivate, N); var t = s.ToByteArray(32); var t1 = new byte[16]; var vK = new byte[40]; // modified srp - uses a session key (vK, K) created by interleaving two hashes (tK) created from half (t1) of the shared secret (s) for (int i = 0; i < 16; ++i) { t1[i] = t[i * 2]; } var tK = sha1.CalculateDigest(t1); for (int i = 0; i < 20; ++i) { vK[i * 2] = tK[i]; } for (int i = 0; i < 16; ++i) { t1[i] = t[i * 2 + 1]; } tK = sha1.CalculateDigest(t1); for (int i = 0; i < 20; ++i) { vK[i * 2 + 1] = tK[i]; } SessionKey = BigIntegers.FromUnsignedByteArray(vK); var nghash = sha1.CalculateDigest(N.ToByteArray(32)); var ghash = sha1.CalculateDigest(new[] { G }); for (int i = 0; i < sha1.DigestSize; ++i) { nghash[i] ^= ghash[i]; } var serverM1Bytes = sha1.CalculateDigest(new byte[][] { nghash, sha1.CalculateDigest(State.Name), State.Salt.ToByteArray(32), a.ToByteArray(32), BPublic.ToByteArray(32), vK, }); // if client M1 matches server M1, then client and server have agreed on shared SessionKey, but only server knows that right now var serverM1 = BigIntegers.FromUnsignedByteArray(serverM1Bytes); if (serverM1 == m1) { // need to send M2 so client knows session key is shared also var m2Bytes = sha1.CalculateDigest(new byte[][] { a.ToByteArray(32), serverM1Bytes, SessionKey.ToByteArray(40), }); AuthState = AccountAuthState.Authenticated; var m2 = BigIntegers.FromUnsignedByteArray(m2Bytes); return(Task.FromResult(new SrpResult(true, m2))); } else { Deauthenticate().Wait(); return(Task.FromResult(new SrpResult(false, BigInteger.Zero))); } } }