Skip to content

filipw/Strathweb.Dilithium

Repository files navigation

Strathweb.Dilithium

This repo contains a set of libraries facilitating and streamlining the integration of Module-Lattice-Based Digital Signature Standard signature scheme (a FIPS 204 Post-Quantum Cryptography suite, based on Crystals-Dilithium) into ASP.NET Core projects - both for the purposes of token signing and their validation.

The algorithm implementations come from the excellent BouncyCastle and supports ML-DSA-44, ML-DSA-65 and ML-DSA-87 parameter sets.

The libraries are intended to be used as the ASP.NET Core implementation of ML-DSA for JOSE and COSE IETF draft.

While the type names all follow the ML-DSA convention, the naming of the library intentionally still refers to "Dilithium" (the original name before standardization), because it is cool 😎

Available packages

Strathweb.Dilithium.IdentityModel

NuGet

This is the base package on top of which other features can be built. Contains integration of ML-DSA into the ecosystem of Microsoft.IdentityModel.Tokens. Those are:

  • MlDsaSecurityKey, which implements AsymmetricSecurityKey abstract class
  • MlDsaSignatureProvider, which implements SignatureProvider abstract class
  • MlDsaCryptoProviderFactory, which extends the default CryptoProviderFactory

A new instance of a ML-DSA public-private pair can be created by using the main constructor that takes in the algorithm (ML-DSA-44, ML-DSA-65 or ML-DSA-87) identifier.

var securityKey = new MlDsaSecurityKey("ML-DSA-65");

The encoded private and public keys can then be read using the relevant properties:

byte[] publicKey = securityKey.PublicKey;
byte[] privateKey = securityKey.PrivateKey;

They can also be exported out of process (e.g. using base64url encoding) and later used to re-initialize the key:

var securityKey = new MlDsaSecurityKey("ML-DSA-65", publicKey, privateKey);

The private key is optional - in which case the key can still be used for signature validation but not longer for signing.

It is also possible to export the key to JSON Web Key format (where it is possible to decide whether the private key should be included or not):

JsonWebKey jwk = securityKey.ToJsonWebKey(includePrivateKey: true);

Such a JWK can be serialized, persisted or published, and later re-imported:

var securityKey = new MlDsaSecurityKey(jwk);

Depending on whether the JWK was exported with the private key or not, the instance of MlDsaSecurityKey will be suitable for signing or only for validation of signatures.

Strathweb.Dilithium.DuendeIdentityServer

NuGet

Add-on to Duende IdentityServer, which allows for registering a MlDsaSecurityKey as valid token signing credential. Once configured, the ML-DSA key can be used for token signing for API resources that are flagged as compatible with the ML-DSA algorithms. The public key is also going to get announced with the JWKS document.

Example usage:

Create an ephemeral public-private pair

This pair will be discarded upon application shutdown.

builder.Services.AddIdentityServer()
    .AddMlDsaSigningCredential(new MlDsaSecurityKey("ML-DSA-65")) // new key per startup

Load an ML-DSA key from a JSON Web Key format

It is possible to manually load JWK (Microsoft.IdentityModel.Tokens.JsonWebKey) from some source, such as a key vault, and then use it to initialize the MlDsaSecurityKey:

// load the JWK from somewhere e.g. KeyVault or filesystem
builder.Services.AddIdentityServer()
    .AddMlDsaSigningCredential(new MlDsaSecurityKey(jwk)) // key from the JWK
    // continue with the rest of Identity Server configuration

Alternatively, it can also be loaded from the file system (using a path relative to the current directory or an absolute one):

builder.Services.AddIdentityServer()
    .AddMlDsaSigningCredential(pathToMlDsaJwk) // key from the JWK on the filesystem
    // continue with the rest of Identity Server configuration

Load an ML-DSA key from byte array public/private key representations

// load the public key and private key from somewhere e.g. KeyVault or filesystem
byte[] privateKey = ...
byte[] publicKey = ...
builder.Services.AddIdentityServer()
    .AddMlDsaSigningCredential(new MlDsaSecurityKey("ML-DSA-65", publicKey, privateKey)) // key from the JWK
    // continue with the rest of Identity Server configuration

Once registered, the Identity Server will announce the public part of the ML-DSA key in the JWKS document. Other non-post quantum keys are allowed to co-exist. Example:

{
    "keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "30F4....",
            "e": "AQAB",
            "n": "scmPFy....",
            "alg": "RS256"
        },
        {
            "kty": "AKP",
            "use": "sig",
            "kid": "A574....",
            "alg": "ML-DSA-65",
            "x": "OMjMS...."
        }
    ]
}

The JWT tokens issued by the Identity Server will contains the "alg": "ML-DSA-65" in the header; otherwise the token will be indistinguishable from the other tokens.

Automatic key management

The library can also manage its own ML-DSA keys using Identity Server's key management feature.

builder.Services.AddIdentityServer()
    .AddMlDsaSupport() // automatically manage ML-DSA keys
    // continue with the rest of Identity Server configuration

This set up instructs the library to create ML-DSA-65 keys, store them securely and rotate them according to the schedule configured in Identity Server. By default, the keys are automatically rotated every 90 days, announced 14 days in advance, and retained for 14 days after it expires.

The normal customization of key management rules is still supported, and the library will respect those rules:

builder.Services.AddIdentityServer(options =>
    {
        // new key every 14 days
        options.KeyManagement.RotationInterval = TimeSpan.FromDays(14);
        
        // announce new key 3 days in advance in discovery
        options.KeyManagement.PropagationTime = TimeSpan.FromDays(3);
        
        // keep old key for 3 days in discovery for validation of tokens
        options.KeyManagement.RetentionDuration = TimeSpan.FromDays(3);
    })
    .AddMlDsaSupport() // automatically manage ML-DSA keys
    // continue with the rest of Identity Server configuration

By default, the library disallows any other keys than ML-DSA, which means the built-in Identity Server behavior of generating RSA keys gets suppressed. It can be restored via the options. The same options can also be used to choose a different algorithm than ML-DSA-65:

builder.Services.AddIdentityServer()
    .AddMlDsaSupport(new MlDsaSupportOptions {
        KeyManagementAlgorithm = "ML-DSA-87", // override the default "ML-DSA-65"
        DisallowNonMlDsaKeys = false // allow RSA keys to co-exist
     }) // automatically manage ML-DSA keys
    // continue with the rest of Identity Server configuration

Strathweb.Dilithium.AspNetCore

NuGet

Add-on for Microsoft.AspNetCore.Authentication.JwtBearer package, allowing for enabling ML-DSA-signed JWT token validation for the Bearer scheme.

Usage:

builder.Services.AddAuthentication().AddJwtBearer(opt =>
{
    // all the usual necessary configuration such as authoritiy or audience
    // omitted for brevity
    
    // enable ML-DSA tokens
    opt.ConfigureMlDsaTokenSupport();
});

When ML-DSA token support is enabled, the extension takes over the management of JWKS fetched from the trusted authority. Those are cached for 24h, but this can be changed in the configuration.

By default any other tokens from the trusted authority are allowed as well. However, it is also possible to restrict the API to only accept ML-DSA based signing keys.

builder.Services.AddAuthentication().AddJwtBearer(opt =>
{
    // all the usual necessary configuration such as authoritiy or audience
    // omitted for brevity
    
    // enable ML-DSA tokens
    opt.ConfigureMlDsaTokenSupport(dopt => dopt.AllowNonMlDsaKeys = false;);
});

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages