Skip to content

Commit 33748fc

Browse files
authored
Merge pull request #133 from dorssel/ephemeral
Refactor ephemeral keys
2 parents 9045ae6 + 9aec27f commit 33748fc

File tree

11 files changed

+114
-152
lines changed

11 files changed

+114
-152
lines changed

.github/linters/.mega-linter.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ DISABLE_LINTERS:
2121
- CSHARP_ROSLYNATOR # Not compatible with .NET 8 (yet)
2222
- JSON_V8R # Too many missing/obsolete schemas
2323
- REPOSITORY_TRIVY # Unstable, leading to a lot of build failures
24-
- MARKDOWN_MARKDOWN_LINK_CHECK # TODO: remove once repository goes public
2524
SHOW_ELAPSED_TIME: true
2625
FILEIO_REPORTER: false
2726
# DISABLE_ERRORS: true # Uncomment if you want MegaLinter to detect errors but not block CI to pass

Documentation/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ SPDX-License-Identifier: MIT
88

99
## Under construction
1010

11+
<!-- markdown-link-check-disable-next-line -->
1112
Already available: [XMSS WebAssembly Demo](wasm-example/).
1213

1314
> [!NOTE]

Examples/ConsoleApp/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static async Task Main()
3535
Console.WriteLine("Generating new key...");
3636

3737
using var xmss = new Xmss();
38-
xmss.GeneratePrivateKey(new XmssEphemeralStateManager(), XmssParameterSet.XMSS_SHA2_10_256, false);
38+
xmss.GeneratePrivateKey(null, XmssParameterSet.XMSS_SHA2_10_256, false);
3939
{
4040
// this is special for XMSS, a long-running (cancelable) process
4141

UnitTests/UnitTests/CalculateTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ namespace UnitTests;
99
[TestClass]
1010
sealed class CalculateTests
1111
{
12+
[TestMethod]
13+
public async Task CalculatePublicKeyAsync_Ephemeral_AndSign()
14+
{
15+
using var xmss = new Xmss();
16+
17+
Assert.IsFalse(xmss.HasPrivateKey);
18+
Assert.IsFalse(xmss.HasPublicKey);
19+
20+
xmss.GeneratePrivateKey(null, XmssParameterSet.XMSS_SHA2_10_256, true);
21+
22+
Assert.IsTrue(xmss.HasPrivateKey);
23+
Assert.IsFalse(xmss.HasPublicKey);
24+
25+
await xmss.CalculatePublicKeyAsync();
26+
27+
Assert.IsTrue(xmss.HasPrivateKey);
28+
Assert.IsTrue(xmss.HasPublicKey);
29+
30+
_ = xmss.Sign([1, 2, 3]);
31+
}
32+
1233
[TestMethod]
1334
public async Task CalculatePublicKeyAsync_AndImport()
1435
{

UnitTests/UnitTests/GenerateTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ public void GeneratePrivateKey(XmssParameterSet parameterSet)
2929
Assert.IsFalse(xmss.HasPublicKey);
3030
}
3131

32+
[TestMethod]
33+
public void GeneratePrivateKey_Ephemeral()
34+
{
35+
using var xmss = new Xmss();
36+
37+
Assert.IsFalse(xmss.HasPrivateKey);
38+
Assert.IsFalse(xmss.HasPublicKey);
39+
40+
xmss.GeneratePrivateKey(null, XmssParameterSet.XMSS_SHA2_10_256, false);
41+
42+
Assert.IsTrue(xmss.HasPrivateKey);
43+
Assert.IsFalse(xmss.HasPublicKey);
44+
}
45+
3246
[TestMethod]
3347
public void GeneratePrivateKey_AfterPrivateKey()
3448
{

UnitTests/UnitTests/PartitionTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ public void SplitPrivateKey()
4646
Assert.AreEqual(oldRemaining - 100, xmss.SignaturesRemaining);
4747
}
4848

49+
[TestMethod]
50+
public void SplitPrivateKey_Ephemeral()
51+
{
52+
using var xmss = new Xmss();
53+
xmss.GeneratePrivateKey(null, XmssParameterSet.XMSS_SHA2_10_256, false);
54+
55+
Assert.ThrowsException<InvalidOperationException>(() =>
56+
{
57+
xmss.SplitPrivateKey(new MockStateManager(), 100);
58+
});
59+
}
60+
4961
[TestMethod]
5062
public void SplitPrivateKey_WithPublic()
5163
{
@@ -332,6 +344,24 @@ public void MergePartition()
332344
}
333345
}
334346

347+
[TestMethod]
348+
public void MergePartition_Ephemeral()
349+
{
350+
var stateManager = new MockStateManager();
351+
{
352+
using var tmpXmss = new Xmss();
353+
tmpXmss.GeneratePrivateKey(stateManager, XmssParameterSet.XMSS_SHA2_10_256, false);
354+
}
355+
356+
using var xmss = new Xmss();
357+
xmss.GeneratePrivateKey(null, XmssParameterSet.XMSS_SHA2_10_256, false);
358+
359+
Assert.ThrowsException<InvalidOperationException>(() =>
360+
{
361+
xmss.MergePartition(stateManager);
362+
});
363+
}
364+
335365
[TestMethod]
336366
public void MergePartition_DeleteFails()
337367
{

UnitTests/UnitTests/XmssEphemeralStateManagerTests.cs

Lines changed: 0 additions & 98 deletions
This file was deleted.

Xmss/XmssPrivateKey.cs renamed to Xmss/PrivateKey.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
namespace Dorssel.Security.Cryptography;
88

9-
sealed class XmssPrivateKey(StateManagerWrapper wrappedStateManager) : IDisposable
9+
sealed class PrivateKey(WrappedStateManager wrappedStateManager) : IDisposable
1010
{
11-
readonly StateManagerWrapper _WrappedStateManager = wrappedStateManager;
11+
readonly WrappedStateManager _WrappedStateManager = wrappedStateManager;
1212
readonly CriticalXmssKeyContextHandle _KeyContext = new();
1313
readonly CriticalXmssPrivateKeyStatefulBlobHandle _StatefulBlob = new();
1414

15-
public IXmssStateManager WrappedStateManager
15+
public WrappedStateManager WrappedStateManager
1616
{
1717
get
1818
{

Xmss/StateManagerWrapper.cs renamed to Xmss/WrappedStateManager.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44

55
namespace Dorssel.Security.Cryptography;
66

7-
sealed class StateManagerWrapper(IXmssStateManager stateManager)
7+
sealed class WrappedStateManager(IXmssStateManager? stateManager)
88
: IXmssStateManager
99
{
10-
readonly IXmssStateManager StateManager = stateManager;
10+
readonly IXmssStateManager? StateManager = stateManager;
1111

1212
public void Store(XmssKeyPart part, ReadOnlySpan<byte> data)
1313
{
14+
if (StateManager is null)
15+
{
16+
// ephemeral key
17+
return;
18+
}
1419
try
1520
{
1621
StateManager.Store(part, data);
@@ -23,6 +28,11 @@ public void Store(XmssKeyPart part, ReadOnlySpan<byte> data)
2328

2429
public void StoreStatefulPart(ReadOnlySpan<byte> expected, ReadOnlySpan<byte> data)
2530
{
31+
if (StateManager is null)
32+
{
33+
// ephemeral key
34+
return;
35+
}
2636
try
2737
{
2838
StateManager.StoreStatefulPart(expected, data);
@@ -37,6 +47,11 @@ public void Load(XmssKeyPart part, Span<byte> destination)
3747
{
3848
try
3949
{
50+
if (StateManager is null)
51+
{
52+
// ephemeral key
53+
throw new NotImplementedException();
54+
}
4055
StateManager.Load(part, destination);
4156
}
4257
catch (Exception ex)
@@ -47,6 +62,11 @@ public void Load(XmssKeyPart part, Span<byte> destination)
4762

4863
public void DeletePublicPart()
4964
{
65+
if (StateManager is null)
66+
{
67+
// ephemeral key
68+
return;
69+
}
5070
try
5171
{
5272
StateManager.DeletePublicPart();
@@ -59,6 +79,11 @@ public void DeletePublicPart()
5979

6080
public void Purge()
6181
{
82+
if (StateManager is null)
83+
{
84+
// ephemeral key
85+
return;
86+
}
6287
try
6388
{
6489
StateManager.Purge();
@@ -80,4 +105,12 @@ public void PurgeAfterFailure(Exception exception)
80105
throw new AggregateException(exception, ex2);
81106
}
82107
}
108+
109+
public void ThrowIfEphemeralKey()
110+
{
111+
if (StateManager is null)
112+
{
113+
throw new InvalidOperationException("Ephemeral keys do not support partitioning.");
114+
}
115+
}
83116
}

Xmss/Xmss.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public static void RegisterWithCryptoConfig()
9696
public XmssParameterSet ParameterSet { get; private set; } = XmssParameterSet.None;
9797

9898
bool IsDisposed;
99-
XmssPrivateKey? PrivateKey;
99+
PrivateKey? PrivateKey;
100100
XmssPublicKey PublicKey;
101101

102102
void ResetState()
@@ -167,7 +167,7 @@ void ThrowIfNoPublicKey()
167167
#endregion
168168

169169
#region Private Key
170-
static void VerifyNoPrivateState(StateManagerWrapper wrappedStateManager)
170+
static void VerifyNoPrivateState(WrappedStateManager wrappedStateManager)
171171
{
172172
var partExists = false;
173173

@@ -202,13 +202,11 @@ static void VerifyNoPrivateState(StateManagerWrapper wrappedStateManager)
202202
/// <param name="stateManager">TODO</param>
203203
/// <param name="parameterSet">TODO</param>
204204
/// <param name="enableIndexObfuscation">TODO</param>
205-
public void GeneratePrivateKey(IXmssStateManager stateManager, XmssParameterSet parameterSet, bool enableIndexObfuscation)
205+
public void GeneratePrivateKey(IXmssStateManager? stateManager, XmssParameterSet parameterSet, bool enableIndexObfuscation)
206206
{
207-
ArgumentNullException.ThrowIfNull(stateManager);
208-
209207
ObjectDisposedException.ThrowIf(IsDisposed, this);
210208

211-
var wrappedStateManager = new StateManagerWrapper(stateManager);
209+
var wrappedStateManager = new WrappedStateManager(stateManager);
212210

213211
// Step 1: Verify that no (possibly valid) private parts exist for the new state.
214212

@@ -282,7 +280,7 @@ public void ImportPrivateKey(IXmssStateManager stateManager)
282280
ArgumentNullException.ThrowIfNull(stateManager);
283281
ObjectDisposedException.ThrowIf(IsDisposed, this);
284282

285-
var wrappedStateManager = new StateManagerWrapper(stateManager);
283+
var wrappedStateManager = new WrappedStateManager(stateManager);
286284

287285
XmssError result;
288286

@@ -360,8 +358,9 @@ public void SplitPrivateKey(IXmssStateManager newPartition, int newPartitionSize
360358

361359
ObjectDisposedException.ThrowIf(IsDisposed, this);
362360
ThrowIfNoPrivateKey();
361+
PrivateKey.WrappedStateManager.ThrowIfEphemeralKey();
363362

364-
var wrappedNewPartition = new StateManagerWrapper(newPartition);
363+
var wrappedNewPartition = new WrappedStateManager(newPartition);
365364

366365
// Step 1: Verify that no (possibly valid) private parts exist for the new state.
367366

@@ -457,8 +456,9 @@ public void MergePartition(IXmssStateManager consumedPartition)
457456

458457
ObjectDisposedException.ThrowIf(IsDisposed, this);
459458
ThrowIfNoPrivateKey();
459+
PrivateKey.WrappedStateManager.ThrowIfEphemeralKey();
460460

461-
var wrappedConsumedPartition = new StateManagerWrapper(consumedPartition);
461+
var wrappedConsumedPartition = new WrappedStateManager(consumedPartition);
462462

463463
using var consumedKeyStatefulBlob = CriticalXmssPrivateKeyStatefulBlobHandle.Alloc();
464464
wrappedConsumedPartition.Load(XmssKeyPart.PrivateStateful, consumedKeyStatefulBlob.Data);

0 commit comments

Comments
 (0)