SEBWIN-608: Implemented basic mechanism for app signature key exchange.

This commit is contained in:
Damian Büchel 2023-02-24 15:48:54 +01:00
parent 718a4550e9
commit ae3755df84
4 changed files with 119 additions and 28 deletions

View file

@ -51,6 +51,10 @@ namespace SafeExamBrowser.Runtime.Operations
if (Context.Next.Settings.SessionMode == SessionMode.Server) if (Context.Next.Settings.SessionMode == SessionMode.Server)
{ {
var exam = default(Exam);
var exams = default(IEnumerable<Exam>);
var uri = default(Uri);
logger.Info("Initializing server..."); logger.Info("Initializing server...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer);
@ -60,22 +64,27 @@ namespace SafeExamBrowser.Runtime.Operations
if (success) if (success)
{ {
(abort, fallback, success) = TryPerformWithFallback(() => server.GetAvailableExams(Context.Next.Settings.Server.ExamId), out var exams); (abort, fallback, success) = TryPerformWithFallback(() => server.GetAvailableExams(Context.Next.Settings.Server.ExamId), out exams);
}
if (success) if (success)
{ {
success = TrySelectExam(exams, out var exam); success = TrySelectExam(exams, out exam);
}
if (success) if (success)
{ {
(abort, fallback, success) = TryPerformWithFallback(() => server.GetConfigurationFor(exam), out var uri); (abort, fallback, success) = TryPerformWithFallback(() => server.GetConfigurationFor(exam), out uri);
}
if (success) if (success)
{ {
result = TryLoadServerSettings(exam, uri); result = TryLoadServerSettings(exam, uri);
} }
}
} if (success && result == OperationResult.Success)
{
(abort, fallback, success) = TryPerformWithFallback(() => server.SendSelectedExam(exam));
} }
if (abort) if (abort)
@ -97,25 +106,22 @@ namespace SafeExamBrowser.Runtime.Operations
public override OperationResult Repeat() public override OperationResult Repeat()
{ {
if (Context.Current.Settings.SessionMode == SessionMode.Server) var result = OperationResult.Success;
{
if (Context.Next.Settings.SessionMode == SessionMode.Server)
{
ShowReconfigurationError();
return OperationResult.Aborted; if (Context.Current.Settings.SessionMode == SessionMode.Server && Context.Next.Settings.SessionMode == SessionMode.Server)
} {
else result = AbortServerReconfiguration();
{ }
return Revert(); else if (Context.Current.Settings.SessionMode == SessionMode.Server)
} {
result = Revert();
} }
else if (Context.Next.Settings.SessionMode == SessionMode.Server) else if (Context.Next.Settings.SessionMode == SessionMode.Server)
{ {
return Perform(); result = Perform();
} }
return OperationResult.Success; return result;
} }
public override OperationResult Revert() public override OperationResult Revert()
@ -254,7 +260,7 @@ namespace SafeExamBrowser.Runtime.Operations
return success; return success;
} }
private void ShowReconfigurationError() private OperationResult AbortServerReconfiguration()
{ {
var args = new MessageEventArgs var args = new MessageEventArgs
{ {
@ -263,9 +269,11 @@ namespace SafeExamBrowser.Runtime.Operations
Message = TextKey.MessageBox_ServerReconfigurationWarning, Message = TextKey.MessageBox_ServerReconfigurationWarning,
Title = TextKey.MessageBox_ServerReconfigurationWarningTitle Title = TextKey.MessageBox_ServerReconfigurationWarningTitle
}; };
logger.Warn("Server reconfiguration requested but is not allowed.");
logger.Warn("Server reconfiguration is currently not supported, aborting...");
ActionRequired?.Invoke(args); ActionRequired?.Invoke(args);
return OperationResult.Aborted;
} }
} }
} }

View file

@ -99,6 +99,11 @@ namespace SafeExamBrowser.Server.Contracts
/// </summary> /// </summary>
ServerResponse LowerHand(); ServerResponse LowerHand();
/// <summary>
/// Sends the selected exam to the server.
/// </summary>
ServerResponse SendSelectedExam(Exam exam);
/// <summary> /// <summary>
/// Sends the given user session identifier of a LMS and thus establishes a connection with the server. /// Sends the given user session identifier of a LMS and thus establishes a connection with the server.
/// </summary> /// </summary>

View file

@ -105,6 +105,27 @@ namespace SafeExamBrowser.Server
return success; return success;
} }
internal bool TryParseAppSignatureKeySalt(HttpResponseMessage response, out string salt)
{
salt = default;
try
{
var hasHeader = response.Headers.TryGetValues("SEBExamSalt", out var values);
if (hasHeader)
{
salt = values.First();
}
}
catch (Exception e)
{
logger.Error("Failed to parse app signature key salt!", e);
}
return salt != default;
}
internal bool TryParseConnectionToken(HttpResponseMessage response, out string connectionToken) internal bool TryParseConnectionToken(HttpResponseMessage response, out string connectionToken)
{ {
connectionToken = default; connectionToken = default;

View file

@ -348,6 +348,38 @@ namespace SafeExamBrowser.Server
return new ServerResponse(success, response.ToLogString()); return new ServerResponse(success, response.ToLogString());
} }
public ServerResponse SendSelectedExam(Exam exam)
{
var authorization = ("Authorization", $"Bearer {oauth2Token}");
var content = $"examId={exam.Id}";
var contentType = "application/x-www-form-urlencoded";
var token = ("SEBConnectionToken", connectionToken);
var success = TryExecute(new HttpMethod("PATCH"), api.HandshakeEndpoint, out var response, content, contentType, authorization, token);
var message = response.ToLogString();
if (success)
{
logger.Info("Successfully sent selected exam.");
}
else
{
logger.Error("Failed to send selected exam!");
}
if (parser.TryParseAppSignatureKeySalt(response, out var salt))
{
logger.Info("App signature key salt detected, performing key exchange...");
success = TrySendAppSignatureKey(out message);
}
else
{
logger.Info("No app signature key salt detected, skipping key exchange.");
}
return new ServerResponse(success, message);
}
public ServerResponse SendSessionIdentifier(string identifier) public ServerResponse SendSessionIdentifier(string identifier)
{ {
var authorization = ("Authorization", $"Bearer {oauth2Token}"); var authorization = ("Authorization", $"Bearer {oauth2Token}");
@ -614,6 +646,31 @@ namespace SafeExamBrowser.Server
return success; return success;
} }
private bool TrySendAppSignatureKey(out string message)
{
// TODO:
// keyGenerator.CalculateAppSignatureKey(configurationKey, server.AppSignatureKeySalt)
var authorization = ("Authorization", $"Bearer {oauth2Token}");
var content = $"seb_signature_key={"WINDOWS-TEST-ASK-1234"}";
var contentType = "application/x-www-form-urlencoded";
var token = ("SEBConnectionToken", connectionToken);
var success = TryExecute(new HttpMethod("PATCH"), api.HandshakeEndpoint, out var response, content, contentType, authorization, token);
message = response.ToLogString();
if (success)
{
logger.Info("Successfully sent app signature key.");
}
else
{
logger.Error("Failed to send app signature key!");
}
return success;
}
private bool TryExecute( private bool TryExecute(
HttpMethod method, HttpMethod method,
string url, string url,