Encryption Sent, Encrpytion Decoded, Offer Sent, Offer Recieved, JS -> C# / C# -> JS Broke (some disconnect here) SendRtcSignalToJsAsync
This commit is contained in:
@@ -189,6 +189,20 @@ public partial class MainPage : ContentPage
|
|||||||
},
|
},
|
||||||
privateKey
|
privateKey
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var rtcSignal = JsonSerializer.Deserialize<RtcSignalMessage>(decryptedJson);
|
||||||
|
|
||||||
|
if (rtcSignal is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(rtcSignal.To) &&
|
||||||
|
!string.Equals(rtcSignal.To, _username, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
SafeSendRawToWebView($"Ignoring RTC signal meant for {rtcSignal.To}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeSendRawToWebView("Received encrypted RTC signal: " + decryptedJson);
|
||||||
|
|
||||||
MainThread.BeginInvokeOnMainThread(async () =>
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
@@ -438,20 +452,20 @@ public partial class MainPage : ContentPage
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region RTC Functions
|
#region RTC Functions
|
||||||
public async Task<bool> JoinRtcChannel()
|
public async Task JoinRtcChannel()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_currentChannelId))
|
if (string.IsNullOrWhiteSpace(_currentChannelId))
|
||||||
return false;
|
return; //false;
|
||||||
|
|
||||||
_wsc.Send($"RTC_JOIN_CHANNEL|{_username}|{_currentChannelId}");
|
_wsc.Send($"RTC_JOIN_CHANNEL|{_username}|{_currentChannelId}");
|
||||||
|
|
||||||
// SafeSendRawToWebView($"Attempting to join RTC Channel {_currentChannelName} | {_currentChannelId} ");
|
// SafeSendRawToWebView($"Attempting to join RTC Channel {_currentChannelName} | {_currentChannelId} ");
|
||||||
|
|
||||||
bool active = await ServerAPI.GetIsChannelActiveAsync(_currentChannelId);
|
//bool active = await ServerAPI.GetIsChannelActiveAsync(_currentChannelId);
|
||||||
|
|
||||||
SafeSendRawToWebView($"Rtc Channel {_currentChannelName} | {_currentChannelId} is active: {active}");
|
//SafeSendRawToWebView($"Rtc Channel {_currentChannelName} | {_currentChannelId} is active: {active}");
|
||||||
|
|
||||||
return active;
|
return; //active;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LeaveRtcChannel()
|
public void LeaveRtcChannel()
|
||||||
@@ -566,10 +580,36 @@ public partial class MainPage : ContentPage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SendRtcSignalToJsAsync(string rawJson)
|
private Task SendRtcSignalToJsAsync(string rawJson)
|
||||||
{
|
{
|
||||||
var jsArg = JsonSerializer.Serialize(rawJson);
|
MainThread.BeginInvokeOnMainThread(async () =>
|
||||||
await hybridWebView.EvaluateJavaScriptAsync($"window.handleRtcSignal({jsArg})");
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SafeSendRawToWebView("Dispatching RTC signal to JS");
|
||||||
|
|
||||||
|
var jsArg = JsonSerializer.Serialize(rawJson);
|
||||||
|
|
||||||
|
await hybridWebView.EvaluateJavaScriptAsync($@"
|
||||||
|
window.HybridWebView.SendRawMessage('JS dispatch wrapper hit');
|
||||||
|
const fn = window.handleRtcSignal || window.dispatchRtcSignal;
|
||||||
|
if (!fn) {{
|
||||||
|
window.HybridWebView.SendRawMessage('No RTC signal handler found on window');
|
||||||
|
}} else {{
|
||||||
|
window.HybridWebView.SendRawMessage('Calling RTC signal handler');
|
||||||
|
fn({jsArg});
|
||||||
|
}}
|
||||||
|
");
|
||||||
|
|
||||||
|
SafeSendRawToWebView("RTC signal dispatched to JS");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SafeSendRawToWebView("SendRtcSignalToJsAsync failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
} //Remove?
|
} //Remove?
|
||||||
|
|
||||||
private async Task PushRtcContextToJsAsync()
|
private async Task PushRtcContextToJsAsync()
|
||||||
@@ -585,41 +625,63 @@ public partial class MainPage : ContentPage
|
|||||||
|
|
||||||
public void SendRtcSignal(string json)
|
public void SendRtcSignal(string json)
|
||||||
{
|
{
|
||||||
|
SafeSendRawToWebView("SendRtcSignal entered: " + json);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_serverPublicKey))
|
if (string.IsNullOrWhiteSpace(_serverPublicKey))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Server public key not loaded yet.");
|
SafeSendRawToWebView("SendRtcSignal failed: server public key not loaded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RtcSignalMessage? rtcSignal;
|
RtcSignalMessage? rtcSignal;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rtcSignal = JsonSerializer.Deserialize<RtcSignalMessage>(json);
|
rtcSignal = JsonSerializer.Deserialize<RtcSignalMessage>(json);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Failed to parse RTC signal from JS: {ex.Message}");
|
SafeSendRawToWebView("SendRtcSignal failed to parse RTC signal: " + ex.Message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtcSignal is null)
|
if (rtcSignal is null)
|
||||||
return;
|
|
||||||
|
|
||||||
var encrypted = E2EeHelper.EncryptForRecipient(json, _serverPublicKey);
|
|
||||||
|
|
||||||
var payload = new SocketRtcSignalMessage
|
|
||||||
{
|
{
|
||||||
Type = SignalType.EncryptedSignal,
|
SafeSendRawToWebView("SendRtcSignal failed: rtcSignal was null.");
|
||||||
SenderUsername = _username,
|
return;
|
||||||
ChannelId = rtcSignal.ChannelId,
|
}
|
||||||
CipherText = encrypted.CipherText,
|
|
||||||
Nonce = encrypted.Nonce,
|
|
||||||
Tag = encrypted.Tag,
|
|
||||||
EncryptedKey = encrypted.EncryptedKey
|
|
||||||
};
|
|
||||||
|
|
||||||
_wsc.Send(JsonSerializer.Serialize(payload));
|
if (string.IsNullOrWhiteSpace(rtcSignal.ChannelId))
|
||||||
Console.WriteLine($"[{_username}] sent RTC signal: {rtcSignal.Type} -> {rtcSignal.ChannelId}");
|
{
|
||||||
|
SafeSendRawToWebView("SendRtcSignal failed: channelId was empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encrypted = E2EeHelper.EncryptForRecipient(json, _serverPublicKey);
|
||||||
|
|
||||||
|
var payload = new SocketRtcSignalMessage
|
||||||
|
{
|
||||||
|
Type = SignalType.EncryptedSignal,
|
||||||
|
SenderUsername = _username,
|
||||||
|
ChannelId = rtcSignal.ChannelId,
|
||||||
|
CipherText = encrypted.CipherText,
|
||||||
|
Nonce = encrypted.Nonce,
|
||||||
|
Tag = encrypted.Tag,
|
||||||
|
EncryptedKey = encrypted.EncryptedKey
|
||||||
|
};
|
||||||
|
|
||||||
|
var socketJson = JsonSerializer.Serialize(payload);
|
||||||
|
_wsc.Send(socketJson);
|
||||||
|
|
||||||
|
SafeSendRawToWebView($"SendRtcSignal sent: {rtcSignal.Type} -> {rtcSignal.ChannelId}");
|
||||||
|
Console.WriteLine($"[{_username}] sent RTC signal: {rtcSignal.Type} -> {rtcSignal.ChannelId}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SafeSendRawToWebView("SendRtcSignal websocket/encrypt failed: " + ex.Message);
|
||||||
|
}
|
||||||
} //Remove?
|
} //Remove?
|
||||||
|
|
||||||
public async Task<string> GetRtcParticipants()
|
public async Task<string> GetRtcParticipants()
|
||||||
|
|||||||
@@ -164,58 +164,43 @@ async function joinChannelCall() {
|
|||||||
LogMessage("Current username: " + currentUsername);
|
LogMessage("Current username: " + currentUsername);
|
||||||
LogMessage("Current channel: " + currentChannelId);
|
LogMessage("Current channel: " + currentChannelId);
|
||||||
|
|
||||||
const isActive = await window.HybridWebView.InvokeDotNet("JoinRtcChannel");
|
await window.HybridWebView.InvokeDotNet("JoinRtcChannel");
|
||||||
const peerConnection = await ensurePeerConnectionForUser(currentUsername);
|
|
||||||
await ensureLocalMedia();
|
await ensureLocalMedia();
|
||||||
|
|
||||||
if (isActive) {
|
const rawParticipants = await window.HybridWebView.InvokeDotNet("GetRtcParticipants");
|
||||||
|
const participants = typeof rawParticipants === "string"
|
||||||
const rawJson = await window.HybridWebView.InvokeDotNet("GetRtcOffer");
|
? JSON.parse(rawParticipants)
|
||||||
const offer = typeof rawJson === "string" ? JSON.parse(rawJson) : rawJson;
|
: rawParticipants;
|
||||||
await peerConnection.setRemoteDescription(offer);
|
|
||||||
const answer = await peerConnection.createAnswer();
|
|
||||||
await peerConnection.setRemoteDescription(answer);
|
|
||||||
await window.HybridWebView.InvokeDotNet("WriteRtcAnswer", [JSON.stringify(answer)])
|
|
||||||
|
|
||||||
const rawParticipants = await window.HybridWebView.InvokeDotNet("GetRtcParticipants");
|
LogMessage("Participants: " + JSON.stringify(participants));
|
||||||
const participants = typeof rawParticipants === "string" ? JSON.parse(rawParticipants) : rawParticipants;
|
|
||||||
|
|
||||||
LogMessage("Participants: " + JSON.stringify(participants)); // TODO: Remove
|
const otherUsers = participants.filter(username => username !== currentUsername);
|
||||||
|
|
||||||
for (const username of participants) {
|
if (otherUsers.length === 0) {
|
||||||
if (username === currentUsername) continue;
|
LogMessage("Joined call as first participant. Waiting for others...");
|
||||||
|
return;
|
||||||
const pc = await ensurePeerConnectionForUser(username);
|
|
||||||
|
|
||||||
const offer = await pc.createOffer();
|
|
||||||
await pc.setLocalDescription(offer);
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
type: "rtc_offer",
|
|
||||||
from: currentUsername,
|
|
||||||
to: username,
|
|
||||||
channelId: currentChannelId,
|
|
||||||
sdp: offer.sdp
|
|
||||||
};
|
|
||||||
|
|
||||||
await window.HybridWebView.InvokeDotNet("SendRtcSignal", [JSON.stringify(payload)]);
|
|
||||||
LogMessage(`Sent offer to ${username}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
for (const username of otherUsers) {
|
||||||
try {
|
const pc = await ensurePeerConnectionForUser(username);
|
||||||
LogMessage(currentUsername + " attempted to join inactive channel. Making new call.")
|
|
||||||
const offer = await peerConnection.createOffer();
|
const offer = await pc.createOffer();
|
||||||
await peerConnection.setLocalDescription(offer);
|
await pc.setLocalDescription(offer);
|
||||||
await window.HybridWebView.InvokeDotNet("WriteRtcOffer", [JSON.stringify(offer)]);
|
|
||||||
LogMessage(`Joining call with media offer: ${JSON.stringify(offer)}`);
|
const payload = {
|
||||||
}
|
type: "rtc_offer",
|
||||||
catch (error) {
|
from: currentUsername,
|
||||||
LogMessage(error)
|
to: username,
|
||||||
}
|
channelId: currentChannelId,
|
||||||
|
sdp: offer.sdp,
|
||||||
|
isInitiator: true
|
||||||
|
};
|
||||||
|
|
||||||
|
await window.HybridWebView.InvokeDotNet("SendRtcSignal", [JSON.stringify(payload)]);
|
||||||
|
LogMessage(`Sent offer to ${username}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function channelCallJoin(activeCall)
|
async function channelCallJoin(activeCall)
|
||||||
{
|
{
|
||||||
// LogMessage("Active call: " + activeCall);
|
// LogMessage("Active call: " + activeCall);
|
||||||
@@ -337,6 +322,11 @@ async function handleRtcSignal(rawJson) {
|
|||||||
|
|
||||||
if (!msg.from || msg.from === currentUsername) return;
|
if (!msg.from || msg.from === currentUsername) return;
|
||||||
|
|
||||||
|
if (msg.to && msg.to !== currentUsername) {
|
||||||
|
LogMessage(`Ignoring signal meant for ${msg.to}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const pc = await ensurePeerConnectionForUser(msg.from);
|
const pc = await ensurePeerConnectionForUser(msg.from);
|
||||||
|
|
||||||
if (msg.type === "rtc_offer") {
|
if (msg.type === "rtc_offer") {
|
||||||
@@ -390,6 +380,8 @@ async function handleRtcSignal(rawJson) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.handleRtcSignal = handleRtcSignal;
|
||||||
|
|
||||||
async function loadDevices() {
|
async function loadDevices() {
|
||||||
try {
|
try {
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
@@ -510,8 +502,6 @@ function removeRemoteTile(username) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.handleRtcSignal = handleRtcSignal;
|
|
||||||
|
|
||||||
window.addEventListener("HybridWebViewMessageReceived", function (e) {
|
window.addEventListener("HybridWebViewMessageReceived", function (e) {
|
||||||
LogMessage("Raw message: " + e.detail.message);
|
LogMessage("Raw message: " + e.detail.message);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -122,6 +122,27 @@ public class ChatSocketBehavior : WebSocketBehavior
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string plainText;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
plainText = E2EeHelper.DecryptForRecipient(
|
||||||
|
new EncryptedPayload
|
||||||
|
{
|
||||||
|
CipherText = clientPayload.CipherText,
|
||||||
|
Nonce = clientPayload.Nonce,
|
||||||
|
Tag = clientPayload.Tag,
|
||||||
|
EncryptedKey = clientPayload.EncryptedKey
|
||||||
|
},
|
||||||
|
ServerPrivateKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to decrypt RTC signal: {ex.Message}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var sessionIds = RtcChannelPresenceService.GetSessionsInChannel(clientPayload.ChannelId);
|
var sessionIds = RtcChannelPresenceService.GetSessionsInChannel(clientPayload.ChannelId);
|
||||||
|
|
||||||
foreach (var sessionId in sessionIds)
|
foreach (var sessionId in sessionIds)
|
||||||
@@ -129,7 +150,28 @@ public class ChatSocketBehavior : WebSocketBehavior
|
|||||||
if (sessionId == ID)
|
if (sessionId == ID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Sessions.SendTo(JsonSerializer.Serialize(clientPayload), sessionId);
|
var username = RtcChannelPresenceService.GetUsernameForSession(sessionId);
|
||||||
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var clientKey = GetClientPublicKeyByUsernameSync(username);
|
||||||
|
if (clientKey is null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var encrypted = E2EeHelper.EncryptForRecipient(plainText, clientKey.PublicKey);
|
||||||
|
|
||||||
|
var outbound = new SocketRtcSignalMessage
|
||||||
|
{
|
||||||
|
Type = SignalType.EncryptedSignal,
|
||||||
|
SenderUsername = clientPayload.SenderUsername,
|
||||||
|
ChannelId = clientPayload.ChannelId,
|
||||||
|
CipherText = encrypted.CipherText,
|
||||||
|
Nonce = encrypted.Nonce,
|
||||||
|
Tag = encrypted.Tag,
|
||||||
|
EncryptedKey = encrypted.EncryptedKey
|
||||||
|
};
|
||||||
|
|
||||||
|
Sessions.SendTo(JsonSerializer.Serialize(outbound), sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"Forwarded encrypted RTC signal from {clientPayload.SenderUsername} to channel {clientPayload.ChannelId}");
|
Console.WriteLine($"Forwarded encrypted RTC signal from {clientPayload.SenderUsername} to channel {clientPayload.ChannelId}");
|
||||||
|
|||||||
@@ -57,4 +57,11 @@ public static class RtcChannelPresenceService
|
|||||||
return SessionToChannel.TryGetValue(sessionId, out var currentChannel) &&
|
return SessionToChannel.TryGetValue(sessionId, out var currentChannel) &&
|
||||||
string.Equals(currentChannel, channelId, StringComparison.Ordinal);
|
string.Equals(currentChannel, channelId, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string? GetUsernameForSession(string sessionId)
|
||||||
|
{
|
||||||
|
return SessionToUsername.TryGetValue(sessionId, out var username)
|
||||||
|
? username
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ public enum ChannelType
|
|||||||
public sealed class RtcSignalMessage
|
public sealed class RtcSignalMessage
|
||||||
{
|
{
|
||||||
[JsonPropertyName("type")]
|
[JsonPropertyName("type")]
|
||||||
public SignalType Type { get; set; }
|
public string Type { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("from")]
|
[JsonPropertyName("from")]
|
||||||
public string From { get; set; } = string.Empty;
|
public string From { get; set; } = string.Empty;
|
||||||
@@ -59,7 +59,7 @@ public sealed class RtcSignalMessage
|
|||||||
|
|
||||||
[JsonPropertyName("sdpMLineIndex")]
|
[JsonPropertyName("sdpMLineIndex")]
|
||||||
public int? SdpMLineIndex { get; set; }
|
public int? SdpMLineIndex { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("isInitiator")]
|
[JsonPropertyName("isInitiator")]
|
||||||
public bool IsInitiator { get; set; }
|
public bool IsInitiator { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user