chore: add hrynco common library solution
- add the standalone HrynCo.Common solution and projects - include the shared common library source and tests - add package metadata and a repo gitignore
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
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>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user