XIVChat/XIVChatCommon/SecretMessage.cs

60 lines
2.3 KiB
C#

using Sodium;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using XIVChatCommon.Message;
namespace XIVChatCommon {
public static class SecretMessage {
private const uint MaxMessageLen = 128_000;
public static async Task<byte[]> ReadSecretMessage(Stream s, byte[] key, CancellationToken token = default) {
var read = 0;
byte[] header = new byte[4 + 24];
while (read < header.Length) {
read += await s.ReadAsync(header, read, header.Length - read, token);
}
var length = BitConverter.ToUInt32(header, 0);
byte[] nonce = header.Skip(4).ToArray();
if (length > MaxMessageLen) {
throw new ArgumentOutOfRangeException($"Encrypted message specified a size of {length}, which is greater than the limit of {MaxMessageLen}");
}
byte[] ciphertext = new byte[length];
read = 0;
while (read < ciphertext.Length) {
read += await s.ReadAsync(ciphertext, read, ciphertext.Length - read, token);
}
return SecretBox.Open(ciphertext, nonce, key);
}
public static async Task SendSecretMessage(Stream s, byte[] key, byte[] message, CancellationToken token = default) {
byte[] nonce = SecretBox.GenerateNonce();
byte[] ciphertext = SecretBox.Create(message, nonce, key);
byte[] len = BitConverter.GetBytes((uint)ciphertext.Length);
if (ciphertext.Length > MaxMessageLen) {
throw new ArgumentOutOfRangeException($"Encrypted message would be {len} bytes long, which is larger than the limit of {MaxMessageLen}");
}
await s.WriteAsync(len, 0, len.Length, token);
await s.WriteAsync(nonce, 0, nonce.Length, token);
await s.WriteAsync(ciphertext, 0, ciphertext.Length, token);
await s.FlushAsync(token);
}
public static async Task SendSecretMessage(Stream s, byte[] key, Encodable message, CancellationToken token = default) {
await SendSecretMessage(s, key, message.Encode(), token);
}
public const int MacSize = 16;
public const int NonceSize = 24;
}
}