using System; using System.Linq; using System.Numerics; using System.Security.Cryptography; using Helper; using Log; namespace LoginClient { public class SRP6Client { private readonly BigInteger n; // N private readonly BigInteger g; // g private readonly BigInteger b; // B private readonly BigInteger a; // A private readonly BigInteger x; // X private readonly BigInteger u; // U private readonly BigInteger s; // S private readonly BigInteger k; // K private readonly BigInteger m; // M private readonly byte[] p; private readonly byte[] account; private readonly BigInteger salt; // s, 服务端发过来的salt private const int lowerK = 3; private readonly BigInteger smallA ; private readonly HashAlgorithm hashAlgorithm; public SRP6Client( HashAlgorithm hashAlgorithm, BigInteger n, BigInteger g, BigInteger b, BigInteger salt, byte[] account, byte[] passwordMd5Hex) { this.smallA = BigIntegerHelper.RandUnsignedBigInteger(19); this.hashAlgorithm = hashAlgorithm; this.n = n; this.g = g; this.b = b; this.salt = salt; this.account = account; this.p = hashAlgorithm.ComputeHash(new byte[0] .Concat(account) .Concat(new[] { (byte)':' }) .Concat(passwordMd5Hex) .ToArray()); this.a = this.CalculateA(); // A = g ^ a % N this.x = this.CalculateX(); // X = H(s, P) this.u = this.CalculateU(); // U = H(A, B) this.s = this.CalculateS(); // S = (B - (k * g.ModExp(x, N))).ModExp(a + (u * x), N) this.k = this.CalculateK(); this.m = this.CalculateM(); // H(H(N) ^ H(g), H(P), s, A, B, K) } public BigInteger N { get { return this.n; } } public BigInteger G { get { return this.g; } } public BigInteger B { get { return this.b; } } public BigInteger A { get { return this.a; } } public BigInteger X { get { return this.x; } } public BigInteger U { get { return this.u; } } public BigInteger S { get { return this.s; } } public BigInteger K { get { return this.k; } } public BigInteger M { get { return this.m; } } public byte[] P { get { return this.p; } } public BigInteger Salt { get { return this.salt; } } public BigInteger SmallA { get { return this.smallA; } } public byte[] Account { get { return this.account; } } /// /// 计算X: X = H(s, P) /// /// private BigInteger CalculateX() { hashAlgorithm.Initialize(); var joinBytes = new byte[0] .Concat(this.Salt.ToUBigIntegerArray()) .Concat(this.P) .ToArray(); return hashAlgorithm.ComputeHash(joinBytes).ToUBigInteger(); } /// /// 计算A: A = g ^ a % N /// /// private BigInteger CalculateA() { return BigIntegerHelper.UModPow(this.G, this.SmallA, this.N); } /// /// 计算U: U = H(A, B) /// /// private BigInteger CalculateU() { hashAlgorithm.Initialize(); var joinBytes = new byte[0] .Concat(this.A.ToUBigIntegerArray(32)) .Concat(this.B.ToUBigIntegerArray(32)) .ToArray(); return hashAlgorithm.ComputeHash(joinBytes).ToUBigInteger(); } /// /// 计算S: S = (B - (k * g.ModExp(x, N))).ModExp(a + (u * x), N); /// /// private BigInteger CalculateS() { BigInteger s1 = this.B - BigIntegerHelper.UModPow(this.G, this.X, this.N) * lowerK; BigInteger s2 = this.SmallA + (this.U * this.X); BigInteger s3 = BigIntegerHelper.UModPow(s1, s2, this.N); return s3; } /// /// /// /// private BigInteger CalculateK() { hashAlgorithm.Initialize(); byte[] sBytes = this.S.ToUBigIntegerArray(); int halfLength = sBytes.Length / 2; var kBytes = new byte[40]; var halfS = new byte[halfLength]; for (int i = 0; i < halfLength; ++i) { halfS[i] = sBytes[i * 2]; } var p1 = hashAlgorithm.ComputeHash(halfS); for (int i = 0; i < 20; ++i) { kBytes[i * 2] = p1[i]; } for (int i = 0; i < halfLength; ++i) { halfS[i] = sBytes[i * 2 + 1]; } var p2 = hashAlgorithm.ComputeHash(halfS); for (int i = 0; i < 20; ++i) { kBytes[i * 2 + 1] = p2[i]; } return kBytes.ToUBigInteger(); } /// /// /// /// private BigInteger CalculateM() { hashAlgorithm.Initialize(); var hashN = hashAlgorithm.ComputeHash(this.N.ToUBigIntegerArray()); var hashG = hashAlgorithm.ComputeHash(this.G.ToUBigIntegerArray()); // 这里与标准srp6不一样,只异或了20个byte,实际上有32个byte for (var i = 0; i < 20; ++i) { hashN[i] ^= hashG[i]; } var hashGXorhashN = hashN; // H(N) ^ H(g) var hashedIdentity = hashAlgorithm.ComputeHash(this.Account); // H(I) // H(H(N) ^ H(g), H(P), s, A, B, K_c) var mBytes = hashAlgorithm.ComputeHash(new byte[0] .Concat(hashGXorhashN) .Concat(hashedIdentity) .Concat(this.Salt.ToUBigIntegerArray(32)) .Concat(this.A.ToUBigIntegerArray(32)) .Concat(this.B.ToUBigIntegerArray(32)) .Concat(this.K.ToUBigIntegerArray(40)) .ToArray()); return mBytes.ToUBigInteger(); } public byte[] CalculateGateDigest(uint clientSeed, uint serverSeed) { hashAlgorithm.Initialize(); var digest = hashAlgorithm.ComputeHash(new byte[0] .Concat(this.Account) .Concat(new byte[4] { 0, 0, 0, 0 }) .Concat(BitConverter.GetBytes(clientSeed)) .Concat(BitConverter.GetBytes(serverSeed)) .Concat(k.ToUBigIntegerArray()) .ToArray()); return digest; } } }