85b362e8cd
- add the standalone HrynCo.Common solution and projects - include the shared common library source and tests - add package metadata and a repo gitignore
82 lines
2.7 KiB
C#
82 lines
2.7 KiB
C#
namespace HrynCo.Common.Tests;
|
|
|
|
using System.Security.Cryptography;
|
|
using HrynCo.Common.Security;
|
|
using FluentAssertions;
|
|
using Xunit;
|
|
|
|
public sealed class SecretProtectorTests
|
|
{
|
|
[Fact]
|
|
public void Constructor_ShouldRejectMissingKey()
|
|
{
|
|
Action act = () => _ = new SecretProtector(string.Empty);
|
|
|
|
act.Should().Throw<InvalidOperationException>()
|
|
.WithMessage("Secret encryption key is not configured.");
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ShouldRejectInvalidKeyLength()
|
|
{
|
|
string key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(31));
|
|
|
|
Action act = () => _ = new SecretProtector(key);
|
|
|
|
act.Should().Throw<InvalidOperationException>()
|
|
.WithMessage("Secret encryption key must be 32 bytes encoded as Base64.");
|
|
}
|
|
|
|
[Fact]
|
|
public void ProtectAndUnprotect_ShouldRoundTripPlaintext()
|
|
{
|
|
string key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
|
var protector = new SecretProtector(key);
|
|
|
|
string protectedValue = protector.Protect("hello world");
|
|
string plaintext = protector.Unprotect(protectedValue);
|
|
|
|
protectedValue.Should().StartWith("v1:");
|
|
plaintext.Should().Be("hello world");
|
|
}
|
|
|
|
[Fact]
|
|
public void Unprotect_ShouldRejectUnsupportedFormat()
|
|
{
|
|
string key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
|
var protector = new SecretProtector(key);
|
|
|
|
Action act = () => protector.Unprotect("v2:payload");
|
|
|
|
act.Should().Throw<InvalidOperationException>()
|
|
.WithMessage("Unsupported protected value format.");
|
|
}
|
|
|
|
[Fact]
|
|
public void Unprotect_ShouldFailWhenProtectedValueIsTamperedWith()
|
|
{
|
|
string key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
|
var protector = new SecretProtector(key);
|
|
string protectedValue = protector.Protect("hello world");
|
|
string tamperedValue = protectedValue[..^1] + (protectedValue.EndsWith('A') ? 'B' : 'A');
|
|
|
|
Action act = () => protector.Unprotect(tamperedValue);
|
|
|
|
act.Should().Throw<CryptographicException>();
|
|
}
|
|
|
|
[Fact]
|
|
public void Unprotect_ShouldFailWhenUsingTheWrongKey()
|
|
{
|
|
string originalKey = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
|
string otherKey = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
|
var protector = new SecretProtector(originalKey);
|
|
var wrongProtector = new SecretProtector(otherKey);
|
|
string protectedValue = protector.Protect("hello world");
|
|
|
|
Action act = () => wrongProtector.Unprotect(protectedValue);
|
|
|
|
act.Should().Throw<CryptographicException>();
|
|
}
|
|
}
|