Skip to content

Commit

Permalink
Small improvements
Browse files Browse the repository at this point in the history
- remove unnecessary version API
- add some XML documentation
- fix Home link in WebAssembly example
- refactor secure erase private blobs
  • Loading branch information
dorssel committed Jan 2, 2025
1 parent d8389f5 commit 4c17e67
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 98 deletions.
3 changes: 3 additions & 0 deletions .github/linters/vs-spell-exclusion.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ Codecov
Dorssel
hashsig
marshaller
navmenu
param
realloc
sitenav
stateful
xmss
zeroize
Xtended
4 changes: 0 additions & 4 deletions Examples/ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ static Program()

static async Task Main()
{
{
Console.WriteLine($"Native headers version: {Xmss.NativeHeadersVersion}");
Console.WriteLine($"Native library version: {Xmss.NativeLibraryVersion}");
}
{
var oid = CryptoConfig.MapNameToOID("XMSS");
Console.WriteLine($"Found OID for 'XMSS': {oid}");
Expand Down
2 changes: 1 addition & 1 deletion Examples/WebAssembly/Layout/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<label for="navmenu-toggle" class="navmenu-icon"><FluentIcon Value="@(new Icons.Regular.Size20.Navigation())" Color="Color.Fill" /></label>
<nav class="sitenav" aria-labelledby="main-menu">
<FluentNavMenu Id="main-menu" Width="250" Collapsible="true" Title="Navigation menu" @bind-Expanded="expanded" CustomToggle="true">
<FluentNavLink Href="/" Match="NavLinkMatch.All" Icon="@(new Icons.Regular.Size20.Home())" IconColor="Color.Accent">Home</FluentNavLink>
<FluentNavLink Href="./" Match="NavLinkMatch.All" Icon="@(new Icons.Regular.Size20.Home())" IconColor="Color.Accent">Home</FluentNavLink>
</FluentNavMenu>
</nav>
</div>
Expand Down
1 change: 0 additions & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/GitTools/GitVersion/main/schemas/6.0/GitVersion.configuration.json

assembly-file-versioning-format: '{Major}.{Minor}.{Patch}.{env:GITHUB_RUN_NUMBER ?? 0}'
next-version: 1.0.0
23 changes: 0 additions & 23 deletions UnitTests/UnitTests/VersionTests.cs

This file was deleted.

10 changes: 2 additions & 8 deletions Xmss/InteropServices/CriticalXmssBlobHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
// SPDX-License-Identifier: MIT

using System.Runtime.InteropServices;
using System.Security.Cryptography;

namespace Dorssel.Security.Cryptography.InteropServices;

abstract class CriticalXmssBlobHandle<T>(bool SecureZeroize) : CriticalXmssHandle<T> where T : unmanaged
abstract class CriticalXmssBlobHandle<T> : CriticalXmssHandle<T> where T : unmanaged
{
protected static H Alloc<H>(int size) where H : CriticalXmssBlobHandle<T>, new()
{
Expand Down Expand Up @@ -42,13 +41,8 @@ public Span<byte> Data
}
}

protected sealed override unsafe void Free(T* pointer)
protected override unsafe void Free(T* pointer)

Check notice

Code scanning / devskim

The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers. Unsafe code in is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. Note

Review unsafe code
{
if (SecureZeroize)
{
// cannot use Data and DataLength, as object is marked as closed already
CryptographicOperations.ZeroMemory(new((nuint*)handle + 1, (int)*(nuint*)handle));
}
NativeMemory.Free(pointer);
}
}
17 changes: 17 additions & 0 deletions Xmss/InteropServices/CriticalXmssPrivateBlobHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: 2025 Frans van Dorsselaer
//
// SPDX-License-Identifier: MIT

using System.Security.Cryptography;

namespace Dorssel.Security.Cryptography.InteropServices;

abstract class CriticalXmssPrivateBlobHandle<T> : CriticalXmssBlobHandle<T> where T : unmanaged
{
protected sealed override unsafe void Free(T* pointer)

Check notice

Code scanning / devskim

The unsafe keyword denotes an unsafe context, which is required for any operation involving pointers. Unsafe code in is not necessarily dangerous; it is just code whose safety cannot be verified by the CLR. Note

Review unsafe code
{
// cannot use Data and DataLength, as object is marked as closed already
CryptographicOperations.ZeroMemory(new((nuint*)handle + 1, (int)*(nuint*)handle));
base.Free(pointer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Dorssel.Security.Cryptography.InteropServices;

sealed class CriticalXmssPrivateKeyStatefulBlobHandle() : CriticalXmssBlobHandle<XmssPrivateKeyStatefulBlob>(true)
sealed class CriticalXmssPrivateKeyStatefulBlobHandle : CriticalXmssPrivateBlobHandle<XmssPrivateKeyStatefulBlob>
{
public static CriticalXmssPrivateKeyStatefulBlobHandle Alloc()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Dorssel.Security.Cryptography.InteropServices;

sealed class CriticalXmssPrivateKeyStatelessBlobHandle() : CriticalXmssBlobHandle<XmssPrivateKeyStatelessBlob>(true)
sealed class CriticalXmssPrivateKeyStatelessBlobHandle : CriticalXmssPrivateBlobHandle<XmssPrivateKeyStatelessBlob>
{
public static CriticalXmssPrivateKeyStatelessBlobHandle Alloc()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace Dorssel.Security.Cryptography.InteropServices;

sealed class CriticalXmssPublicKeyInternalBlobHandle() : CriticalXmssBlobHandle<XmssPublicKeyInternalBlob>(false)
sealed class CriticalXmssPublicKeyInternalBlobHandle : CriticalXmssBlobHandle<XmssPublicKeyInternalBlob>
{
public static CriticalXmssPublicKeyInternalBlobHandle Alloc(XmssCacheType cacheType, byte cacheLevel, XmssParameterSet parameterSet)
{
Expand Down
2 changes: 1 addition & 1 deletion Xmss/InteropServices/CriticalXmssSignatureBlobHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

namespace Dorssel.Security.Cryptography.InteropServices;

sealed class CriticalXmssSignatureBlobHandle() : CriticalXmssBlobHandle<XmssSignatureBlob>(false)
sealed class CriticalXmssSignatureBlobHandle : CriticalXmssBlobHandle<XmssSignatureBlob>
{
}
12 changes: 8 additions & 4 deletions Xmss/X509Certificates/XmssCertificateExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@
//
// SPDX-License-Identifier: MIT

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace Dorssel.Security.Cryptography.X509Certificates;

/// <summary>
/// TODO
/// Provides an extension method for retrieving the XMSS implementation for the public key of an <see cref="X509Certificate2"/>.
/// </summary>
public static class XmssCertificateExtensions
{
/// <summary>
/// TODO
/// Gets the XMSS public key from the <see cref="X509Certificate2"/>.
/// </summary>
/// <param name="certificate">TODO</param>
/// <returns>TODO</returns>
/// <param name="certificate">The certificate.</param>
/// <returns>The public key, or <see langword="null"/> if the certificate does not have an XMSS public key.</returns>
/// <exception cref="ArgumentNullException"><paramref name="certificate"/> is <see langword="null"/>.</exception>
/// <exception cref="CryptographicException">The XMSS library reports an error.
/// See the <see cref="Exception.Message"/> property for more information.</exception>
public static Xmss? GetXmssPublicKey(this X509Certificate2 certificate)
{
ArgumentNullException.ThrowIfNull(certificate);
Expand Down
120 changes: 67 additions & 53 deletions Xmss/Xmss.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,47 @@
namespace Dorssel.Security.Cryptography;

/// <summary>
/// TODO
/// Provides an implementation of the XMSS algorithm.
/// </summary>
public sealed class Xmss
: AsymmetricAlgorithm
{
#region Construction
/// <summary>
/// TODO
/// </summary>
public Xmss()
static Xmss()
{
LegalKeySizesValue = [new(256, 256, 0)];
KeySizeValue = 256;
VerifyLibraryVersion();
TryRegisterWithCryptoConfig();
}

/// <summary>
/// TODO
/// Throws if either the library cannot be loaded, or it does not expose the expected version (i.e., it is the wrong one).
/// </summary>
/// <returns>TODO</returns>
public static new Xmss Create()
[ExcludeFromCodeCoverage(Justification = "Not testable.")]
static void VerifyLibraryVersion()
{
return new Xmss();
var runtimeVersion = SafeNativeMethods.xmss_library_get_version();
if (runtimeVersion != Defines.XMSS_LIBRARY_VERSION)
{
throw new DllNotFoundException($"XMSS library version mismatch ({runtimeVersion})");
}
}
#endregion

#region Version
/// <summary>
/// TODO
/// Initializes a new instance of the <see cref="Xmss"/> class.
/// </summary>
public static Version NativeHeadersVersion => new(Defines.XMSS_LIBRARY_VERSION_MAJOR, Defines.XMSS_LIBRARY_VERSION_MINOR,
Defines.XMSS_LIBRARY_VERSION_PATCH);
public Xmss()
{
LegalKeySizesValue = [new(256, 256, 0)];
KeySizeValue = 256;
}

/// <summary>
/// TODO
/// Creates a new instance of the default implementation of the eXtended Merkle Signature Scheme (XMSS).
/// </summary>
public static Version NativeLibraryVersion
/// <returns>A new instance of the default implementation (<see cref="Xmss"/>) of this class.</returns>
public static new Xmss Create()
{
get
{
var nativeVersion = SafeNativeMethods.xmss_library_get_version();
return new(Defines.XMSS_LIBRARY_GET_VERSION_MAJOR(nativeVersion),
Defines.XMSS_LIBRARY_GET_VERSION_MINOR(nativeVersion), Defines.XMSS_LIBRARY_GET_VERSION_PATCH(nativeVersion));
}
return new Xmss();
}
#endregion

Expand All @@ -81,7 +79,7 @@ static void RegisterWithCryptoConfig()
}

[ExcludeFromCodeCoverage(Justification = "Not testable; WASM only.")]
static Xmss()
static void TryRegisterWithCryptoConfig()
{
try
{
Expand Down Expand Up @@ -112,10 +110,7 @@ void ResetState()
ParameterSet = XmssParameterSet.None;
}

/// <summary>
/// TODO
/// </summary>
/// <param name="disposing">TODO</param>
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (!IsDisposed)
Expand All @@ -142,6 +137,7 @@ protected override void Dispose(bool disposing)
/// </summary>
/// <seealso href="https://www.iana.org/assignments/xml-security-uris/xml-security-uris.xhtml" />
/// <seealso href="https://www.rfc-editor.org/rfc/rfc9231.html#name-xmss-and-xmssmt" />
/// <exception cref="InvalidOperationException">When the current <see cref="ParameterSet"/> is <see cref="XmssParameterSet.None"/>.</exception>
public override string? SignatureAlgorithm => ParameterSet switch
{
XmssParameterSet.XMSS_SHA2_10_256 => "http://www.w3.org/2021/04/xmldsig-more#xmss-sha2-10-256", // DevSkim: ignore DS137138
Expand Down Expand Up @@ -665,10 +661,10 @@ public void RequestFutureSignatures(int count)
}

/// <summary>
/// TODO
/// Computes the XMSS signature for the specified data.
/// </summary>
/// <param name="data">TODO</param>
/// <returns>TODO</returns>
/// <param name="data">The data to sign.</param>
/// <returns>The XMSS signature for the specified data.</returns>
public byte[] Sign(ReadOnlySpan<byte> data)
{
var signature = new byte[Defines.XMSS_SIGNATURE_SIZE(ParameterSet.AsOID())];
Expand All @@ -692,12 +688,12 @@ public unsafe byte[] Sign(void* data, nuint dataLength)
}

/// <summary>
/// TODO
/// Computes the XMSS signature for the specified data into the provided buffer.
/// </summary>
/// <param name="data">TODO</param>
/// <param name="destination">TODO</param>
/// <returns>TODO</returns>
/// <exception cref="ArgumentException">TODO</exception>
/// <param name="data">The data to sign.</param>
/// <param name="destination">The buffer to receive the signature.</param>
/// <returns>The total number of bytes written to <paramref name="destination"/>.</returns>
/// <exception cref="ArgumentException">The buffer in <paramref name="destination"/> is too small to hold the signature.</exception>
public int Sign(ReadOnlySpan<byte> data, Span<byte> destination)
{
return TrySign(data, destination, out var bytesWritten) ? bytesWritten
Expand All @@ -719,12 +715,13 @@ public unsafe int Sign(void* data, nuint dataLength, Span<byte> destination)
}

/// <summary>
/// TODO
/// Attempts to compute the XMSS digital signature for the specified read-only span of bytes into the provided destination by using the current key.
/// </summary>
/// <param name="data">TODO</param>
/// <param name="destination">TODO</param>
/// <param name="bytesWritten">TODO</param>
/// <returns>TODO</returns>
/// <param name="data">The data to be signed.</param>
/// <param name="destination">The buffer to receive the signature.</param>
/// <param name="bytesWritten">When this method returns, the total number of bytes written into <paramref name="destination"/>.
/// This parameter is treated as uninitialized.</param>
/// <returns><see langword="true"/> if <paramref name="destination"/> is big enough to receive the signature; otherwise, <see langword="false"/>.</returns>
public bool TrySign(ReadOnlySpan<byte> data, Span<byte> destination, out int bytesWritten)
{
unsafe
Expand Down Expand Up @@ -783,11 +780,11 @@ public unsafe bool TrySign(void* data, nuint dataLength, Span<byte> destination,

#region Verify
/// <summary>
/// TODO
/// Verifies that a digital signature is appropriate for the current key and provided data.
/// </summary>
/// <param name="data">TODO</param>
/// <param name="signature">TODO</param>
/// <returns>TODO</returns>
/// <param name="data">The signed data.</param>
/// <param name="signature">The signature to be verified.</param>
/// <returns><see langword="true"/> if the signature is valid for the provided data; otherwise, <see langword="false"/>.</returns>
public bool Verify(Stream data, ReadOnlySpan<byte> signature)
{
ArgumentNullException.ThrowIfNull(data);
Expand Down Expand Up @@ -838,11 +835,11 @@ public bool Verify(Stream data, ReadOnlySpan<byte> signature)
}

/// <summary>
/// TODO
/// Verifies that a digital signature is appropriate for the current key and provided data.
/// </summary>
/// <param name="data">TODO</param>
/// <param name="signature">TODO</param>
/// <returns>TODO</returns>
/// <param name="data">The signed data.</param>
/// <param name="signature">The signature to be verified.</param>
/// <returns><see langword="true"/> if the signature is valid for the provided data; otherwise, <see langword="false"/>.</returns>
public bool Verify(ReadOnlySpan<byte> data, ReadOnlySpan<byte> signature)
{
ObjectDisposedException.ThrowIf(IsDisposed, this);
Expand Down Expand Up @@ -1109,10 +1106,27 @@ void ImportXmssPublicKey(XmssParameterSet parameterSet, in XmssPublicKey publicK
}

/// <summary>
/// TODO
/// Imports an RFC 7468 PEM-encoded key, replacing the keys for this object.
/// </summary>
/// <param name="input">TODO</param>
/// <exception cref="ArgumentException">TODO</exception>
/// <param name="input">The PEM text of the key to import.</param>
/// <exception cref="ArgumentException">
/// <paramref name="input"/> does not contain a PEM-encoded key with a recognized label.
///
/// -or-
///
/// <paramref name="input"/> contains multiple PEM-encoded keys with a recognized label.
/// </exception>
/// <remarks>
/// Unsupported or malformed PEM-encoded objects will be ignored.
/// If multiple supported PEM labels are found, an exception is raised to prevent importing a key when the key is ambiguous.
///
/// This method supports the following PEM labels:
/// <list type="bullet">
/// <item><c>PUBLIC KEY</c></item>
/// <item><c>XMSS PUBLIC KEY</c></item>
/// <item><c>CERTIFICATE</c></item>
/// </list>
/// </remarks>
public override void ImportFromPem(ReadOnlySpan<char> input)
{
PemFields? foundFields = default;
Expand Down

0 comments on commit 4c17e67

Please sign in to comment.