From 2915c3227485004496d2893c8ceca11e7685b3e5 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Tue, 31 Mar 2026 07:15:03 -0500 Subject: [PATCH] feat: implement machine key provider - implement logic for hashing machine key - extracted interfaces --- .../Cryptography/IMachineIdKeyGenerator.cs | 6 ++ .../Cryptography/IMachineIdKeyProvider.cs | 6 ++ .../Cryptography/MachineIdKeyGenerator.cs | 7 +- .../Cryptography/MachineIdKeyProvider.cs | 14 +++- .../Cryptography/MachineIdKeyProviderTests.cs | 72 +++++++++++++++++++ 5 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyGenerator.cs create mode 100644 src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyProvider.cs diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyGenerator.cs b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyGenerator.cs new file mode 100644 index 0000000..53a05a8 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyGenerator.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cryptography; + +internal interface IMachineIdKeyGenerator +{ + string GetId(); +} diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyProvider.cs b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyProvider.cs new file mode 100644 index 0000000..9bcd2c4 --- /dev/null +++ b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/IMachineIdKeyProvider.cs @@ -0,0 +1,6 @@ +namespace StevanFreeborn.Extensions.Configuration.Secure.Cryptography; + +internal interface IEncryptionKeyProvider +{ + byte[] GetKey(); +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyGenerator.cs b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyGenerator.cs index 971a7ca..93e4673 100644 --- a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyGenerator.cs +++ b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyGenerator.cs @@ -5,11 +5,6 @@ using Microsoft.Extensions.Logging; namespace StevanFreeborn.Extensions.Configuration.Secure.Cryptography; -internal interface IMachineIdKeyGenerator -{ - string GetId(); -} - internal sealed class MachineIdKeyGenerator : IMachineIdKeyGenerator { private const string IOPlatformUUID = nameof(IOPlatformUUID); @@ -111,4 +106,4 @@ internal static partial class LogMessages Message = "Using fallback strategy for Machine ID generation." )] public static partial void LogUsingFallbackStrategy(this ILogger logger); -} +} \ No newline at end of file diff --git a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyProvider.cs b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyProvider.cs index 088f671..cc17a29 100644 --- a/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyProvider.cs +++ b/src/StevanFreeborn.Extensions.Configuration.Secure/Cryptography/MachineIdKeyProvider.cs @@ -1,5 +1,17 @@ +using System.Security.Cryptography; +using System.Text; + namespace StevanFreeborn.Extensions.Configuration.Secure.Cryptography; -internal sealed class MachineIdKeyProvider +internal sealed class MachineIdKeyProvider(IMachineIdKeyGenerator generator) : IEncryptionKeyProvider { + private readonly IMachineIdKeyGenerator _generator = generator; + + public byte[] GetKey() + { + var machineId = _generator.GetId(); + var @bytes = Encoding.UTF8.GetBytes(machineId); + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(@bytes); + } } \ No newline at end of file diff --git a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Cryptography/MachineIdKeyProviderTests.cs b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Cryptography/MachineIdKeyProviderTests.cs index bc6af7b..ba80069 100644 --- a/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Cryptography/MachineIdKeyProviderTests.cs +++ b/tests/StevanFreeborn.Extensions.Configuration.Secure.Tests/Unit/Cryptography/MachineIdKeyProviderTests.cs @@ -1,6 +1,78 @@ +using System.Security.Cryptography; +using System.Text; + +using Moq; + +using StevanFreeborn.Extensions.Configuration.Secure.Cryptography; + namespace StevanFreeborn.Extensions.Configuration.Secure.Tests.Unit.Cryptography; public class MachineIdKeyProviderTests { + private readonly Mock _mockKeyGenerator = new(); + private readonly MachineIdKeyProvider _sut; + public MachineIdKeyProviderTests() + { + _sut = new(_mockKeyGenerator.Object); + } + + [Fact] + public void GetKey_WhenCalled_ItShouldReturn32ByteKey() + { + _mockKeyGenerator.Setup(static m => m.GetId()).Returns("random-stuff"); + + var result = _sut.GetKey(); + + result.Length.Should().Be(32); + } + + [Fact] + public void GetKey_WhenCalledForSameInput_ItShouldProduceConsistentHash() + { + var input = "some-machine-id"; + + var mockInstanceOne = new Mock(); + mockInstanceOne.Setup(static m => m.GetId()).Returns(input); + + var instanceOne = new MachineIdKeyProvider(mockInstanceOne.Object); + var resultOne = instanceOne.GetKey(); + + var mockInstanceTwo = new Mock(); + mockInstanceTwo.Setup(static m => m.GetId()).Returns(input); + + var instanceTwo = new MachineIdKeyProvider(mockInstanceTwo.Object); + var resultTwo = instanceTwo.GetKey(); + + resultOne.Should().BeEquivalentTo(resultTwo); + } + + [Fact] + public void GetKey_WhenCalledWithDifferentInput_ItShouldProduceDifferentHashes() + { + var inputOne = "inputOne"; + var inputTwo = "inputTwo"; + + _mockKeyGenerator.SetupSequence(static m => m.GetId()) + .Returns(inputOne) + .Returns(inputTwo); + + var resultOne = _sut.GetKey(); + var resultTwo = _sut.GetKey(); + + resultOne.Should().NotBeEquivalentTo(resultTwo); + } + + [Fact] + public void GetKey_WhenCalled_ItShouldUseSHA256Hash() + { + var rawId = "rawId"; + var expectedHash = SHA256.HashData(Encoding.UTF8.GetBytes(rawId)); + + _mockKeyGenerator.Setup(static m => m.GetId()).Returns(rawId); + + var result = _sut.GetKey(); + + result.Should().BeEquivalentTo(expectedHash); + } } \ No newline at end of file