using Esiur.Core; using Esiur.Data; using Esiur.Net.Packets; using Esiur.Resource; using Esiur.Security.Authority; using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace Esiur.Security.Membership { public class SimpleMembership : IMembership { public bool GuestsAllowed { get; set; } = false; public event ResourceEventHandler Authorization { add { } remove { } } KeyList users = new KeyList(); KeyList tokens = new KeyList(); public class QuestionAnswer { public string Question { get; set; } public object Answer { get; set; } public bool Hashed { get; set; } } public class UserInfo { public string Username { get; set; } public string Password { get; set; } public QuestionAnswer[] Questions { get; set; } public List Results { get; set; } = new List(); } public class TokenInfo { public ulong Index { get; set; } public string Token { get; set; } public string Username { get; set; } } public void AddToken(ulong index, string value, string username) { if (users.ContainsKey(username)) throw new Exception("User not found."); tokens.Add(index, new TokenInfo() { Index = index, Token = value, Username = username }); } public void AddUser(string username, string password, QuestionAnswer[] questions) { users.Add(username, new UserInfo() { Password = password, Username = username, Questions = questions }); } public void RemoveToken(ulong index) => tokens.Remove(index); public void RemoveUser(string username) => users.Remove(username); public AsyncReply Authorize(Session session) { if (session.AuthorizedAccount.StartsWith("g-")) return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success }); if (users[session.AuthorizedAccount].Questions.Length > 0) { var q = users[session.AuthorizedAccount].Questions.First(); var r = new Random(); var format = q.Answer.GetIAuthFormat(); var ar = new AuthorizationResults() { Clue = q.Question, Destination = IIPAuthPacketIAuthDestination.Self, Reference = (uint)r.Next(), RequiredFormat = format, Expire = DateTime.Now.AddSeconds(60), Response = q.Hashed ? AuthorizationResultsResponse.IAuthHashed : AuthorizationResultsResponse.IAuthPlain }; users[session.AuthorizedAccount].Results.Add(ar); return new AsyncReply(ar); } else { return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success }); } } public AsyncReply AuthorizeEncrypted(Session session, uint reference, IIPAuthPacketPublicKeyAlgorithm algorithm, byte[] value) { throw new NotImplementedException(); } public AsyncReply AuthorizeHashed(Session session, uint reference, IIPAuthPacketHashAlgorithm algorithm, byte[] value) { if (algorithm != IIPAuthPacketHashAlgorithm.SHA256) throw new NotImplementedException(); var ar = users[session.AuthorizedAccount].Results.First(x => x.Reference == reference); var qa = users[session.AuthorizedAccount].Questions.First(x => x.Question == ar.Clue); // compute hash var remoteNonce = (byte[])session.RemoteHeaders[IIPAuthPacketHeader.Nonce]; var localNonce = (byte[])session.LocalHeaders[IIPAuthPacketHeader.Nonce]; var hashFunc = SHA256.Create(); // local nonce + password or token + remote nonce var challenge = hashFunc.ComputeHash(new BinaryList() .AddUInt8Array(remoteNonce) .AddUInt8Array(Codec.Compose(qa.Answer, null)) .AddUInt8Array(localNonce) .ToArray()); if (challenge.SequenceEqual(value)) return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success }); else return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Failed }); } public AsyncReply AuthorizePlain(Session session, uint reference, object value) { var ar = users[session.AuthorizedAccount].Results.First(x => x.Reference == reference); var qa = users[session.AuthorizedAccount].Questions.First(x => x.Question == ar.Clue); if (qa.Answer.ToString() == value.ToString()) return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Success }); else return new AsyncReply(new AuthorizationResults() { Response = AuthorizationResultsResponse.Failed }); } public AsyncReply GetPassword(string username, string domain) { return new AsyncReply(DC.ToBytes(users[username].Password)); } public AsyncReply GetToken(ulong tokenIndex, string domain) { return new AsyncReply(DC.ToBytes(tokens[tokenIndex].Token)); } public AsyncReply Login(Session session) { return new AsyncReply(true); } public AsyncReply Logout(Session session) { return new AsyncReply(true); } public AsyncReply TokenExists(ulong tokenIndex, string domain) { if (!tokens.ContainsKey(tokenIndex)) return new AsyncReply(null); else return new AsyncReply(tokens[tokenIndex].Username); } public AsyncReply UserExists(string username, string domain) { if (!users.ContainsKey(username)) return new AsyncReply(null); else return new AsyncReply(username); } } }