Browse Source
* add tests and refactor Pkcs11RsaKeyService * add missed files * add annotations to interface * change public constructor to privatepull/170/head
6 changed files with 722 additions and 55 deletions
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
using Net.Pkcs11Interop.Common; |
||||
using Net.Pkcs11Interop.HighLevelAPI; |
||||
using Net.Pkcs11Interop.HighLevelAPI.Factories; |
||||
using Net.Pkcs11Interop.HighLevelAPI.MechanismParams; |
||||
|
||||
namespace Bit.KeyConnector.Services.Pkcs11; |
||||
|
||||
public interface IPkcs11InteropFactory |
||||
{ |
||||
/// <inheritdoc cref="IPkcs11LibraryFactory.LoadPkcs11Library(Pkcs11InteropFactories, string, AppType)"/> |
||||
IPkcs11Library LoadPkcs11Library(string libraryPath, AppType appType); |
||||
/// <inheritdoc cref="IMechanismParamsFactory.CreateCkRsaPkcsOaepParams(ulong, ulong, ulong, byte[])"/> |
||||
ICkRsaPkcsOaepParams CreateCkRsaPkcsOaepParams(ulong hashAlg, ulong mgf, ulong source, byte[] sourceData); |
||||
/// <inheritdoc cref="IMechanismFactory.Create(CKM)"/> |
||||
IMechanism CreateMechanism(CKM type); |
||||
/// <inheritdoc cref="IMechanismFactory.Create(CKM, IMechanismParams)"/> |
||||
IMechanism CreateMechanism(CKM type, IMechanismParams parameters); |
||||
/// <inheritdoc cref="IObjectAttributeFactory.Create(CKA, CKO)"/> |
||||
public IObjectAttribute CreateObjectAttribute(CKA type, CKO value); |
||||
/// <inheritdoc cref="IObjectAttributeFactory.Create(CKA, bool)"/> |
||||
public IObjectAttribute CreateObjectAttribute(CKA type, bool value); |
||||
/// <inheritdoc cref="IObjectAttributeFactory.Create(CKA, ulong)"/> |
||||
public IObjectAttribute CreateObjectAttribute(CKA type, ulong value); |
||||
/// <inheritdoc cref="IObjectAttributeFactory.Create(CKA, string)"/> |
||||
public IObjectAttribute CreateObjectAttribute(CKA type, string value); |
||||
} |
||||
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
using System; |
||||
using System.Reflection; |
||||
using System.Runtime.InteropServices; |
||||
using Net.Pkcs11Interop.Common; |
||||
using Net.Pkcs11Interop.HighLevelAPI; |
||||
using Net.Pkcs11Interop.HighLevelAPI.MechanismParams; |
||||
|
||||
namespace Bit.KeyConnector.Services.Pkcs11; |
||||
|
||||
public class Pkcs11InteropFactory : IPkcs11InteropFactory |
||||
{ |
||||
private readonly Pkcs11InteropFactories _factories; |
||||
|
||||
public Pkcs11InteropFactory() |
||||
{ |
||||
if (Platform.IsLinux) |
||||
{ |
||||
// https://github.com/Pkcs11Interop/Pkcs11Interop/issues/239 |
||||
NativeLibrary.SetDllImportResolver(typeof(Pkcs11InteropFactories).Assembly, CustomDllImportResolver); |
||||
} |
||||
_factories = new Pkcs11InteropFactories(); |
||||
} |
||||
|
||||
public IPkcs11Library LoadPkcs11Library(string libraryPath, AppType appType) |
||||
{ |
||||
return _factories.Pkcs11LibraryFactory.LoadPkcs11Library(_factories, libraryPath, appType); |
||||
} |
||||
|
||||
public ICkRsaPkcsOaepParams CreateCkRsaPkcsOaepParams(ulong hashAlg, ulong mgf, ulong source, byte[] sourceData) |
||||
{ |
||||
return _factories.MechanismParamsFactory.CreateCkRsaPkcsOaepParams(hashAlg, mgf, source, sourceData); |
||||
} |
||||
|
||||
public IMechanism CreateMechanism(CKM type) |
||||
{ |
||||
return _factories.MechanismFactory.Create(type); |
||||
} |
||||
|
||||
public IMechanism CreateMechanism(CKM type, IMechanismParams parameters) |
||||
{ |
||||
return _factories.MechanismFactory.Create(type, parameters); |
||||
} |
||||
|
||||
public IObjectAttribute CreateObjectAttribute(CKA type, CKO value) |
||||
{ |
||||
return _factories.ObjectAttributeFactory.Create(type, value); |
||||
} |
||||
|
||||
public IObjectAttribute CreateObjectAttribute(CKA type, bool value) |
||||
{ |
||||
return _factories.ObjectAttributeFactory.Create(type, value); |
||||
} |
||||
|
||||
public IObjectAttribute CreateObjectAttribute(CKA type, ulong value) |
||||
{ |
||||
return _factories.ObjectAttributeFactory.Create(type, value); |
||||
} |
||||
|
||||
public IObjectAttribute CreateObjectAttribute(CKA type, string value) |
||||
{ |
||||
return _factories.ObjectAttributeFactory.Create(type, value); |
||||
} |
||||
|
||||
|
||||
private static IntPtr CustomDllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath) |
||||
{ |
||||
var mappedLibraryName = (libraryName == "libdl") ? "libdl.so.2" : libraryName; |
||||
return NativeLibrary.Load(mappedLibraryName, assembly, dllImportSearchPath); |
||||
} |
||||
} |
||||
@ -0,0 +1,571 @@
@@ -0,0 +1,571 @@
|
||||
using Bit.KeyConnector.Services; |
||||
using Xunit; |
||||
using NSubstitute; |
||||
using Bit.KeyConnector; |
||||
using System.Threading.Tasks; |
||||
using Net.Pkcs11Interop.HighLevelAPI; |
||||
using Net.Pkcs11Interop.Common; |
||||
using System; |
||||
using System.Linq; |
||||
using System.Security.Cryptography; |
||||
using System.Security.Cryptography.X509Certificates; |
||||
using Bit.KeyConnector.Services.Pkcs11; |
||||
using Net.Pkcs11Interop.HighLevelAPI.MechanismParams; |
||||
|
||||
namespace KeyConnector.Tests.Services; |
||||
|
||||
internal delegate void InitializationModifier( |
||||
ICertificateProviderService certificateProviderService, |
||||
ICryptoFunctionService cryptoFunctionService, |
||||
IPkcs11InteropFactory pkcs11InteropFactory, |
||||
KeyConnectorSettings keyConnectorSettings |
||||
); |
||||
|
||||
public class Pkcs11RsaKeyServiceTests |
||||
{ |
||||
private readonly byte[] _data = "data"u8.ToArray(); |
||||
private readonly byte[] _encryptedData = "encryptedData"u8.ToArray(); |
||||
private readonly byte[] _decryptedData = "decryptedData"u8.ToArray(); |
||||
private readonly byte[] _signature = "signature"u8.ToArray(); |
||||
|
||||
private static Pkcs11RsaKeyService InitializeService(InitializationModifier modifier = null) |
||||
{ |
||||
var certificateProviderService = Substitute.For<ICertificateProviderService>(); |
||||
var cryptoFunctionService = Substitute.For<ICryptoFunctionService>(); |
||||
var pkcs11InteropFactory = Substitute.For<IPkcs11InteropFactory>(); |
||||
var settings = new KeyConnectorSettings(); |
||||
modifier?.Invoke(certificateProviderService, cryptoFunctionService, pkcs11InteropFactory, settings); |
||||
|
||||
return new Pkcs11RsaKeyService(certificateProviderService, cryptoFunctionService, pkcs11InteropFactory, |
||||
settings); |
||||
} |
||||
|
||||
// EncryptAsync Tests |
||||
|
||||
[Fact] |
||||
public async Task EncryptAsync_ReturnsEncryptedData() |
||||
{ |
||||
// Create certificate |
||||
var cert = CreateCertificate(); |
||||
var publicKey = cert.GetRSAPublicKey()!.ExportSubjectPublicKeyInfo(); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((certProvider, cryptoService, _, _) => |
||||
{ |
||||
certProvider.GetCertificateAsync().Returns(cert); |
||||
cryptoService.RsaEncryptAsync( |
||||
_data, |
||||
Arg.Is<byte[]>(input => publicKey.SequenceEqual(input))) |
||||
.Returns(_encryptedData); |
||||
}); |
||||
|
||||
// Act |
||||
var result = await sut.EncryptAsync(_data); |
||||
|
||||
// Assert |
||||
Assert.Equal("encryptedData"u8.ToArray(), result); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task EncryptAsync_ReturnsNull_GivenNullData() |
||||
{ |
||||
var sut = InitializeService(); |
||||
Assert.Null(await sut.EncryptAsync(null)); |
||||
} |
||||
|
||||
// DecryptAsync Tests |
||||
|
||||
[Fact] |
||||
public async Task DecryptAsync_ReturnsDecryptedData() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
var privateKey = session.AddPrivateKey(); |
||||
|
||||
var mechanismParams = Substitute.For<ICkRsaPkcsOaepParams>(); |
||||
var mechanism = Substitute.For<IMechanism>(); |
||||
|
||||
|
||||
session.Decrypt(mechanism, privateKey, _data).Returns(_decryptedData); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
interopFactory.CreateCkRsaPkcsOaepParams(default, default, default, default) |
||||
.ReturnsForAnyArgs(mechanismParams); |
||||
interopFactory.CreateMechanism(Arg.Any<CKM>(), mechanismParams).Returns(mechanism); |
||||
}); |
||||
|
||||
// Act |
||||
var result = await sut.DecryptAsync(_data); |
||||
|
||||
// Assert |
||||
Assert.Equal("decryptedData"u8.ToArray(), result); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task DecryptAsync_LogsOutOfSession_GivenValidData() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
var privateKey = session.AddPrivateKey(); |
||||
|
||||
var mechanismParams = Substitute.For<ICkRsaPkcsOaepParams>(); |
||||
var mechanism = Substitute.For<IMechanism>(); |
||||
|
||||
session.Decrypt(mechanism, privateKey, _data).Returns(_decryptedData); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
interopFactory.CreateCkRsaPkcsOaepParams(default, default, default, default) |
||||
.ReturnsForAnyArgs(mechanismParams); |
||||
interopFactory.CreateMechanism(Arg.Any<CKM>(), mechanismParams).Returns(mechanism); |
||||
}); |
||||
|
||||
// Act |
||||
await sut.DecryptAsync(_data); |
||||
|
||||
// Assert |
||||
session.Received().Logout(); |
||||
} |
||||
|
||||
[Theory] |
||||
[InlineData("yubihsm", "/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so")] |
||||
[InlineData("opensc", "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so")] |
||||
public async Task DecryptAsync_UsesCorrectProviderPath_WhenLoadingLibrary(string provider, string path) |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
IPkcs11InteropFactory pkcs11InteropFactory = null; |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings { Pkcs11Provider = provider }; |
||||
interopFactory.LoadPkcs11Library(path, Arg.Any<AppType>()).Returns(library); |
||||
pkcs11InteropFactory = interopFactory; |
||||
}); |
||||
|
||||
// Act |
||||
try |
||||
{ |
||||
await sut.DecryptAsync(_data); |
||||
} |
||||
catch |
||||
{ |
||||
} |
||||
|
||||
// Assert |
||||
pkcs11InteropFactory.Received().LoadPkcs11Library(path, Arg.Any<AppType>()); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task DecryptAsync_ThrowsException_WhenSlotHasNoTokenPresent() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
library.AddSlot("chosenSerialNumber", false); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
}); |
||||
|
||||
// Assert |
||||
var exception = await Assert.ThrowsAsync<Exception>(async () => await sut.DecryptAsync(_data)); |
||||
Assert.Contains("Cannot locate token slot.", exception.Message); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task DecryptAsync_ChoosesCorrectSlot_GivenASlotSerialNumber() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
|
||||
var slot1 = Substitute.For<ISlot>(); |
||||
slot1.GetSlotInfo().SlotFlags.TokenPresent.Returns(true); |
||||
slot1.GetTokenInfo().SerialNumber.Returns("chosenSerialNumber"); |
||||
var slot2 = Substitute.For<ISlot>(); |
||||
slot2.GetSlotInfo().SlotFlags.TokenPresent.Returns(true); |
||||
slot2.GetTokenInfo().SerialNumber.Returns("wrongSerialNumber"); |
||||
library.GetSlotList(default).ReturnsForAnyArgs([slot1, slot2]); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
}); |
||||
|
||||
// Act |
||||
try |
||||
{ |
||||
await sut.DecryptAsync(_data); |
||||
} |
||||
catch |
||||
{ |
||||
} |
||||
|
||||
// Assert |
||||
slot1.Received().OpenSession(Arg.Any<SessionType>()); |
||||
} |
||||
|
||||
[Theory] |
||||
[InlineData(null, CKU.CKU_USER)] |
||||
[InlineData("so", CKU.CKU_SO)] |
||||
[InlineData("context_specific", CKU.CKU_CONTEXT_SPECIFIC)] |
||||
public async Task DecryptAsync_UsesCorrectUserType_GivenInSettings(string userTypeSetting, CKU expectedUserType) |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = userTypeSetting, |
||||
Pkcs11LoginPin = "loginPin" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
}); |
||||
|
||||
// Act |
||||
try |
||||
{ |
||||
await sut.DecryptAsync(_data); |
||||
} |
||||
catch |
||||
{ |
||||
} |
||||
|
||||
// Assert |
||||
session.Received().Login(expectedUserType, "loginPin"); |
||||
} |
||||
|
||||
[Theory] |
||||
[InlineData(123UL, CKA.CKA_ID)] |
||||
[InlineData(null, CKA.CKA_LABEL)] |
||||
public async Task DecryptAsync_UsesPrivateKeyId_IfAvailableInSettings(ulong? id, CKA expectedType) |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
session.AddPrivateKey(); |
||||
|
||||
IPkcs11InteropFactory pkcs11InteropFactory = null; |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin", |
||||
Pkcs11PrivateKeyId = id, |
||||
Pkcs11PrivateKeyLabel = "label" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
pkcs11InteropFactory = interopFactory; |
||||
}); |
||||
|
||||
// Act |
||||
await sut.DecryptAsync(_data); |
||||
|
||||
// Assert |
||||
if (id is not null) |
||||
{ |
||||
pkcs11InteropFactory.Received().CreateObjectAttribute(expectedType, id.Value); |
||||
} |
||||
else |
||||
{ |
||||
pkcs11InteropFactory.Received().CreateObjectAttribute(expectedType, "label"); |
||||
} |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task DecryptAsync_ReturnsNull_GivenNullData() |
||||
{ |
||||
var sut = InitializeService(); |
||||
Assert.Null(await sut.DecryptAsync(null)); |
||||
} |
||||
|
||||
// SignAsync Tests |
||||
|
||||
[Fact] |
||||
public async Task SignAsync_ReturnsSignature() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
var privateKey = session.AddPrivateKey(); |
||||
var mechanism = Substitute.For<IMechanism>(); |
||||
|
||||
session.Sign(mechanism, privateKey, _data).Returns(_signature); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
interopFactory.CreateMechanism(Arg.Any<CKM>()).Returns(mechanism); |
||||
}); |
||||
|
||||
// Act |
||||
var result = await sut.SignAsync(_data); |
||||
|
||||
// Assert |
||||
Assert.Equal("signature"u8.ToArray(), result); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task SignAsync_LogsOutOfSession_GivenValidData() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
var privateKey = session.AddPrivateKey(); |
||||
var mechanism = Substitute.For<IMechanism>(); |
||||
|
||||
session.Sign(mechanism, privateKey, _data).Returns(_signature); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin" |
||||
}; |
||||
|
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
interopFactory.CreateMechanism(Arg.Any<CKM>()).Returns(mechanism); |
||||
}); |
||||
|
||||
// Act |
||||
await sut.SignAsync(_data); |
||||
|
||||
// Assert |
||||
session.Received().Logout(); |
||||
} |
||||
|
||||
[Theory] |
||||
[InlineData("yubihsm", "/usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so")] |
||||
[InlineData("opensc", "/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so")] |
||||
public async Task SignAsync_UsesCorrectProviderPath_WhenLoadingLibrary(string provider, string path) |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
IPkcs11InteropFactory pkcs11InteropFactory = null; |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings { Pkcs11Provider = provider }; |
||||
interopFactory.LoadPkcs11Library(path, Arg.Any<AppType>()).Returns(library); |
||||
pkcs11InteropFactory = interopFactory; |
||||
}); |
||||
|
||||
// Act |
||||
try |
||||
{ |
||||
await sut.SignAsync(_data); |
||||
} |
||||
catch |
||||
{ |
||||
} |
||||
|
||||
// Assert |
||||
pkcs11InteropFactory.Received().LoadPkcs11Library(path, Arg.Any<AppType>()); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task SignAsync_ThrowsException_WhenSlotHasNoTokenPresent() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
library.AddSlot("chosenSerialNumber", false); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
}); |
||||
|
||||
// Assert |
||||
var exception = await Assert.ThrowsAsync<Exception>(async () => await sut.SignAsync(_data)); |
||||
Assert.Contains("Cannot locate token slot.", exception.Message); |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task SignAsync_ChoosesCorrectSlot_GivenASlotSerialNumber() |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
|
||||
var slot1 = Substitute.For<ISlot>(); |
||||
slot1.GetSlotInfo().SlotFlags.TokenPresent.Returns(true); |
||||
slot1.GetTokenInfo().SerialNumber.Returns("chosenSerialNumber"); |
||||
var slot2 = Substitute.For<ISlot>(); |
||||
slot2.GetSlotInfo().SlotFlags.TokenPresent.Returns(true); |
||||
slot2.GetTokenInfo().SerialNumber.Returns("wrongSerialNumber"); |
||||
library.GetSlotList(default).ReturnsForAnyArgs([slot1, slot2]); |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
}); |
||||
|
||||
// Act |
||||
try |
||||
{ |
||||
await sut.SignAsync(_data); |
||||
} |
||||
catch |
||||
{ |
||||
} |
||||
|
||||
// Assert |
||||
slot1.Received().OpenSession(Arg.Any<SessionType>()); |
||||
} |
||||
|
||||
[Theory] |
||||
[InlineData(123UL, CKA.CKA_ID)] |
||||
[InlineData(null, CKA.CKA_LABEL)] |
||||
public async Task SignAsync_UsesPrivateKeyId_IfAvailableInSettings(ulong? id, CKA expectedType) |
||||
{ |
||||
// Create mocks |
||||
var library = Substitute.For<IPkcs11Library>(); |
||||
var slot = library.AddSlot("chosenSerialNumber"); |
||||
var session = slot.AddSession(); |
||||
session.AddPrivateKey(); |
||||
|
||||
IPkcs11InteropFactory pkcs11InteropFactory = null; |
||||
|
||||
// Initialize sut |
||||
var sut = InitializeService((_, _, interopFactory, settings) => |
||||
{ |
||||
settings.RsaKey = new KeyConnectorSettings.RsaKeySettings |
||||
{ |
||||
Pkcs11Provider = "yubihsm", |
||||
Pkcs11SlotTokenSerialNumber = "chosenSerialNumber", |
||||
Pkcs11LoginUserType = "userType", |
||||
Pkcs11LoginPin = "loginPin", |
||||
Pkcs11PrivateKeyId = id, |
||||
Pkcs11PrivateKeyLabel = "label" |
||||
}; |
||||
interopFactory.LoadPkcs11Library(default, default).ReturnsForAnyArgs(library); |
||||
pkcs11InteropFactory = interopFactory; |
||||
}); |
||||
|
||||
// Act |
||||
await sut.SignAsync(_data); |
||||
|
||||
// Assert |
||||
if (id is not null) |
||||
{ |
||||
pkcs11InteropFactory.Received().CreateObjectAttribute(expectedType, id.Value); |
||||
} |
||||
else |
||||
{ |
||||
pkcs11InteropFactory.Received().CreateObjectAttribute(expectedType, "label"); |
||||
} |
||||
} |
||||
|
||||
[Fact] |
||||
public async Task SignAsync_ReturnsNull_GivenNullData() |
||||
{ |
||||
var sut = InitializeService(); |
||||
Assert.Null(await sut.SignAsync(null)); |
||||
} |
||||
|
||||
private static X509Certificate2 CreateCertificate() |
||||
{ |
||||
var rsa = RSA.Create(2048); |
||||
var req = new CertificateRequest("CN=Experimental Issuing Authority", rsa, HashAlgorithmName.SHA256, |
||||
RSASignaturePadding.Pkcs1); |
||||
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5)); |
||||
return cert; |
||||
} |
||||
} |
||||
|
||||
internal static class Extensions |
||||
{ |
||||
public static ISlot AddSlot(this IPkcs11Library library, string serialNumber, bool tokenPresent = true) |
||||
{ |
||||
var slot = Substitute.For<ISlot>(); |
||||
slot.GetSlotInfo().SlotFlags.TokenPresent.Returns(tokenPresent); |
||||
slot.GetTokenInfo().SerialNumber.Returns(serialNumber); |
||||
library.GetSlotList(default).ReturnsForAnyArgs([slot]); |
||||
return slot; |
||||
} |
||||
|
||||
public static ISession AddSession(this ISlot slot) |
||||
{ |
||||
var session = Substitute.For<ISession>(); |
||||
slot.OpenSession(default).ReturnsForAnyArgs(session); |
||||
return session; |
||||
} |
||||
|
||||
public static IObjectHandle AddPrivateKey(this ISession session) |
||||
{ |
||||
var privateKey = Substitute.For<IObjectHandle>(); |
||||
session.FindAllObjects(default).ReturnsForAnyArgs([privateKey]); |
||||
return privateKey; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue