diff --git a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Helpers/UrlReservationChecker.cs b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Helpers/UrlReservationChecker.cs
new file mode 100644
index 000000000..21e0ce2d0
--- /dev/null
+++ b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Helpers/UrlReservationChecker.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Security.Principal;
+using fiskaltrust.storage.serialization.V0;
+using Microsoft.Extensions.Logging;
+
+namespace fiskaltrust.Middleware.Queue.Test.Launcher.Helpers
+{
+ ///
+ /// Verifies that http/https URLs configured for the queues (e.g. retrieved from Helipad)
+ /// have a matching netsh URL ACL reservation, so hosting does not fail at runtime.
+ ///
+ public static class UrlReservationChecker
+ {
+ public static void CheckQueueUrlReservations(ftCashBoxConfiguration cashBoxConfiguration, ILogger logger)
+ {
+ if (cashBoxConfiguration?.ftQueues == null)
+ {
+ return;
+ }
+
+ // Already elevated: HttpListener can self-register, no need to warn.
+ if (IsRunningAsAdministrator())
+ {
+ return;
+ }
+
+ string aclOutput = null;
+ var checkedPorts = new HashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var queue in cashBoxConfiguration.ftQueues)
+ {
+ if (queue?.Url == null)
+ {
+ continue;
+ }
+
+ foreach (var rawUrl in queue.Url)
+ {
+ if (string.IsNullOrWhiteSpace(rawUrl))
+ {
+ continue;
+ }
+
+ var httpUrl = rawUrl.Replace("rest://", "http://");
+ if (!Uri.TryCreate(httpUrl, UriKind.Absolute, out var uri))
+ {
+ continue;
+ }
+
+ if (!string.Equals(uri.Scheme, "http", StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(uri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var key = uri.Scheme.ToLowerInvariant() + ":" + uri.Port.ToString(CultureInfo.InvariantCulture);
+ if (!checkedPorts.Add(key))
+ {
+ continue;
+ }
+
+ aclOutput ??= QueryUrlAcls();
+
+ if (HasReservationForPort(aclOutput, uri.Scheme, uri.Port))
+ {
+ logger.LogDebug("URL reservation found for {Scheme}://+:{Port}/", uri.Scheme, uri.Port);
+ continue;
+ }
+
+ var urlPrefix = $"{uri.Scheme}://+:{uri.Port}{uri.AbsolutePath}";
+ if (!urlPrefix.EndsWith("/", StringComparison.Ordinal))
+ {
+ urlPrefix += "/";
+ }
+
+ var userName = WindowsIdentity.GetCurrent().Name;
+ logger.LogWarning(
+ "No URL reservation found for port {Port} (configured URL: {Url}). Run the following command once as administrator: netsh http add urlacl url={UrlPrefix} user={User}",
+ uri.Port, rawUrl, urlPrefix, userName);
+ }
+ }
+ }
+
+ private static bool IsRunningAsAdministrator()
+ {
+ try
+ {
+ var identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static string QueryUrlAcls()
+ {
+ try
+ {
+ var psi = new ProcessStartInfo("netsh", "http show urlacl")
+ {
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ CreateNoWindow = true
+ };
+ using var process = Process.Start(psi);
+ var output = process.StandardOutput.ReadToEnd();
+ process.WaitForExit(5000);
+ return output ?? string.Empty;
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+
+ private static bool HasReservationForPort(string aclOutput, string scheme, int port)
+ {
+ if (string.IsNullOrEmpty(aclOutput))
+ {
+ return false;
+ }
+
+ var portToken = ":" + port.ToString(CultureInfo.InvariantCulture) + "/";
+ foreach (var rawLine in aclOutput.Split('\n'))
+ {
+ var line = rawLine.Trim();
+ var schemeIdx = line.IndexOf(scheme + "://", StringComparison.OrdinalIgnoreCase);
+ if (schemeIdx < 0)
+ {
+ continue;
+ }
+ if (line.IndexOf(portToken, schemeIdx, StringComparison.Ordinal) >= 0)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Program.cs b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Program.cs
index 4efc30d31..fb7743791 100644
--- a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Program.cs
+++ b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Program.cs
@@ -40,6 +40,15 @@ public static void Main(string configurationFilePath = "", string serviceFolder
serviceFolder = Directory.GetCurrentDirectory();
}
+ var serviceCollectionForChecker = new ServiceCollection();
+ serviceCollectionForChecker.AddStandardLoggers(LogLevel.Debug);
+ using (var checkerProvider = serviceCollectionForChecker.BuildServiceProvider())
+ {
+ var checkerLogger = checkerProvider.GetRequiredService()
+ .CreateLogger(typeof(Helpers.UrlReservationChecker).FullName);
+ Helpers.UrlReservationChecker.CheckQueueUrlReservations(cashBoxConfiguration, checkerLogger);
+ }
+
var config = cashBoxConfiguration.ftQueues[0];
config.Configuration.Add("cashboxid", cashBoxConfiguration.ftCashBoxId);
@@ -173,4 +182,4 @@ private static void ConfigureEF(PackageConfiguration queue, ServiceCollection se
bootStrapper.ConfigureServices(serviceCollection);
}
}
-}
+}
\ No newline at end of file
diff --git a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/RestService.cs b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/RestService.cs
index a919494d0..ba64034eb 100644
--- a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/RestService.cs
+++ b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/RestService.cs
@@ -150,7 +150,7 @@ private void TryAddUrlReservation(Uri baseAddress)
urlPrefix += "/";
}
- if (HasUrlReservation(urlPrefix))
+ if (HasUrlReservationForPort(baseAddress.Scheme, baseAddress.Port))
{
return;
}
@@ -159,7 +159,7 @@ private void TryAddUrlReservation(Uri baseAddress)
_logger.LogWarning("No URL reservation found for {Url}. Run the following command once as administrator: netsh http add urlacl url={Url} user={User}", urlPrefix, urlPrefix, userName);
}
- private bool HasUrlReservation(string urlPrefix)
+ private bool HasUrlReservationForPort(string scheme, int port)
{
try
{
@@ -172,7 +172,24 @@ private bool HasUrlReservation(string urlPrefix)
var process = Process.Start(psi);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit(5000);
- return output.IndexOf(urlPrefix, StringComparison.OrdinalIgnoreCase) >= 0;
+
+ // Consider any reservation on the same scheme and port as sufficient,
+ // regardless of the path portion of the URL.
+ var portToken = ":" + port.ToString(System.Globalization.CultureInfo.InvariantCulture) + "/";
+ foreach (var rawLine in output.Split('\n'))
+ {
+ var line = rawLine.Trim();
+ var schemeIdx = line.IndexOf(scheme + "://", StringComparison.OrdinalIgnoreCase);
+ if (schemeIdx < 0)
+ {
+ continue;
+ }
+ if (line.IndexOf(portToken, schemeIdx, StringComparison.Ordinal) >= 0)
+ {
+ return true;
+ }
+ }
+ return false;
}
catch
{
diff --git a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/WCFService.cs b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/WCFService.cs
index e337f3366..e3a2193ab 100644
--- a/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/WCFService.cs
+++ b/queue/test/Manual/fiskaltrust.Middleware.Queue.Test.Launcher/Wcf/WCFService.cs
@@ -162,7 +162,7 @@ private void TryAddUrlReservation(Uri baseAddress)
urlPrefix += "/";
}
- if (HasUrlReservation(urlPrefix))
+ if (HasUrlReservationForPort(baseAddress.Scheme, baseAddress.Port))
{
return;
}
@@ -171,7 +171,7 @@ private void TryAddUrlReservation(Uri baseAddress)
_logger.LogWarning("No URL reservation found for {Url}. Run the following command once as administrator: netsh http add urlacl url={Url} user={User}", urlPrefix, urlPrefix, userName);
}
- private bool HasUrlReservation(string urlPrefix)
+ private bool HasUrlReservationForPort(string scheme, int port)
{
try
{
@@ -184,7 +184,24 @@ private bool HasUrlReservation(string urlPrefix)
var process = Process.Start(psi);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit(5000);
- return output.IndexOf(urlPrefix, StringComparison.OrdinalIgnoreCase) >= 0;
+
+ // Consider any reservation on the same scheme and port as sufficient,
+ // regardless of the path portion of the URL.
+ var portToken = ":" + port.ToString(System.Globalization.CultureInfo.InvariantCulture) + "/";
+ foreach (var rawLine in output.Split('\n'))
+ {
+ var line = rawLine.Trim();
+ var schemeIdx = line.IndexOf(scheme + "://", StringComparison.OrdinalIgnoreCase);
+ if (schemeIdx < 0)
+ {
+ continue;
+ }
+ if (line.IndexOf(portToken, schemeIdx, StringComparison.Ordinal) >= 0)
+ {
+ return true;
+ }
+ }
+ return false;
}
catch
{
diff --git a/scu-de/fiskaltrust.Middleware.SCU.DE.sln b/scu-de/fiskaltrust.Middleware.SCU.DE.sln
index 858f1ae01..f1683fafb 100644
--- a/scu-de/fiskaltrust.Middleware.SCU.DE.sln
+++ b/scu-de/fiskaltrust.Middleware.SCU.DE.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.0.31521.260
+# Visual Studio Version 18
+VisualStudioVersion = 18.6.11822.322
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2F690A2B-96D1-45AC-BD36-E27D68422352}"
EndProject
@@ -29,8 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.SCU.
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Helpers", "Helpers", "{860D3016-9A5C-4162-ACC8-B2031B80F697}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.SCU.DE.Swissbit", "src\fiskaltrust.Middleware.SCU.DE.Swissbit\fiskaltrust.Middleware.SCU.DE.Swissbit.csproj", "{13BBFBA8-0991-484C-8626-A62062119E38}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.SCU.DE.Test.Launcher", "test\Manual\fiskaltrust.Middleware.SCU.DE.Test.Launcher\fiskaltrust.Middleware.SCU.DE.Test.Launcher.csproj", "{C00787F3-B5C1-413B-813A-57E6168E7661}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Manual", "Manual", "{800DAE8D-CCF0-43E6-A974-916FB22C112A}"
@@ -65,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.SCU.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Middleware.SCU.DE.FiskalyCertified.UnitTest", "test\fiskaltrust.Middleware.SCU.DE.FiskalyCertified.UnitTest\fiskaltrust.Middleware.SCU.DE.FiskalyCertified.UnitTest.csproj", "{4DAA4C67-C0B0-48C3-B41F-851759D86DFB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Middleware.SCU.DE.Swissbit", "src\fiskaltrust.Middleware.SCU.DE.Swissbit\fiskaltrust.Middleware.SCU.DE.Swissbit.csproj", "{AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -147,18 +147,6 @@ Global
{0AECD929-369B-4644-AF9A-16824AD9FFEE}.Release|x64.Build.0 = Release|Any CPU
{0AECD929-369B-4644-AF9A-16824AD9FFEE}.Release|x86.ActiveCfg = Release|Any CPU
{0AECD929-369B-4644-AF9A-16824AD9FFEE}.Release|x86.Build.0 = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|x64.ActiveCfg = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|x64.Build.0 = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|x86.ActiveCfg = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Debug|x86.Build.0 = Debug|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|Any CPU.Build.0 = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|x64.ActiveCfg = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|x64.Build.0 = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|x86.ActiveCfg = Release|Any CPU
- {13BBFBA8-0991-484C-8626-A62062119E38}.Release|x86.Build.0 = Release|Any CPU
{C00787F3-B5C1-413B-813A-57E6168E7661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C00787F3-B5C1-413B-813A-57E6168E7661}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C00787F3-B5C1-413B-813A-57E6168E7661}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -351,6 +339,18 @@ Global
{4DAA4C67-C0B0-48C3-B41F-851759D86DFB}.Release|x64.Build.0 = Release|Any CPU
{4DAA4C67-C0B0-48C3-B41F-851759D86DFB}.Release|x86.ActiveCfg = Release|Any CPU
{4DAA4C67-C0B0-48C3-B41F-851759D86DFB}.Release|x86.Build.0 = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|x64.Build.0 = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Debug|x86.Build.0 = Debug|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|x64.ActiveCfg = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|x64.Build.0 = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|x86.ActiveCfg = Release|Any CPU
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -363,7 +363,6 @@ Global
{115522DE-DEC8-461B-AEDD-9D68631F9E57} = {7201C6FB-C25C-45DB-9C0B-1355FEF13939}
{0AECD929-369B-4644-AF9A-16824AD9FFEE} = {2F690A2B-96D1-45AC-BD36-E27D68422352}
{860D3016-9A5C-4162-ACC8-B2031B80F697} = {2F690A2B-96D1-45AC-BD36-E27D68422352}
- {13BBFBA8-0991-484C-8626-A62062119E38} = {2F690A2B-96D1-45AC-BD36-E27D68422352}
{C00787F3-B5C1-413B-813A-57E6168E7661} = {800DAE8D-CCF0-43E6-A974-916FB22C112A}
{800DAE8D-CCF0-43E6-A974-916FB22C112A} = {7201C6FB-C25C-45DB-9C0B-1355FEF13939}
{DFAC4C0A-A019-4E09-B9A1-0CBEF132335E} = {7201C6FB-C25C-45DB-9C0B-1355FEF13939}
@@ -381,6 +380,7 @@ Global
{61F3E070-5E01-4EBE-B6FC-64D05472CB19} = {2F690A2B-96D1-45AC-BD36-E27D68422352}
{0728A325-37E5-4E23-AAC5-DB2860BB75F3} = {7201C6FB-C25C-45DB-9C0B-1355FEF13939}
{4DAA4C67-C0B0-48C3-B41F-851759D86DFB} = {7201C6FB-C25C-45DB-9C0B-1355FEF13939}
+ {AB1DAD78-6EF3-72DD-D37A-D8A5089CCF3A} = {2F690A2B-96D1-45AC-BD36-E27D68422352}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C5E269B8-4A21-4B1B-8805-C8193C08FE3E}
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/.nuspec b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/.nuspec
index 6ee7f87cb..a0efc0b10 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/.nuspec
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/.nuspec
@@ -39,8 +39,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/FunctionPointerFactory.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/FunctionPointerFactory.cs
index 148825bf7..472009d78 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/FunctionPointerFactory.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/FunctionPointerFactory.cs
@@ -1,4 +1,5 @@
-namespace fiskaltrust.Middleware.SCU.DE.Swissbit.Interop
+
+namespace fiskaltrust.Middleware.SCU.DE.Swissbit.Interop
{
public class FunctionPointerFactory : INativeFunctionPointerFactory
{
@@ -22,9 +23,6 @@ public class FunctionPointerFactory : INativeFunctionPointerFactory
func_worm_info_isExportEnabledIfCspTestFails = NativeWormAPI.worm_info_isExportEnabledIfCspTestFails,
func_worm_info_initializationState = NativeWormAPI.worm_info_initializationState,
func_worm_info_isDataImportInProgress = NativeWormAPI.worm_info_isDataImportInProgress,
- func_worm_info_hasChangedPuk = NativeWormAPI.worm_info_hasChangedPuk,
- func_worm_info_hasChangedAdminPin = NativeWormAPI.worm_info_hasChangedAdminPin,
- func_worm_info_hasChangedTimeAdminPin = NativeWormAPI.worm_info_hasChangedTimeAdminPin,
func_worm_info_timeUntilNextSelfTest = NativeWormAPI.worm_info_timeUntilNextSelfTest,
func_worm_info_startedTransactions = NativeWormAPI.worm_info_startedTransactions,
func_worm_info_maxStartedTransactions = NativeWormAPI.worm_info_maxStartedTransactions,
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/ISwissbitProxy.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/ISwissbitProxy.cs
index c1ced39a6..9a6a6f508 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/ISwissbitProxy.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/ISwissbitProxy.cs
@@ -40,5 +40,8 @@ public interface ISwissbitProxy : IDisposable
public Task ExportTarFilteredTransactionAsync(Stream stream, UInt64 startTransactionNumber, UInt64 endTransactionNumber, string clientId);
public Task GetLogMessageCertificateAsync();
public Task DeleteStoredDataAsync();
+
+ public Task TseFactoryReset(bool throwException = true);
+ public Task IsV2Async();
}
}
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeFunctionPointer.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeFunctionPointer.cs
index 4b257438e..04636eb23 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeFunctionPointer.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeFunctionPointer.cs
@@ -174,45 +174,11 @@ @param[in] info Info reference
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int worm_info_isDataImportInProgress(IntPtr info);
- /* Returns whether the initial PUK has been changed.
+ // Removed in Swissbit TSE API v6.0.0:
+ // - worm_info_hasChangedPuk
+ // - worm_info_hasChangedAdminPin
+ // - worm_info_hasChangedTimeAdminPin
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedPuk(const WormInfo* info);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate int worm_info_hasChangedPuk(IntPtr info);
-
- /* Returns whether the initial *Admin* PIN has been changed.
-
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedAdminPin(const WormInfo* info);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate int worm_info_hasChangedAdminPin(IntPtr info);
-
-
-
-
-
- /* Returns whether the initial *TimeAdmin* PIN has been changed.
-
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedTimeAdminPin(const WormInfo* info);
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate int worm_info_hasChangedTimeAdminPin(IntPtr info);
-
-
- /* Returns the timeout in seconds after which the next self test must be run.
-
- If this reaches 0, all following commands will fail until the self test is
- executed again.
-
- @param[in] info Info reference
- @returns the timeout
- @see @ref worm_tse_runSelfTest
- */
- //WORMAPI uint32_t WORMAPI_CALL worm_info_timeUntilNextSelfTest(const WormInfo* info);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate UInt32 worm_info_timeUntilNextSelfTest(IntPtr info);
@@ -2108,9 +2074,7 @@ is allowed. */
public worm_info_isExportEnabledIfCspTestFails func_worm_info_isExportEnabledIfCspTestFails { get; set; }
public worm_info_initializationState func_worm_info_initializationState { get; set; }
public worm_info_isDataImportInProgress func_worm_info_isDataImportInProgress { get; set; }
- public worm_info_hasChangedPuk func_worm_info_hasChangedPuk { get; set; }
- public worm_info_hasChangedAdminPin func_worm_info_hasChangedAdminPin { get; set; }
- public worm_info_hasChangedTimeAdminPin func_worm_info_hasChangedTimeAdminPin { get; set; }
+ // Removed in v6.0.0: func_worm_info_hasChangedPuk, func_worm_info_hasChangedAdminPin, func_worm_info_hasChangedTimeAdminPin
public worm_info_timeUntilNextSelfTest func_worm_info_timeUntilNextSelfTest { get; set; }
public worm_info_startedTransactions func_worm_info_startedTransactions { get; set; }
public worm_info_maxStartedTransactions func_worm_info_maxStartedTransactions { get; set; }
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeWormAPI.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeWormAPI.cs
index f986c67a8..b47b173e8 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeWormAPI.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/NativeWormAPI.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using fiskaltrust.Middleware.SCU.DE.Swissbit.Interop;
namespace fiskaltrust.Middleware.SCU.DE.Swissbit.Interop
{
@@ -172,40 +173,11 @@ @param[in] info Info reference
[DllImport("WormAPI")]
internal static extern int worm_info_isDataImportInProgress(IntPtr info);
- /* Returns whether the initial PUK has been changed.
+ // Removed in Swissbit TSE API v6.0.0:
+ // - worm_info_hasChangedPuk
+ // - worm_info_hasChangedAdminPin
+ // - worm_info_hasChangedTimeAdminPin
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedPuk(const WormInfo* info);
- [DllImport("WormAPI")]
- internal static extern int worm_info_hasChangedPuk(IntPtr info);
-
- /* Returns whether the initial *Admin* PIN has been changed.
-
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedAdminPin(const WormInfo* info);
- [DllImport("WormAPI")]
- internal static extern int worm_info_hasChangedAdminPin(IntPtr info);
-
- /* Returns whether the initial *TimeAdmin* PIN has been changed.
-
- @param[in] info Info reference
- */
- //WORMAPI int WORMAPI_CALL worm_info_hasChangedTimeAdminPin(const WormInfo* info);
- [DllImport("WormAPI")]
- internal static extern int worm_info_hasChangedTimeAdminPin(IntPtr info);
-
- /* Returns the timeout in seconds after which the next self test must be run.
-
- If this reaches 0, all following commands will fail until the self test is
- executed again.
-
- @param[in] info Info reference
- @returns the timeout
- @see @ref worm_tse_runSelfTest
- */
- //WORMAPI uint32_t WORMAPI_CALL worm_info_timeUntilNextSelfTest(const WormInfo* info);
[DllImport("WormAPI")]
internal static extern uint worm_info_timeUntilNextSelfTest(IntPtr info);
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/SwissbitProxy.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/SwissbitProxy.cs
index c15c24fe3..1e0167c0d 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/SwissbitProxy.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/Interop/SwissbitProxy.cs
@@ -114,7 +114,13 @@ public async Task CleanupAsync(bool throwException = false)
{
await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
{
+ if (context == IntPtr.Zero)
+ {
+ return;
+ }
+
var result = _nativeFunctionPointer.func_worm_cleanup(context);
+ context = IntPtr.Zero;
if (throwException)
{
result.ThrowIfError();
@@ -156,9 +162,6 @@ public async Task GetTseStatusAsync()
CreatedSignatures = _nativeFunctionPointer.func_worm_info_createdSignatures(infoPtr),
HardwareVersion = _nativeFunctionPointer.func_worm_info_hardwareVersion(infoPtr),
SoftwareVersion = _nativeFunctionPointer.func_worm_info_softwareVersion(infoPtr),
- HasChangedAdminPin = _nativeFunctionPointer.func_worm_info_hasChangedAdminPin(infoPtr) != 0,
- HasChangedPuk = _nativeFunctionPointer.func_worm_info_hasChangedPuk(infoPtr) != 0,
- HasChangedTimeAdminPin = _nativeFunctionPointer.func_worm_info_hasChangedTimeAdminPin(infoPtr) != 0,
HasPassedSelfTest = _nativeFunctionPointer.func_worm_info_hasPassedSelfTest(infoPtr) != 0,
HasValidTime = _nativeFunctionPointer.func_worm_info_hasValidTime(infoPtr) != 0,
initializationState = _nativeFunctionPointer.func_worm_info_initializationState(infoPtr),
@@ -182,7 +185,8 @@ public async Task GetTseStatusAsync()
_logger.LogTrace("GetTseStatusAsync: Reading customization identifier from TSE..");
_nativeFunctionPointer.func_worm_info_customizationIdentifier(infoPtr, ref idPtr, idLengthPtr);
- status.CustomizationIdentifier = Marshal.PtrToStringAnsi(idPtr, Marshal.ReadInt16(idLengthPtr));
+ // Fix: ReadInt32 instead of ReadInt16 — the C API declares int* idLength (4 bytes)
+ status.CustomizationIdentifier = Marshal.PtrToStringAnsi(idPtr, Marshal.ReadInt32(idLengthPtr));
_logger.LogTrace("GetTseStatusAsync: Reading public key from TSE..");
_nativeFunctionPointer.func_worm_info_tsePublicKey(infoPtr, ref publicKeyPtr, publicKeyLengthPtr);
@@ -249,8 +253,20 @@ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
clientIdPtr)
.ThrowIfError();
- _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_ADMIN).ThrowIfError();
- _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_TIME_ADMIN).ThrowIfError();
+ // In Swissbit TSE API v6.0.0, worm_tse_setup no longer leaves users
+ // logged in after completion. Ignore logout failures to stay compatible
+ // with both old and new SDK versions.
+ var adminLogout = _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_ADMIN);
+ if (adminLogout != WormError.WORM_ERROR_NOERROR && adminLogout != WormError.WORM_ERROR_AUTHENTICATION_USER_NOT_LOGGED_IN)
+ {
+ adminLogout.ThrowIfError();
+ }
+
+ var timeAdminLogout = _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_TIME_ADMIN);
+ if (timeAdminLogout != WormError.WORM_ERROR_NOERROR && timeAdminLogout != WormError.WORM_ERROR_AUTHENTICATION_USER_NOT_LOGGED_IN)
+ {
+ timeAdminLogout.ThrowIfError();
+ }
}
finally
{
@@ -290,7 +306,8 @@ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
}
finally
{
- _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_ADMIN);
+ // Fix: was logging out WORM_USER_ADMIN but logged in WORM_USER_TIME_ADMIN
+ _nativeFunctionPointer.func_worm_user_logout(context, WormUserId.WORM_USER_TIME_ADMIN);
}
});
}
@@ -351,6 +368,19 @@ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
});
}
+ public async Task TseFactoryReset(bool throwException = true)
+ {
+ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
+ {
+ var error = _nativeFunctionPointer.func_worm_tse_factoryReset(context);
+ if (throwException)
+ {
+ error.ThrowIfError();
+ }
+
+ });
+ }
+
public async Task> TseGetRegisteredClientsAsync()
{
return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
@@ -405,7 +435,8 @@ public async Task TransactionStartAsync(string clientId, by
return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
{
var clientIdPtr = Marshal.StringToHGlobalAnsi(clientId);
- var processDataPtr = Marshal.AllocHGlobal(processData.Length);
+ // Fix: guard against zero-length allocation — AllocHGlobal(0) is platform-dependent
+ var processDataPtr = Marshal.AllocHGlobal(Math.Max(processData.Length, 1));
var processTypePtr = Marshal.StringToHGlobalAnsi(processType);
var transactionResponsePtr = IntPtr.Zero;
var serialNumberPtr = new IntPtr();
@@ -414,7 +445,10 @@ public async Task TransactionStartAsync(string clientId, by
var signatureLengthPtr = Marshal.AllocHGlobal(sizeof(UInt64));
try
{
- Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ if (processData.Length > 0)
+ {
+ Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ }
transactionResponsePtr = _nativeFunctionPointer.func_worm_transaction_response_new(context);
@@ -470,7 +504,8 @@ public async Task TransactionUpdateAsync(string clientId, U
return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
{
var clientIdPtr = Marshal.StringToHGlobalAnsi(clientId);
- var processDataPtr = Marshal.AllocHGlobal(processData.Length);
+ // Fix: guard against zero-length allocation
+ var processDataPtr = Marshal.AllocHGlobal(Math.Max(processData.Length, 1));
var processTypePtr = Marshal.StringToHGlobalAnsi(processType);
var transactionResponsePtr = IntPtr.Zero;
var serialNumberPtr = new IntPtr();
@@ -479,7 +514,10 @@ public async Task TransactionUpdateAsync(string clientId, U
var signatureLengthPtr = Marshal.AllocHGlobal(sizeof(UInt64));
try
{
- Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ if (processData.Length > 0)
+ {
+ Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ }
transactionResponsePtr = _nativeFunctionPointer.func_worm_transaction_response_new(context);
@@ -538,7 +576,8 @@ public async Task TransactionFinishAsync(string clientId, U
return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
{
var clientIdPtr = Marshal.StringToHGlobalAnsi(clientId);
- var processDataPtr = Marshal.AllocHGlobal(processData.Length);
+ // Fix: guard against zero-length allocation
+ var processDataPtr = Marshal.AllocHGlobal(Math.Max(processData.Length, 1));
var processTypePtr = Marshal.StringToHGlobalAnsi(processType);
var transactionResponsePtr = IntPtr.Zero;
var serialNumberPtr = new IntPtr();
@@ -547,7 +586,10 @@ public async Task TransactionFinishAsync(string clientId, U
var signatureLengthPtr = Marshal.AllocHGlobal(sizeof(UInt64));
try
{
- Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ if (processData.Length > 0)
+ {
+ Marshal.Copy(processData, 0, processDataPtr, processData.Length);
+ }
transactionResponsePtr = _nativeFunctionPointer.func_worm_transaction_response_new(context);
@@ -643,17 +685,23 @@ public async Task ExportTarAsync(Stream stream)
{
await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
{
+ // Fix: store delegate in local variable to prevent GC collection during native call
+ var callbackDelegate = new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
+ {
+ var chunkBytes = new byte[chunkLength];
+ Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
+ stream.Write(chunkBytes, 0, (int) chunkLength);
+ return WormError.WORM_ERROR_NOERROR;
+ });
+ var callbackPtr = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
+
_nativeFunctionPointer.func_worm_export_tar(
context,
- Marshal.GetFunctionPointerForDelegate(new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
- {
- var chunkBytes = new byte[chunkLength];
- Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
- stream.Write(chunkBytes, 0, (int) chunkLength);
- return WormError.WORM_ERROR_NOERROR;
- })),
+ callbackPtr,
IntPtr.Zero)
.ThrowIfError();
+
+ GC.KeepAlive(callbackDelegate);
});
}
@@ -674,13 +722,15 @@ public async Task ExportTarIncrementalAsync(Stream stream, byte[] lastSt
var lastSignatureCounterPtr = Marshal.AllocHGlobal(sizeof(UInt64));
try
{
- var callback = Marshal.GetFunctionPointerForDelegate(new WormExportTarIncrementalCallback((IntPtr chunk, uint chunkLength, UInt32 processedBlocks, UInt32 totalBlocks, IntPtr callbackData) =>
+ // Fix: store delegate in local variable to prevent GC collection during native call
+ var callbackDelegate = new WormExportTarIncrementalCallback((IntPtr chunk, uint chunkLength, UInt32 processedBlocks, UInt32 totalBlocks, IntPtr callbackData) =>
{
var chunkBytes = new byte[chunkLength];
Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
stream.Write(chunkBytes, 0, (int) chunkLength);
return WormError.WORM_ERROR_NOERROR;
- }));
+ });
+ var callback = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
if (lastStateBytes.Length == 0)
{
@@ -704,6 +754,8 @@ public async Task ExportTarIncrementalAsync(Stream stream, byte[] lastSt
.ThrowIfError();
}
+ GC.KeepAlive(callbackDelegate);
+
var newStateBytes = new byte[stateSize];
Marshal.Copy(newStatePtr, newStateBytes, 0, stateSize);
return newStateBytes;
@@ -723,17 +775,20 @@ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
var clientIdPtr = Marshal.StringToHGlobalAnsi(clientId);
try
{
-
- var callback = Marshal.GetFunctionPointerForDelegate(new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
- {
- var chunkBytes = new byte[chunkLength];
- Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
- stream.Write(chunkBytes, 0, (int) chunkLength);
- return WormError.WORM_ERROR_NOERROR;
- }));
+ // Fix: store delegate in local variable to prevent GC collection during native call
+ var callbackDelegate = new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
+ {
+ var chunkBytes = new byte[chunkLength];
+ Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
+ stream.Write(chunkBytes, 0, (int) chunkLength);
+ return WormError.WORM_ERROR_NOERROR;
+ });
+ var callback = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
_nativeFunctionPointer.func_worm_export_tar_filtered_time(context, startDateUnixTime, endDateUnixTime, clientIdPtr, callback, IntPtr.Zero)
.ThrowIfError();
+
+ GC.KeepAlive(callbackDelegate);
}
finally
{
@@ -749,15 +804,19 @@ await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
var clientIdPtr = Marshal.StringToHGlobalAnsi(clientId);
try
{
- var callback = Marshal.GetFunctionPointerForDelegate(new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
+ // Fix: store delegate in local variable to prevent GC collection during native call
+ var callbackDelegate = new WormExportTarCallback((IntPtr chunk, uint chunkLength, IntPtr callbackData) =>
{
var chunkBytes = new byte[chunkLength];
Marshal.Copy(chunk, chunkBytes, 0, (int) chunkLength);
stream.Write(chunkBytes, 0, (int) chunkLength);
return WormError.WORM_ERROR_NOERROR;
- }));
+ });
+ var callback = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
_nativeFunctionPointer.func_worm_export_tar_filtered_transaction(context, startTransactionNumber, endTransactionNumber, clientIdPtr, callback, IntPtr.Zero).ThrowIfError();
+
+ GC.KeepAlive(callbackDelegate);
}
finally
{
@@ -796,25 +855,91 @@ public async Task GetLogMessageCertificateAsync()
public async Task DeleteStoredDataAsync() => await _lockingHelper.PerformWithLock(_hwSemaphore, () => _nativeFunctionPointer.func_worm_export_deleteStoredData(context).ThrowIfError());
+ // Fix: added lock, proper worm_info_free, and try/finally for all three methods below
public async Task HasValidTimeAsync()
{
- var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
- _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
- return await Task.FromResult(_nativeFunctionPointer.func_worm_info_hasValidTime(infoPtr) != 0);
+ return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
+ {
+ var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
+ try
+ {
+ _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
+ return _nativeFunctionPointer.func_worm_info_hasValidTime(infoPtr) != 0;
+ }
+ finally
+ {
+ if (infoPtr != IntPtr.Zero)
+ {
+ _nativeFunctionPointer.func_worm_info_free(infoPtr);
+ }
+ }
+ });
}
public async Task HasPassedSelfTestAsync()
{
- var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
- _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
- return await Task.FromResult(_nativeFunctionPointer.func_worm_info_hasPassedSelfTest(infoPtr) != 0);
+ return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
+ {
+ var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
+ try
+ {
+ _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
+ return _nativeFunctionPointer.func_worm_info_hasPassedSelfTest(infoPtr) != 0;
+ }
+ finally
+ {
+ if (infoPtr != IntPtr.Zero)
+ {
+ _nativeFunctionPointer.func_worm_info_free(infoPtr);
+ }
+ }
+ });
}
public async Task GetInitializationState()
{
- var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
- _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
- return await Task.FromResult(_nativeFunctionPointer.func_worm_info_initializationState(infoPtr).ToTseStates());
+ return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
+ {
+ var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
+ try
+ {
+ _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
+ return _nativeFunctionPointer.func_worm_info_initializationState(infoPtr).ToTseStates();
+ }
+ finally
+ {
+ if (infoPtr != IntPtr.Zero)
+ {
+ _nativeFunctionPointer.func_worm_info_free(infoPtr);
+ }
+ }
+ });
+ }
+
+ public async Task IsV2Async()
+ {
+ return await _lockingHelper.PerformWithLock(_hwSemaphore, () =>
+ {
+ var infoPtr = _nativeFunctionPointer.func_worm_info_new(context);
+ try
+ {
+ _nativeFunctionPointer.func_worm_info_read(infoPtr).ThrowIfError();
+
+ // Software version is encoded as: 2 bytes major | 1 byte minor | 1 byte patch
+ var softwareVersion = _nativeFunctionPointer.func_worm_info_softwareVersion(infoPtr);
+ var major = (softwareVersion >> 16) & 0xFFFF;
+
+ // Swissbit V2 TSEs use major version >= 2
+ return major >= 2;
+ }
+ finally
+ {
+ if (infoPtr != IntPtr.Zero)
+ {
+ _nativeFunctionPointer.func_worm_info_free(infoPtr);
+ }
+ }
+ });
}
public void Dispose()
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCU.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCU.cs
index 2dd31258c..afe7c858c 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCU.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCU.cs
@@ -42,9 +42,8 @@ public sealed class SwissbitSCU : IDESSCD, IDisposable
private TimeSpan _selftestInterval = TimeSpan.FromHours(24);
private uint _hwSelftestIntervalSeconds = 0;
private ISwissbitProxy _proxy = null;
- private DateTime _nextSyncTime;
- // Never change these values, as all existing installations are depending on them
+ // Never change these values, as all existing installations are dependent on them
private readonly byte[] _adminPuk = Encoding.ASCII.GetBytes("123456");
private readonly byte[] _seed = Encoding.ASCII.GetBytes("SwissbitSwissbit");
@@ -59,7 +58,9 @@ public SwissbitSCU(SwissbitSCUConfiguration configuration, INativeFunctionPointe
EnsureDevicePathIsCorrect();
- _selftestTimer = new Timer(SelftestCallback, null, Timeout.Infinite, Timeout.Infinite);
+ // Create the timer first (disabled) so SelftestAsync can call _selftestTimer.Change()
+ // during initialization without a NullReferenceException.
+ _selftestTimer = new Timer(SelftestCallback, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
try
{
@@ -75,6 +76,12 @@ public SwissbitSCU(SwissbitSCUConfiguration configuration, INativeFunctionPointe
_logger.LogCritical(ex, "An error occured while initializing the Swissbit SCU.");
throw;
}
+
+ // At this point, SelftestAsync (called by ReadTseInfoAsync inside GetProxy) has already
+ // updated _selftestInterval from the TSE's TimeUntilNextSelfTest. If SelftestAsync
+ // successfully called _selftestTimer.Change(), the timer is already running with the
+ // correct interval. Otherwise, kick it off now with whatever interval we have.
+ _selftestTimer.Change(_selftestInterval, _selftestInterval);
}
private ISwissbitProxy GetProxy([CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0)
@@ -87,14 +94,14 @@ private ISwissbitProxy GetProxy([CallerMemberName] string memberName = "", [Call
{
try
{
- //maybe move to proxy
if (!Directory.Exists(_configuration.DevicePath))
{
throw new SwissbitException($"The Swissbit TSE cannot be found at {_configuration.DevicePath}.");
}
- _logger.LogDebug("Try to initialize SwissbitProxy with devicePath {_configuration.DevicePath}", _configuration.DevicePath);
+ _logger.LogDebug($"Try to initialize SwissbitProxy with devicePath {_configuration.DevicePath}", _configuration.DevicePath);
_proxy = new SwissbitProxy(_configuration.DevicePath, Encoding.ASCII.GetBytes(_configuration.AdminPin), Encoding.ASCII.GetBytes(_configuration.TimeAdminPin), _nativeFunctionPointerFactory, _lockingHelper, _logger);
+
ReadTseInfoAsync(_proxy, true).GetAwaiter().GetResult();
}
catch (Exception ex)
@@ -178,14 +185,10 @@ private async Task UpdateTimeAsync(ISwissbitProxy proxy, bool force = false)
{
_logger.LogWarning("Tried to call UpdateTime, but the Ctss Interface hasn´t been activated.");
}
- else if (!tseStatus.HasChangedTimeAdminPin)
- {
- _logger.LogWarning("Tried to call UpdateTime, but the TimeAdmin Pin has never been changed.");
- }
else
{
await proxy.TseUpdateTimeAsync();
- _nextSyncTime = currentTime.AddSeconds(tseStatus.MaxTimeSynchronizationDelay);
+ currentTime.AddSeconds(tseStatus.MaxTimeSynchronizationDelay);
}
}
@@ -204,6 +207,20 @@ private async void SelftestCallback(object state)
private async Task SelftestAsync(ISwissbitProxy proxy, bool force = false)
{
+ var tseState = await proxy.GetInitializationState();
+
+ if (tseState == TseStates.Uninitialized)
+ {
+ try
+ {
+ await proxy.TseSetupAsync(_seed, _adminPuk, Encoding.ASCII.GetBytes(_configuration.AdminPin), Encoding.ASCII.GetBytes(_configuration.TimeAdminPin));
+ }
+ catch (SwissbitException ex)
+ {
+ _logger.LogDebug(ex, "TseSetup skipped: TSE already initialized.");
+ }
+ }
+
if (!force && await proxy.HasPassedSelfTestAsync())
{
_logger.LogDebug("Self test already passed, skipping.");
@@ -290,12 +307,9 @@ private async Task ReadTseInfoAsync(ISwissbitProxy proxy, bool initLibrary = fal
_logger.LogDebug("Initializing WORM library..");
await proxy.InitAsync();
await SelftestAsync(proxy);
+
await UpdateTimeAsync(proxy);
- if (await proxy.UpdateFirmwareAsync(_configuration.EnableFirmwareUpdate))
- {
- await SelftestAsync(proxy);
- }
await UpdateTimeAsync(GetProxy());
}
@@ -307,7 +321,6 @@ private async Task ReadTseInfoAsync(ISwissbitProxy proxy, bool initLibrary = fal
const long blockSize = 0x200; //512 byte
-
var softwareVersion = ConvertToVersion((int) status.SoftwareVersion);
var hardwareVersion = ConvertToVersion((int) status.HardwareVersion);
@@ -571,14 +584,7 @@ public async Task FinishTransactionAsync(FinishTransa
{
// If the TSE log memory is too full, this call takes too long, and transactions cannot be canceled anymore - basically creating a deadlock.
// Thus, we skip reading the timestamp of the start-transaction and fall-back to the one of the finish-transaction.
- if (!IsCancellationTransaction(request) || LastTseInfo?.CurrentLogMemorySize < _configuration.TooLargeToExportThreshold)
- {
- startTransactionTimeStamp = await GetStartTransactionTimeStamp(GetProxy(), request.TransactionNumber);
- }
- else
- {
- _logger.LogWarning("Could not set StartTransactionTimestamp, as the TSE's log storage is too full to extract it. Used finish-transaction log time as fallback value.");
- }
+ startTransactionTimeStamp = await GetStartTransactionTimeStamp(GetProxy(), request.TransactionNumber);
}
var response = new FinishTransactionResponse()
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCUConfiguration.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCUConfiguration.cs
index c829e8644..ffd3c2fc5 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCUConfiguration.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/SwissbitSCUConfiguration.cs
@@ -7,7 +7,6 @@ public class SwissbitSCUConfiguration
public string TimeAdminPin { get; set; } = "98765";
public bool EnableTarFileExport { get; set; } = true;
public int TooLargeToExportThreshold { get; set; } = 100 * 1024 * 1024; // 100 MB
- public bool EnableFirmwareUpdate { get; set; } = false;
public string NativeLibArch { get; set; }
public bool StoreTemporaryExportFiles { get; set; } = false;
public string ServiceFolder { get; set; }
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/WormLibraryManager.cs b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/WormLibraryManager.cs
index e3c2c071c..bbc9e5afa 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/WormLibraryManager.cs
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/WormLibraryManager.cs
@@ -10,7 +10,7 @@ public static class WormLibraryManager
private const string LINUX_LIB = "libWormAPI.so";
private const string WINDOWS_LIB = "WormAPI.dll";
- private const string PATH_RUNTIMES = "runtimes";
+ private const string PATH_RUNTIMES = "runtimesv2";
private const string PATH_NATIVE = "native";
private static readonly string _win32LibraryFile = Path.Combine(PATH_RUNTIMES, "win-x86", PATH_NATIVE, WINDOWS_LIB);
@@ -27,7 +27,23 @@ public static void CopyLibraryToWorkingDirectory(SwissbitSCUConfiguration config
: SelectPathBasedOnArchitecture();
var currentDirectory = Path.GetDirectoryName(Assembly.GetAssembly(typeof(SwissbitSCU)).Location);
- File.Copy(Path.Combine(currentDirectory, libraryFile), Path.Combine(currentDirectory, Path.GetFileName(libraryFile)), true);
+ var source = Path.Combine(currentDirectory, libraryFile);
+ var destination = Path.Combine(currentDirectory, Path.GetFileName(libraryFile));
+
+ try
+ {
+ File.Copy(source, destination, true);
+ }
+ catch (IOException)
+ {
+ // The native library is already loaded by another process or a previous
+ // instance. If the file already exists we can safely skip the copy,
+ // since it will be loaded from the existing location.
+ if (!File.Exists(destination))
+ {
+ throw;
+ }
+ }
}
private static string SelectPathBasedOnArchitecture()
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/fiskaltrust.Middleware.SCU.DE.Swissbit.csproj b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/fiskaltrust.Middleware.SCU.DE.Swissbit.csproj
index 34ee64ea5..cdfa8c41e 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/fiskaltrust.Middleware.SCU.DE.Swissbit.csproj
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/fiskaltrust.Middleware.SCU.DE.Swissbit.csproj
@@ -28,7 +28,7 @@
-
+
PreserveNewest
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm/native/libWormAPI.so
deleted file mode 100644
index 711202622..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm/native/libWormAPI.so and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm64/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm64/native/libWormAPI.so
deleted file mode 100644
index 376958637..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-arm64/native/libWormAPI.so and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-armel/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-armel/native/libWormAPI.so
deleted file mode 100644
index 711202622..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-armel/native/libWormAPI.so and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x64/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x64/native/libWormAPI.so
deleted file mode 100644
index 14105ce41..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x64/native/libWormAPI.so and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x86/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x86/native/libWormAPI.so
deleted file mode 100644
index f495a4bf3..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/linux-x86/native/libWormAPI.so and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x64/native/WormAPI.dll b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x64/native/WormAPI.dll
deleted file mode 100644
index ed86558e6..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x64/native/WormAPI.dll and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x86/native/WormAPI.dll b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x86/native/WormAPI.dll
deleted file mode 100644
index ebffc5983..000000000
Binary files a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimes/win-x86/native/WormAPI.dll and /dev/null differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm/native/libWormAPI.so
new file mode 100644
index 000000000..6712fe521
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm/native/libWormAPI.so differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm64/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm64/native/libWormAPI.so
new file mode 100644
index 000000000..ceb51040b
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-arm64/native/libWormAPI.so differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x64/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x64/native/libWormAPI.so
new file mode 100644
index 000000000..76d3f280c
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x64/native/libWormAPI.so differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x86/native/libWormAPI.so b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x86/native/libWormAPI.so
new file mode 100644
index 000000000..a6673278f
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/linux-x86/native/libWormAPI.so differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x64/native/WormAPI.dll b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x64/native/WormAPI.dll
new file mode 100644
index 000000000..2e9a28569
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x64/native/WormAPI.dll differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x86/native/WormAPI.dll b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x86/native/WormAPI.dll
new file mode 100644
index 000000000..d57184011
Binary files /dev/null and b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/runtimesv2/win-x86/native/WormAPI.dll differ
diff --git a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/version.json b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/version.json
index 260ae598c..e97cc45f6 100644
--- a/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/version.json
+++ b/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit/version.json
@@ -4,10 +4,10 @@
"/scu-de/src/fiskaltrust.Middleware.SCU.DE.Swissbit"
],
"publicReleaseRefSpec": [
- "^refs/tags/scu-de/swissbit/v.+"
+ "^refs/tags/scu-de/Swissbit/v.+"
],
"release": {
- "tagName": "scu-de/swissbit/v{version}"
+ "tagName": "scu-de/Swissbit/v{version}"
},
"inherit": true
}
\ No newline at end of file
diff --git a/scu-de/test/Manual/fiskaltrust.Middleware.SCU.DE.Test.Launcher/Program.cs b/scu-de/test/Manual/fiskaltrust.Middleware.SCU.DE.Test.Launcher/Program.cs
index e3024db51..53f6c8007 100644
--- a/scu-de/test/Manual/fiskaltrust.Middleware.SCU.DE.Test.Launcher/Program.cs
+++ b/scu-de/test/Manual/fiskaltrust.Middleware.SCU.DE.Test.Launcher/Program.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Runtime.InteropServices;
using fiskaltrust.ifPOS.v1.de;
using fiskaltrust.Middleware.Queue.Test.Launcher.Helpers;
using fiskaltrust.Middleware.SCU.DE.Test.Launcher.Helpers;
@@ -14,7 +15,7 @@ public static class Program
{
/* in vs 2026 use Debug - Attach to Process and select testlauncher after starting it
*/
- private static readonly bool useHelipad = false;
+ private static readonly bool useHelipad = true;
private static readonly string cashBoxId = "";
private static readonly string accessToken = "";
private static readonly string fccDirectory = "";
@@ -22,6 +23,9 @@ public static class Program
private static readonly string configurationFilePath = "";
private static readonly string serviceFolder = Directory.GetCurrentDirectory();
+ // Set to true to perform a factory reset on the Swissbit TSE before startup (development TSEs only)
+ private static readonly bool performFactoryReset = false;
+
public static void Main()
{
ftCashBoxConfiguration cashBoxConfiguration;
@@ -60,8 +64,26 @@ public static void Main()
var serviceCollection = new ServiceCollection();
serviceCollection.AddStandardLoggers(LogLevel.Debug);
+ //only SWISSBIT usb testversion
if (config.Package == "fiskaltrust.Middleware.SCU.DE.Swissbit")
{
+ if (performFactoryReset)
+ {
+ var devicePath = config.Configuration.ContainsKey("devicePath")
+ ? config.Configuration["devicePath"]?.ToString()
+ : config.Configuration.ContainsKey("DevicePath")
+ ? config.Configuration["DevicePath"]?.ToString()
+ : null;
+
+ if (string.IsNullOrEmpty(devicePath))
+ {
+ Console.WriteLine("ERROR: Cannot perform factory reset - devicePath not found in configuration.");
+ }
+ else
+ {
+ PerformSwissbitFactoryReset(devicePath);
+ }
+ }
ConfigureSwissbit(config, serviceCollection);
}else if (config.Package == "fiskaltrust.Middleware.SCU.DE.SwissbitCloud")
{
@@ -99,6 +121,43 @@ public static void Main()
Console.ReadLine();
}
+ private static void PerformSwissbitFactoryReset(string devicePath)
+ {
+ Console.WriteLine($"Performing TSE factory reset on device: {devicePath}");
+ Console.WriteLine("WARNING: This will erase all data on the TSE. Only works on development TSEs.");
+
+ var nativeFunctions = new Swissbit.Interop.FunctionPointerFactory().LoadLibrary();
+
+ var context = IntPtr.Zero;
+ var mountPointPtr = Marshal.StringToHGlobalAnsi(devicePath);
+ try
+ {
+ var initError = nativeFunctions.func_worm_init(ref context, mountPointPtr);
+ if (initError != Swissbit.Interop.NativeFunctionPointer.WormError.WORM_ERROR_NOERROR)
+ {
+ Console.WriteLine($"ERROR: worm_init failed with error {initError}.");
+ return;
+ }
+
+ var resetError = nativeFunctions.func_worm_tse_factoryReset(context);
+ if (resetError != Swissbit.Interop.NativeFunctionPointer.WormError.WORM_ERROR_NOERROR)
+ {
+ Console.WriteLine($"ERROR: worm_tse_factoryReset failed with error {resetError}.");
+ return;
+ }
+
+ Console.WriteLine("TSE factory reset completed successfully.");
+ }
+ finally
+ {
+ if (context != IntPtr.Zero)
+ {
+ nativeFunctions.func_worm_cleanup(context);
+ }
+ Marshal.FreeHGlobal(mountPointPtr);
+ }
+ }
+
private static ftCashBoxConfiguration GetDemoConfiguration()
{
var cashBoxConfiguration = new ftCashBoxConfiguration(Guid.NewGuid())