Skip to content
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2290bd6
Add ML-DSA (FIPS 204) post-quantum signature support
iNinja Mar 3, 2026
7dd5b0d
Add ML-DSA test suite
iNinja Mar 3, 2026
9b7f788
Add ML-DSA X.509 certificate support to X509SecurityKey
iNinja Mar 3, 2026
f30788c
Mark ML-DSA public API as [Experimental(SYSLIB5006)]
iNinja Mar 3, 2026
93bf3c1
Add X509 ML-DSA certificate tests
iNinja Mar 3, 2026
7a10a42
Minimize [Experimental] to only genuinely experimental API calls
iNinja Mar 3, 2026
7058810
Use embedded PFX for cross-TFM X509 ML-DSA tests
iNinja Mar 3, 2026
0b5efd8
Zero private key seed after import in MlDsaAdapter
iNinja Mar 4, 2026
6dcbf34
Add ML-DSA test improvements and fix span-based signing
iNinja Mar 4, 2026
18529da
Final review: fix span signing bug, optimize verify hot path, add cor…
iNinja Mar 4, 2026
bfd2d83
Add X509 ML-DSA end-to-end JWT tests
iNinja Mar 4, 2026
b24ebfe
Add E2E JWT tests for all three handler entry points
iNinja Mar 4, 2026
625a4bd
Security hardening: algorithm/key enforcement, pub/priv validation, r…
iNinja May 7, 2026
af86d49
Add expected RFC 9964 reference alongside draft URL
iNinja May 7, 2026
7ac0a0e
Add X509-to-JWK conversion for ML-DSA certificates (RFC 9881)
iNinja May 7, 2026
b682811
Suppress BCL TFM support warnings for net6.0
iNinja May 7, 2026
ce0fbf1
Final review fixes: x5c round-trip, seed zeroing, key disposal, defen…
iNinja May 7, 2026
0e3b590
Clarify MLDsa ownership contract in doc comments
iNinja May 7, 2026
df722a1
Address PR review: platform safety, caching, and cleanup
iNinja May 7, 2026
bd465a4
Isolate ML-DSA test material for platform compatibility
iNinja May 7, 2026
244e3da
Guard ML-DSA test data in AsymmetricSignatureTests for unsupported pl…
iNinja May 7, 2026
478837b
Remove unrelated Telemetry-Cardinality-Analysis.md from PR scope
iNinja May 7, 2026
2caaca5
Address PR review: log messages, null guards, alg/OID validation
iNinja May 7, 2026
e337206
Improve test coverage: sign/verify round-trip, thumbprint consistency…
iNinja May 7, 2026
730504e
Address PR review: hot-path allocation, alg validation, shared helpers
iNinja May 7, 2026
a0759c2
Add IDX10723 for X509 private key extraction failure, validate AKP alg
iNinja May 7, 2026
2334da7
Reject non-ML-DSA certs in AKP x5c, add IDX10724, fix indentation
iNinja May 7, 2026
bd23b6c
Update RepresentAsAsymmetricPublicJwk doc to note AKP alg requirement
iNinja May 7, 2026
99492ef
Guard x5c round-trip assertion for platform compatibility
iNinja May 8, 2026
26d94db
Thread safety for ML-DSA key init, null guards in X509 adapter path
iNinja May 8, 2026
a9a37e8
Fix PrivateKeyStatus ordering and make unsupported flag volatile
iNinja May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!-- Directory.Build.targets is imported after NuGet package .targets files,
so target overrides here take effect over package-defined InitialTargets. -->
<Project>

<!-- Microsoft.Bcl.Cryptography 10.0.2 and its transitive dependency System.Formats.Asn1
emit TFM support warnings on net6.0. ML-DSA functionality is validated on net6.0
via the compatibility package and covered by the cross-TFM test suite.
These overrides suppress only these specific package warnings — any new package
that introduces TFM warnings will still surface normally.
Note: Condition="false" applies unconditionally, but the package .targets files
only fire on unsupported TFMs (net6.0), so this has no effect on net8.0+.
Remove when net6.0 is dropped from SrcTargets. -->
<Target Name="NETStandardCompatError_Microsoft_Bcl_Cryptography_net8_0"
Condition="false" />
<Target Name="NETStandardCompatError_System_Formats_Asn1_net8_0"
Condition="false" />
Comment thread
iNinja marked this conversation as resolved.
Comment thread
iNinja marked this conversation as resolved.

</Project>
3 changes: 2 additions & 1 deletion build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
<PropertyGroup>
<AspNetCoreMinSupportedVersion>2.1.1</AspNetCoreMinSupportedVersion>
<BannedApiAnalyzersVersion>4.14.0</BannedApiAnalyzersVersion>
<MicrosoftBclCryptographyVersion>10.0.2</MicrosoftBclCryptographyVersion>
<MicrosoftBclTimeProviderVersion>8.0.1</MicrosoftBclTimeProviderVersion>
<MicrosoftCSharpVersion>4.5.0</MicrosoftCSharpVersion>
<MicrosoftSourceLinkGitHubVersion>1.0.0</MicrosoftSourceLinkGitHubVersion>
<NetStandardVersion>2.0.3</NetStandardVersion>
<NewtonsoftVersion>13.0.3</NewtonsoftVersion>
<SystemDiagnosticSourceVersion>6.0.2</SystemDiagnosticSourceVersion>
<SystemMemoryVersion>4.5.5</SystemMemoryVersion>
<SystemMemoryVersion>4.6.3</SystemMemoryVersion>
<SystemSecurityCryptographyCngVersion>4.5.0</SystemSecurityCryptographyCngVersion>
<SystemTextJsonVersion>8.0.5</SystemTextJsonVersion>
</PropertyGroup>
Expand Down
111 changes: 110 additions & 1 deletion src/Microsoft.IdentityModel.Tokens/AsymmetricAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ internal AsymmetricAdapter(
InitializeUsingX509SecurityKey(x509SecurityKeyFromJsonWebKey, algorithm, requirePrivateKey);
else if (securityKey is ECDsaSecurityKey edcsaSecurityKeyFromJsonWebKey)
InitializeUsingEcdsaSecurityKey(edcsaSecurityKeyFromJsonWebKey);
else if (securityKey is MlDsaSecurityKey mlDsaSecurityKeyFromJsonWebKey)
InitializeUsingMlDsaSecurityKey(mlDsaSecurityKeyFromJsonWebKey);
else
throw LogHelper.LogExceptionMessage(
new NotSupportedException(
Expand All @@ -99,6 +101,10 @@ internal AsymmetricAdapter(
{
InitializeUsingEcdsaSecurityKey(ecdsaKey);
}
else if (key is MlDsaSecurityKey mlDsaKey)
{
InitializeUsingMlDsaSecurityKey(mlDsaKey);
}
else
throw LogHelper.LogExceptionMessage(
new NotSupportedException(
Expand Down Expand Up @@ -138,6 +144,9 @@ protected virtual void Dispose(bool disposing)
{
if (ECDsa != null)
ECDsa.Dispose();

if (MLDsa != null)
MLDsa.Dispose();
#if DESKTOP
if (RsaCryptoServiceProviderProxy != null)
RsaCryptoServiceProviderProxy.Dispose();
Expand All @@ -151,6 +160,8 @@ protected virtual void Dispose(bool disposing)

private ECDsa ECDsa { get; set; }

private MLDsa MLDsa { get; set; }

internal byte[] Encrypt(byte[] data)
{
return _encryptFunction(data);
Expand All @@ -176,6 +187,18 @@ private void InitializeUsingEcdsaSecurityKey(ECDsaSecurityKey ecdsaSecurityKey)
_verifyUsingOffsetFunction = VerifyUsingOffsetECDsa;
}

private void InitializeUsingMlDsaSecurityKey(MlDsaSecurityKey mlDsaSecurityKey)
{
MLDsa = mlDsaSecurityKey.MLDsa;
_signFunction = SignMlDsa;
_signUsingOffsetFunction = SignUsingOffsetMlDsa;
#if NET6_0_OR_GREATER
_signUsingSpanFunction = SignUsingSpanMlDsa;
#endif
_verifyFunction = VerifyMlDsa;
_verifyUsingOffsetFunction = VerifyUsingOffsetMlDsa;
}

private void InitializeUsingRsa(RSA rsa, string algorithm)
{
// The return value for X509Certificate2.GetPrivateKey OR X509Certificate2.GetPublicKey.Key is a RSACryptoServiceProvider
Expand Down Expand Up @@ -258,10 +281,29 @@ private void InitializeUsingX509SecurityKey(
string algorithm,
bool requirePrivateKey)
{
if (requirePrivateKey)
if (x509SecurityKey.MlDsaPublicKey != null)
{
// ML-DSA certificate — borrow the MLDsa instance from X509SecurityKey.
// The X509SecurityKey retains ownership; _disposeCryptoOperators remains
// false so the adapter will not dispose it. Same pattern as RSA/ECDsa.
MLDsa mlDsa = requirePrivateKey ? x509SecurityKey.MlDsaPrivateKey : x509SecurityKey.MlDsaPublicKey;
if (mlDsa == null)
throw LogHelper.LogExceptionMessage(
new InvalidOperationException(
LogHelper.FormatInvariant(
LogMessages.IDX10638,
LogHelper.MarkAsNonPII(algorithm))));
Comment thread
iNinja marked this conversation as resolved.
Outdated

InitializeUsingMlDsaSecurityKey(new MlDsaSecurityKey(mlDsa));
}
Comment thread
iNinja marked this conversation as resolved.
else if (requirePrivateKey)
{
InitializeUsingRsa(x509SecurityKey.PrivateKey as RSA, algorithm);
}
else
{
InitializeUsingRsa(x509SecurityKey.PublicKey as RSA, algorithm);
Comment thread
iNinja marked this conversation as resolved.
}
}

private RSA RSA { get; set; }
Expand Down Expand Up @@ -342,6 +384,51 @@ private byte[] SignUsingOffsetECDsa(byte[] bytes, int offset, int count)
return ECDsa.SignHash(HashAlgorithm.ComputeHash(bytes, offset, count));
}

private byte[] SignMlDsa(byte[] bytes)
{
return MLDsa.SignData(bytes, context: null);
}

#if NET6_0_OR_GREATER
internal bool SignUsingSpanMlDsa(
ReadOnlySpan<byte> data,
Span<byte> destination,
out int bytesWritten)
{
int signatureSize = MLDsa.Algorithm.SignatureSizeInBytes;
if (destination.Length < signatureSize)
{
bytesWritten = 0;
return false;
}

// MLDsa.SignData requires destination to be exactly SignatureSizeInBytes.
MLDsa.SignData(data, destination.Slice(0, signatureSize), context: default);
bytesWritten = signatureSize;
return true;
}
#endif

private byte[] SignUsingOffsetMlDsa(byte[] bytes, int offset, int count)
{
#if NET6_0_OR_GREATER
int signatureSize = MLDsa.Algorithm.SignatureSizeInBytes;
byte[] signature = new byte[signatureSize];
MLDsa.SignData(
new ReadOnlySpan<byte>(bytes, offset, count),
signature.AsSpan(),
context: default);
return signature;
#else
if (offset == 0 && count == bytes.Length)
return MLDsa.SignData(bytes, context: null);

byte[] slice = new byte[count];
Buffer.BlockCopy(bytes, offset, slice, 0, count);
return MLDsa.SignData(slice, context: null);
#endif
}

internal bool Verify(byte[] bytes, byte[] signature)
{
return _verifyFunction(bytes, signature);
Expand Down Expand Up @@ -382,6 +469,28 @@ private bool VerifyUsingOffsetECDsa(byte[] bytes, int offset, int count, byte[]
#endif
}

private bool VerifyMlDsa(byte[] bytes, byte[] signature)
{
return MLDsa.VerifyData(bytes, signature, context: null);
}

private bool VerifyUsingOffsetMlDsa(byte[] bytes, int offset, int count, byte[] signature)
{
#if NET6_0_OR_GREATER
return MLDsa.VerifyData(
new ReadOnlySpan<byte>(bytes, offset, count),
signature.AsSpan(),
context: default);
#else
if (offset == 0 && count == bytes.Length)
return MLDsa.VerifyData(bytes, signature, context: null);

byte[] slice = new byte[count];
Buffer.BlockCopy(bytes, offset, slice, 0, count);
return MLDsa.VerifyData(slice, signature, context: null);
#endif
}

private byte[] DecryptWithRsa(byte[] bytes)
{
return RSA.Decrypt(bytes, RSAEncryptionPadding);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ public class AsymmetricSignatureProvider : SignatureProvider
{ SecurityAlgorithms.RsaSsaPssSha512, 1040 },
{ SecurityAlgorithms.RsaSsaPssSha256Signature, 528 },
{ SecurityAlgorithms.RsaSsaPssSha384Signature, 784 },
{ SecurityAlgorithms.RsaSsaPssSha512Signature, 1040 }
{ SecurityAlgorithms.RsaSsaPssSha512Signature, 1040 },
{ SecurityAlgorithms.MlDsa44, 10496 },
{ SecurityAlgorithms.MlDsa65, 15616 },
{ SecurityAlgorithms.MlDsa87, 20736 }
};

/// <summary>
Expand All @@ -66,7 +69,10 @@ public class AsymmetricSignatureProvider : SignatureProvider
{ SecurityAlgorithms.RsaSsaPssSha512, 1040 },
{ SecurityAlgorithms.RsaSsaPssSha256Signature, 528 },
{ SecurityAlgorithms.RsaSsaPssSha384Signature, 784 },
{ SecurityAlgorithms.RsaSsaPssSha512Signature, 1040 }
{ SecurityAlgorithms.RsaSsaPssSha512Signature, 1040 },
{ SecurityAlgorithms.MlDsa44, 10496 },
{ SecurityAlgorithms.MlDsa65, 15616 },
{ SecurityAlgorithms.MlDsa87, 20736 }
};

internal AsymmetricSignatureProvider(
Expand Down Expand Up @@ -190,6 +196,12 @@ protected virtual HashAlgorithmName GetHashAlgorithmName(string algorithm)

private AsymmetricAdapter CreateAsymmetricAdapter()
{
// ML-DSA and other pure-signing algorithms do not use an external hash.
if (SupportedAlgorithms.IsSupportedMlDsaAlgorithm(Algorithm))
return new AsymmetricAdapter(Key, Algorithm, WillCreateSignatures);

// Preserve the protected virtual GetHashAlgorithmName extensibility point
// for hash-based algorithms (RSA, ECDSA).
HashAlgorithmName hashAlgorithmName = GetHashAlgorithmName(Algorithm);
return new AsymmetricAdapter(
Key,
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryConvertToSymmetricSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryConvertToX509SecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryCreateToRsaSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryConvertToMlDsaSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as platform test", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.RsaSecurityKey.PrivateKeyStatus")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as platform test", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.MlDsaSecurityKey.PrivateKeyStatus")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Appropriate exception will be caught.", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.InMemoryCryptoProviderCache.TryRemove(Microsoft.IdentityModel.Tokens.SignatureProvider)~System.Boolean")]
[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as validation", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.InternalValidators.ValidateLifetimeAndIssuerAfterSignatureNotValidatedSaml(Microsoft.IdentityModel.Tokens.SecurityToken,System.Nullable{System.DateTime},System.Nullable{System.DateTime},System.String,Microsoft.IdentityModel.Tokens.TokenValidationParameters,System.Text.StringBuilder)")]
[assembly: SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Not using Globalization", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.Interop.Kernel32.GetMessage(System.Int32,System.IntPtr)~System.String")]
Expand Down
10 changes: 10 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/InternalAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,13 @@ virtual Microsoft.IdentityModel.Tokens.TokenHandler.CreateClaimsIdentityInternal
virtual Microsoft.IdentityModel.Tokens.TokenHandler.ValidateTokenAsync(Microsoft.IdentityModel.Tokens.SecurityToken token, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.IdentityModel.Tokens.Experimental.ValidationResult<Microsoft.IdentityModel.Tokens.Experimental.ValidatedToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationError>>
virtual Microsoft.IdentityModel.Tokens.TokenHandler.ValidateTokenAsync(string token, Microsoft.IdentityModel.Tokens.Experimental.ValidationParameters validationParameters, Microsoft.IdentityModel.Tokens.CallContext callContext, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.IdentityModel.Tokens.Experimental.ValidationResult<Microsoft.IdentityModel.Tokens.Experimental.ValidatedToken, Microsoft.IdentityModel.Tokens.Experimental.ValidationError>>
virtual Microsoft.IdentityModel.Tokens.ValidationError.CreateException() -> System.Exception
~Microsoft.IdentityModel.Tokens.MlDsaSecurityKey.MlDsaSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey webKey, bool usePrivateKey) -> void
static Microsoft.IdentityModel.Tokens.MlDsaAdapter.CreateMlDsa(Microsoft.IdentityModel.Tokens.JsonWebKey jsonWebKey, bool usePrivateKey) -> System.Security.Cryptography.MLDsa
static Microsoft.IdentityModel.Tokens.MlDsaAdapter.GetMLDsaAlgorithm(string algorithm) -> System.Security.Cryptography.MLDsaAlgorithm
static Microsoft.IdentityModel.Tokens.SupportedAlgorithms.IsSupportedMlDsaAlgorithm(string algorithm) -> bool
static Microsoft.IdentityModel.Tokens.SupportedAlgorithms.TryGetHashAlgorithmName(string algorithm, out System.Security.Cryptography.HashAlgorithmName hashAlgorithmName) -> bool
internal static Microsoft.IdentityModel.Tokens.MlDsaSecurityKey.GetAlgorithmName(System.Security.Cryptography.MLDsaAlgorithm algorithm) -> string
Microsoft.IdentityModel.Tokens.X509SecurityKey.MlDsaPrivateKey.get -> System.Security.Cryptography.MLDsa
Microsoft.IdentityModel.Tokens.X509SecurityKey.MlDsaPublicKey.get -> System.Security.Cryptography.MLDsa
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10721 = "IDX10721: Unable to create a key from the AKP JsonWebKey (alg: '{0}'). The required parameter '{1}' is missing or empty." -> string
const Microsoft.IdentityModel.Tokens.LogMessages.IDX10722 = "IDX10722: The AKP JsonWebKey (alg: '{0}') has inconsistent key material. The 'pub' parameter does not match the public key derived from 'priv'." -> string
12 changes: 12 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public static readonly
"N",
"OTH",
"P",
"PRIV",
"PUB",
"Q",
"QI",
"USE",
Expand Down Expand Up @@ -162,6 +164,10 @@ public static JsonWebKey Read(ref Utf8JsonReader reader, JsonWebKey jsonWebKey)
JsonSerializerPrimitives.ReadStrings(ref reader, jsonWebKey.Oth, JsonWebKeyParameterNames.Oth, JsonWebKey.ClassName, true);
else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.P))
jsonWebKey.P = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.P, JsonWebKey.ClassName, true);
else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Priv))
jsonWebKey.Priv = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Priv, JsonWebKey.ClassName, true);
else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Pub))
jsonWebKey.Pub = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Pub, JsonWebKey.ClassName, true);
else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.Q))
jsonWebKey.Q = JsonSerializerPrimitives.ReadString(ref reader, JsonWebKeyParameterNames.Q, JsonWebKey.ClassName, true);
else if (reader.ValueTextEquals(JsonWebKeyParameterUtf8Bytes.QI))
Expand Down Expand Up @@ -377,6 +383,12 @@ public static void Write(ref Utf8JsonWriter writer, JsonWebKey jsonWebKey)
if (!string.IsNullOrEmpty(jsonWebKey.P))
writer.WriteString(JsonWebKeyParameterUtf8Bytes.P, jsonWebKey.P);

if (!string.IsNullOrEmpty(jsonWebKey.Priv))
writer.WriteString(JsonWebKeyParameterUtf8Bytes.Priv, jsonWebKey.Priv);

if (!string.IsNullOrEmpty(jsonWebKey.Pub))
writer.WriteString(JsonWebKeyParameterUtf8Bytes.Pub, jsonWebKey.Pub);

if (!string.IsNullOrEmpty(jsonWebKey.Q))
writer.WriteString(JsonWebKeyParameterUtf8Bytes.Q, jsonWebKey.Q);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public static class JsonWebAlgorithmsKeyTypes
public const string EllipticCurve = "EC";
public const string RSA = "RSA";
public const string Octet = "oct";

// See: https://datatracker.ietf.org/doc/draft-ietf-cose-dilithium/ (RFC 9964 pending)
public const string Akp = "AKP";
#pragma warning restore 1591
}
}
Loading
Loading