using Bit.Core.Entities; using Bitwarden.OPAQUE; namespace Bit.Core.Auth.Services; #nullable enable public class OpaqueKeyExchangeService : IOpaqueKeyExchangeService { private readonly BitwardenOpaqueServer _bitwardenOpaque; public OpaqueKeyExchangeService( ) { _bitwardenOpaque = new BitwardenOpaqueServer(); } public async Task<(Guid, byte[])> StartRegistration(byte[] request, User user, CipherConfiguration cipherConfiguration) { return await Task.Run(() => { var registrationResponse = _bitwardenOpaque.StartRegistration(cipherConfiguration, null, request, user.Id.ToString()); var sessionId = Guid.NewGuid(); SessionStore.RegisterSessions.Add(sessionId, new RegisterSession() { sessionId = sessionId, serverSetup = registrationResponse.serverSetup, cipherConfiguration = cipherConfiguration, userId = user.Id }); return (sessionId, registrationResponse.registrationResponse); }); } public async void FinishRegistration(Guid sessionId, byte[] registrationUpload, User user) { await Task.Run(() => { var cipherConfiguration = SessionStore.RegisterSessions[sessionId].cipherConfiguration; try { var registrationFinish = _bitwardenOpaque.FinishRegistration(cipherConfiguration, registrationUpload); SessionStore.RegisterSessions[sessionId].serverRegistration = registrationFinish.serverRegistration; // todo move to changepassword SetActive(sessionId, user); } catch (Exception e) { SessionStore.RegisterSessions.Remove(sessionId); throw new Exception(e.Message); } }); } public async Task<(Guid, byte[])> StartLogin(byte[] request, string email) { return await Task.Run(() => { var credential = PersistentStore.Credentials.First(x => x.Value.email == email); if (credential.Value == null) { // generate fake credential throw new InvalidOperationException("User not found"); } var cipherConfiguration = credential.Value.cipherConfiguration; var serverSetup = credential.Value.serverSetup; var serverRegistration = credential.Value.serverRegistration; var loginResponse = _bitwardenOpaque.StartLogin(cipherConfiguration, serverSetup, serverRegistration, request, credential.Key.ToString()); var sessionId = Guid.NewGuid(); SessionStore.LoginSessions.Add(sessionId, new LoginSession() { userId = credential.Key, loginState = loginResponse.state }); return (sessionId, loginResponse.credentialResponse); }); } public async Task<bool> FinishLogin(Guid sessionId, byte[] credentialFinalization) { return await Task.Run(() => { if (!SessionStore.LoginSessions.ContainsKey(sessionId)) { throw new InvalidOperationException("Session not found"); } var credential = PersistentStore.Credentials[SessionStore.LoginSessions[sessionId].userId]; if (credential == null) { throw new InvalidOperationException("User not found"); } var loginState = SessionStore.LoginSessions[sessionId].loginState; var cipherConfiguration = credential.cipherConfiguration; try { var success = _bitwardenOpaque.FinishLogin(cipherConfiguration, loginState, credentialFinalization); SessionStore.LoginSessions.Remove(sessionId); return true; } catch (Exception e) { // print Console.WriteLine(e.Message); SessionStore.LoginSessions.Remove(sessionId); return false; } }); } public async void SetActive(Guid sessionId, User user) { await Task.Run(() => { var session = SessionStore.RegisterSessions[sessionId]; if (session.userId != user.Id) { throw new InvalidOperationException("Session does not belong to user"); } if (session.serverRegistration == null) { throw new InvalidOperationException("Session did not complete registration"); } SessionStore.RegisterSessions.Remove(sessionId); // to be copied to the persistent DB var cipherConfiguration = session.cipherConfiguration; var serverRegistration = session.serverRegistration; var serverSetup = session.serverSetup; if (PersistentStore.Credentials.ContainsKey(user.Id)) { PersistentStore.Credentials.Remove(user.Id); } PersistentStore.Credentials.Add(user.Id, new Credential() { serverRegistration = serverRegistration, serverSetup = serverSetup, cipherConfiguration = cipherConfiguration, email = user.Email }); }); } public async void Unenroll(User user) { await Task.Run(() => { PersistentStore.Credentials.Remove(user.Id); }); } } public class RegisterSession { public required Guid sessionId { get; set; } public required byte[] serverSetup { get; set; } public required CipherConfiguration cipherConfiguration { get; set; } public required Guid userId { get; set; } public byte[]? serverRegistration { get; set; } } public class LoginSession { public required Guid userId { get; set; } public required byte[] loginState { get; set; } } public class SessionStore() { public static Dictionary<Guid, RegisterSession> RegisterSessions = new Dictionary<Guid, RegisterSession>(); public static Dictionary<Guid, LoginSession> LoginSessions = new Dictionary<Guid, LoginSession>(); } public class Credential { public required byte[] serverRegistration { get; set; } public required byte[] serverSetup { get; set; } public required CipherConfiguration cipherConfiguration { get; set; } public required string email { get; set; } } public class PersistentStore() { public static Dictionary<Guid, Credential> Credentials = new Dictionary<Guid, Credential>(); }