public unsafe byte[] GetPacked() { ulong id = this.id; byte *ptr = stackalloc byte[9]; ptr[0] = 0; byte size = 1; for (byte i = 0; id != 0 && size < 9; ++i) { if ((id & 0xff) != 0) { ptr[0] |= (byte)(1 << i); ptr[size] = (byte)(id & 0xff); ++size; } id >>= 8; } return(FixedBuffers.ToArray(ptr, size)); }
public unsafe override void ExecuteCommand(AuthSession session, AuthRequest request, LogonProof header) { log.DebugFormat("handling {0} opcode. securityflags = {1}, keycount = {2}", header.Opcode, header.SecurityFlags, header.KeyCount); using (Digester sha1 = new Digester(new SHA1CryptoServiceProvider())) { // client sends public ephemeral value A session.SRP.A = BigIntegers.FromUnsignedByteArray(FixedBuffers.ToArray(header.A, 32)); log.DebugFormat("server N = {0:X}", session.SRP.N); log.DebugFormat("client A = {0:X}", session.SRP.A); log.DebugFormat("server B = {0:X}", session.SRP.B); log.DebugFormat("server s = {0:X}", session.SRP.s); log.DebugFormat("server v = {0:X}", session.SRP.v); // srp is required to abort if A or B is 0 if (session.SRP.A.IsZero) { throw new ArgumentException("security abort: SRP6.A = 0"); } session.SRP.u = BigIntegers.FromUnsignedByteArray(sha1.CalculateDigest(session.SRP.A, session.SRP.B)); session.SRP.S = BigInteger.ModPow(session.SRP.A * BigInteger.ModPow(session.SRP.v, session.SRP.u, session.SRP.N), session.SRP.b, session.SRP.N); log.DebugFormat("server u = {0:X}", session.SRP.u); log.DebugFormat("server S = {0:X}", session.SRP.S); // BigIntever.ToByteArrays are chopped/padded on the right (taking the 'Left' of the array) because of little endian. we're either killing the sign byte or padding with leading 0's in case of small number byte[] t = Arrays.Left(session.SRP.S.ToByteArray(), 32); byte[] t1 = new byte[16]; byte[] 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]; } byte[] 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]; } session.SRP.K = BigIntegers.FromUnsignedByteArray(vK); log.DebugFormat("server K = {0:X}", session.SRP.K); byte[] Nghash = sha1.CalculateDigest(Arrays.Left(session.SRP.N.ToByteArray(), 32)); byte[] ghash = sha1.CalculateDigest(Arrays.Left(session.SRP.g.ToByteArray(), 1)); for (int i = 0; i < sha1.DigestSize; ++i) { Nghash[i] ^= ghash[i]; } // calculating M1 byte[] M1S = sha1.CalculateDigest(new byte[][] { Nghash, sha1.CalculateDigest(session.SRP.I), Arrays.Left(session.SRP.s.ToByteArray(), 32), Arrays.Left(session.SRP.A.ToByteArray(), 32), Arrays.Left(session.SRP.B.ToByteArray(), 32), vK, }); byte[] M1C = FixedBuffers.ToArray(header.M1, 20); BigInteger M1s = BigIntegers.FromUnsignedByteArray(M1S); BigInteger M1c = BigIntegers.FromUnsignedByteArray(M1C); log.DebugFormat("client M1 = {0:X}", M1c); log.DebugFormat("server M1 = {0:X}", M1s); // if client M1 matches server M1, then client and server have agreed on shared session key (vK, K), but only server knows that right now if (M1s == M1c) { session.Status = AuthStatus.Authenticated; // need to send M2 so client knows session key is shared also byte[] M2S = sha1.CalculateDigest(new byte[][] { Arrays.Left(session.SRP.A.ToByteArray(), 32), M1C, Arrays.Left(session.SRP.K.ToByteArray(), 40), }); BigInteger M2s = BigIntegers.FromUnsignedByteArray(M2S); log.InfoFormat("authentication successful for user {0}", session.SRP.I); log.DebugFormat("server M2 = {0:X}", M2s); int result = session.Server.AuthDB.ExecuteNonQuery("update account set last_login = now(), last_ip = ?, session_key = ? where name = ?", session.RemoteEndPoint.Address.ToString(), string.Format("{0:X}", session.SRP.K), session.SRP.I); if (result != 1) { log.ErrorFormat("expected 1 result when updating account for login, but got {0}", result); } using (ByteBuffer response = new ByteBuffer()) { response.Append((byte)AuthRequestOpcode.LogonProof); response.Append((byte)AuthResponseOpcode.Success); response.Append(Arrays.Left(M2S, 20)); response.Append(0); session.Send(response.GetArraySegment()); } } else { log.InfoFormat("authentication failed for user {0}", session.SRP.I); using (ByteBuffer response = new ByteBuffer()) { response.Append((byte)AuthRequestOpcode.LogonProof); response.Append((byte)AuthResponseOpcode.FailBadCredentials); session.Send(response.GetArraySegment()); } } } }