mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 21:48:12 -05:00
177 lines
6.3 KiB
C#
177 lines
6.3 KiB
C#
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>();
|
|
}
|