Files
natsdotnet/tests/NATS.Server.JetStream.Tests/FileStoreEncryptionTests.cs
Joseph Doherty 78b4bc2486 refactor: extract NATS.Server.JetStream.Tests project
Move 225 JetStream-related test files from NATS.Server.Tests into a
dedicated NATS.Server.JetStream.Tests project. This includes root-level
JetStream*.cs files, storage test files (FileStore, MemStore,
StreamStoreContract), and the full JetStream/ subfolder tree (Api,
Cluster, Consumers, MirrorSource, Snapshots, Storage, Streams).

Updated all namespaces, added InternalsVisibleTo, registered in the
solution file, and added the JETSTREAM_INTEGRATION_MATRIX define.
2026-03-12 15:58:10 -04:00

105 lines
3.5 KiB
C#

// Go: TestFileStoreEncryption server/filestore_test.go
// Reference: golang/nats-server/server/filestore.go:816-907 (genEncryptionKeys, recoverAEK, setupAEK)
// Tests that block files are encrypted at rest and can be recovered with the same key.
using System.Security.Cryptography;
using NATS.Server.JetStream.Storage;
namespace NATS.Server.JetStream.Tests;
public class FileStoreEncryptionTests
{
[Fact]
public async Task Encrypted_block_round_trips_message()
{
// Go: TestFileStoreEncryption server/filestore_test.go
var dir = Directory.CreateTempSubdirectory();
var key = new byte[32];
RandomNumberGenerator.Fill(key);
await using (var store = new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.ChaCha,
EncryptionKey = key,
}))
{
await store.AppendAsync("test.subj", "hello encrypted"u8.ToArray(), default);
}
// Raw block file should NOT contain plaintext
var blkFiles = Directory.GetFiles(dir.FullName, "*.blk");
blkFiles.ShouldNotBeEmpty();
var raw = File.ReadAllBytes(blkFiles[0]);
System.Text.Encoding.UTF8.GetString(raw).ShouldNotContain("hello encrypted");
// Recover with same key should return plaintext
await using var recovered = new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.ChaCha,
EncryptionKey = key,
});
var msg = await recovered.LoadAsync(1, default);
msg.ShouldNotBeNull();
System.Text.Encoding.UTF8.GetString(msg.Payload.Span).ShouldBe("hello encrypted");
}
[Fact]
public async Task Encrypted_block_with_aes_round_trips()
{
var dir = Directory.CreateTempSubdirectory();
var key = new byte[32];
RandomNumberGenerator.Fill(key);
await using (var store = new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.Aes,
EncryptionKey = key,
}))
{
await store.AppendAsync("aes.subj", "aes payload"u8.ToArray(), default);
}
await using var recovered = new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.Aes,
EncryptionKey = key,
});
var msg = await recovered.LoadAsync(1, default);
msg.ShouldNotBeNull();
System.Text.Encoding.UTF8.GetString(msg.Payload.Span).ShouldBe("aes payload");
}
[Fact]
public async Task Wrong_key_fails_to_decrypt()
{
var dir = Directory.CreateTempSubdirectory();
var key1 = new byte[32];
var key2 = new byte[32];
RandomNumberGenerator.Fill(key1);
RandomNumberGenerator.Fill(key2);
await using (var store = new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.ChaCha,
EncryptionKey = key1,
}))
{
await store.AppendAsync("secret", "data"u8.ToArray(), default);
}
// Recovery with wrong key should throw InvalidDataException (from CryptographicException)
var act = () => new FileStore(new FileStoreOptions
{
Directory = dir.FullName,
Cipher = StoreCipher.ChaCha,
EncryptionKey = key2,
});
Should.Throw<InvalidDataException>(act);
}
}