using EnvelopeGenerator.Application.Common.Configurations; using Net.Pkcs11Interop.Common; using Net.Pkcs11Interop.HighLevelAPI; namespace EnvelopeGenerator.Infrastructure.Crypt.Services; internal static class Pkcs11SessionRunner { public static T Run(Pkcs11Options options, Func action) { Pkcs11InteropBootstrap.EnsureLoaded(options); var factories = new Pkcs11InteropFactories(); using var pkcs11 = LoadLibraryWithFallback(factories, options.ResolvedLibraryPath, options.ProxySocket); var slot = ResolveSlot(pkcs11.GetSlotList(SlotsType.WithTokenPresent), options); using var session = slot.OpenSession(SessionType.ReadOnly); session.Login(CKU.CKU_USER, options.UserPin); try { return action(session); } finally { session.Logout(); } } private static IPkcs11Library LoadLibraryWithFallback(Pkcs11InteropFactories factories, string libraryPath, string? proxySocket) { List errors = []; foreach (string candidate in BuildLibraryCandidates(libraryPath)) { try { return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.MultiThreaded); } catch (Exception ex) { errors.Add(new InvalidOperationException($"{candidate} | MultiThreaded+WithFunctionList", ex)); } try { return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.SingleThreaded); } catch (Exception ex) { errors.Add(new InvalidOperationException($"{candidate} | SingleThreaded+WithFunctionList", ex)); } try { return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.MultiThreaded, InitType.WithoutFunctionList); } catch (Exception ex) { errors.Add(new InvalidOperationException($"{candidate} | MultiThreaded+WithoutFunctionList", ex)); } try { return factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, candidate, AppType.SingleThreaded, InitType.WithoutFunctionList); } catch (Exception ex) { errors.Add(new InvalidOperationException($"{candidate} | SingleThreaded+WithoutFunctionList", ex)); } } var socketInfo = string.IsNullOrWhiteSpace(proxySocket) ? "" : proxySocket; throw new AggregateException($"Unable to initialize PKCS#11 library '{libraryPath}' with any supported initialization mode. PKCS11_PROXY_SOCKET={socketInfo}", errors); } private static IEnumerable BuildLibraryCandidates(string configuredPath) { var unique = new HashSet(StringComparer.OrdinalIgnoreCase) { configuredPath }; var directory = Path.GetDirectoryName(configuredPath); if (!string.IsNullOrWhiteSpace(directory)) { string[] alternates = [ "client.dll", "pkcs11-proxy.dll", "libpkcs11-proxy.dll" ]; foreach (var fileName in alternates) { var candidate = Path.Combine(directory, fileName); if (File.Exists(candidate)) unique.Add(candidate); } } return unique; } private static ISlot ResolveSlot(IEnumerable slots, Pkcs11Options options) { if (options.SlotId.HasValue) { var bySlotId = slots.FirstOrDefault(s => s.SlotId == options.SlotId.Value); if (bySlotId is not null) return bySlotId; } if (!string.IsNullOrWhiteSpace(options.TokenLabel)) { var byTokenLabel = slots.FirstOrDefault(s => string.Equals(s.GetTokenInfo().Label?.Trim(), options.TokenLabel.Trim(), StringComparison.Ordinal)); if (byTokenLabel is not null) return byTokenLabel; } throw new InvalidOperationException("Unable to resolve PKCS#11 slot. Check Crypt:Pkcs11:SlotId / TokenLabel."); } }