mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
- Added EncryptionSalt and WrappedRoomKey properties to Channel model. - Introduced RoomCrypto class for client-side encryption and decryption. - Updated ChannelService to handle encrypted channels, including creation and rekeying. - Modified ChannelsController to expose crypto metadata and rekey functionality. - Enhanced IrcCommandHandler to block joining encrypted channels over IRC. - Updated database schema with migration for new encryption fields. - Refactored file validation and image processing services to accommodate encrypted channels. - Added unit tests for RoomCrypto functionality and updated existing tests for channel services.
100 lines
3.7 KiB
C#
100 lines
3.7 KiB
C#
using EchoHub.Core.Security;
|
|
using Xunit;
|
|
|
|
namespace EchoHub.Tests;
|
|
|
|
public class RoomCryptoTests
|
|
{
|
|
[Fact]
|
|
public void EncryptText_RoundTrips()
|
|
{
|
|
var key = RoomCrypto.GenerateRoomKey();
|
|
var ciphertext = RoomCrypto.EncryptText("hello secret room", key);
|
|
|
|
Assert.StartsWith("$RC1$", ciphertext);
|
|
Assert.True(RoomCrypto.TryDecryptText(ciphertext, key, out var plaintext));
|
|
Assert.Equal("hello secret room", plaintext);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryDecryptText_WrongKey_ReturnsFalse()
|
|
{
|
|
var ciphertext = RoomCrypto.EncryptText("hello", RoomCrypto.GenerateRoomKey());
|
|
|
|
Assert.False(RoomCrypto.TryDecryptText(ciphertext, RoomCrypto.GenerateRoomKey(), out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryDecryptText_PlainText_ReturnsFalse()
|
|
{
|
|
Assert.False(RoomCrypto.TryDecryptText("just a normal message", RoomCrypto.GenerateRoomKey(), out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void EncryptBytes_RoundTrips()
|
|
{
|
|
var key = RoomCrypto.GenerateRoomKey();
|
|
var payload = new byte[4096];
|
|
Random.Shared.NextBytes(payload);
|
|
|
|
var blob = RoomCrypto.EncryptBytes(payload, key);
|
|
var decrypted = RoomCrypto.DecryptBytes(blob, key);
|
|
|
|
Assert.Equal(payload, decrypted);
|
|
}
|
|
|
|
[Fact]
|
|
public void DeriveKeys_IsDeterministic_AndSaltSensitive()
|
|
{
|
|
var salt = RoomCrypto.GenerateSalt();
|
|
var a = RoomCrypto.DeriveKeys("correct horse battery staple", salt);
|
|
var b = RoomCrypto.DeriveKeys("correct horse battery staple", salt);
|
|
var other = RoomCrypto.DeriveKeys("correct horse battery staple", RoomCrypto.GenerateSalt());
|
|
|
|
Assert.Equal(a.AuthKeyHex, b.AuthKeyHex);
|
|
Assert.Equal(a.KeyEncryptionKey, b.KeyEncryptionKey);
|
|
Assert.NotEqual(a.AuthKeyHex, other.AuthKeyHex);
|
|
Assert.NotEqual(a.AuthKeyHex, Convert.ToHexString(a.KeyEncryptionKey).ToLowerInvariant());
|
|
}
|
|
|
|
[Fact]
|
|
public void WrapRoomKey_UnwrapsWithSameKek_FailsWithWrongKek()
|
|
{
|
|
var salt = RoomCrypto.GenerateSalt();
|
|
var keys = RoomCrypto.DeriveKeys("passphrase-1", salt);
|
|
var wrongKeys = RoomCrypto.DeriveKeys("passphrase-2", salt);
|
|
var roomKey = RoomCrypto.GenerateRoomKey();
|
|
|
|
var wrapped = RoomCrypto.WrapRoomKey(roomKey, keys.KeyEncryptionKey);
|
|
|
|
Assert.True(RoomCrypto.TryUnwrapRoomKey(wrapped, keys.KeyEncryptionKey, out var unwrapped));
|
|
Assert.Equal(roomKey, unwrapped);
|
|
Assert.False(RoomCrypto.TryUnwrapRoomKey(wrapped, wrongKeys.KeyEncryptionKey, out _));
|
|
}
|
|
|
|
[Fact]
|
|
public void Rewrap_PreservesRoomKey_AcrossPassphraseChange()
|
|
{
|
|
// Simulates a passphrase change: unwrap with old KEK, wrap with new KEK.
|
|
var roomKey = RoomCrypto.GenerateRoomKey();
|
|
|
|
var oldSalt = RoomCrypto.GenerateSalt();
|
|
var oldKeys = RoomCrypto.DeriveKeys("old-passphrase", oldSalt);
|
|
var wrappedOld = RoomCrypto.WrapRoomKey(roomKey, oldKeys.KeyEncryptionKey);
|
|
|
|
Assert.True(RoomCrypto.TryUnwrapRoomKey(wrappedOld, oldKeys.KeyEncryptionKey, out var recovered));
|
|
|
|
var newSalt = RoomCrypto.GenerateSalt();
|
|
var newKeys = RoomCrypto.DeriveKeys("new-passphrase", newSalt);
|
|
var wrappedNew = RoomCrypto.WrapRoomKey(recovered, newKeys.KeyEncryptionKey);
|
|
|
|
Assert.True(RoomCrypto.TryUnwrapRoomKey(wrappedNew, newKeys.KeyEncryptionKey, out var final));
|
|
Assert.Equal(roomKey, final);
|
|
|
|
// Old messages encrypted before the change still decrypt with the unwrapped key
|
|
var oldMessage = RoomCrypto.EncryptText("written before rekey", roomKey);
|
|
Assert.True(RoomCrypto.TryDecryptText(oldMessage, final, out var plaintext));
|
|
Assert.Equal("written before rekey", plaintext);
|
|
}
|
|
}
|