WasmCryptoProvider

Namespace: WvdS.System.Security.Cryptography.Providers

Provider crittografico basato su JavaScript Interop per Blazor WebAssembly. Comunica tramite IJSRuntime con openssl.wasm.

Panoramica

Il WasmCryptoProvider consente la crittografia Post-Quantum in applicazioni Blazor WebAssembly tramite:

  • JavaScript Interop verso OpenSSL compilato in WebAssembly
  • API completamente asincrona (richiesta per JS Interop)
  • Funzionalita identica a NativeCryptoProvider

Architettura

Blazor WebAssembly
      │
      ▼
┌─────────────────┐
│ WasmCrypto-     │
│ Provider        │
│ (C#)            │
└────────┬────────┘
         │ IJSRuntime.InvokeAsync
         ▼
┌─────────────────┐
│ wvds-crypto.js  │
│ (JavaScript)    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ openssl.wasm    │
│ (WebAssembly)   │
└─────────────────┘

Proprieta

Proprieta Tipo Descrizione
Name string "WASM (JS Interop)"
IsAvailable bool true se inizializzato

Dependency Injection

// Program.cs (Blazor WebAssembly)
builder.Services.AddScoped<ICryptoProvider>(sp =>
    new WasmCryptoProvider(sp.GetRequiredService<IJSRuntime>()));

Inizializzazione

@inject ICryptoProvider CryptoProvider
 
@code {
    protected override async Task OnInitializedAsync()
    {
        await CryptoProvider.InitializeAsync();
 
        if (CryptoProvider.IsAvailable)
        {
            var version = CryptoProvider.GetOpenSslVersion();
            Console.WriteLine($"OpenSSL WASM: {version}");
        }
    }
}

File JS/WASM richiesti

In wwwroot/index.html:

<head>
    <!-- OpenSSL WASM Module -->
    <script src="_content/WvdS.Crypto/openssl.js"></script>
 
    <!-- WvdS Crypto Wrapper -->
    <script src="_content/WvdS.Crypto/wvds-crypto.js"></script>
</head>

Operazioni ML-DSA

GenerateMlDsaKeyPairAsync

var (publicKey, privateKey) = await provider.GenerateMlDsaKeyPairAsync("ML-DSA-65");

SignMlDsaAsync

byte[] data = Encoding.UTF8.GetBytes("Dati firmati dal browser");
byte[] signature = await provider.SignMlDsaAsync(data, privateKey);

VerifyMlDsaAsync

bool isValid = await provider.VerifyMlDsaAsync(data, signature, publicKey);

Operazioni ML-KEM

GenerateMlKemKeyPairAsync

var (publicKey, privateKey) = await provider.GenerateMlKemKeyPairAsync("ML-KEM-768");

EncapsulateAsync

var (sharedSecret, ciphertext) = await provider.EncapsulateAsync(recipientPublicKey);

DecapsulateAsync

byte[] sharedSecret = await provider.DecapsulateAsync(ciphertext, privateKey);

Operazioni Key Derivation

Pbkdf2Async

PBKDF2 tramite Web Crypto API.

byte[] salt = await provider.RandomBytesAsync(32);
 
byte[] derivedKey = await provider.Pbkdf2Async(
    password: "UserPassword",
    salt: salt,
    iterations: 100000,
    outputLength: 32,
    hash: "SHA-256");

Pbkdf2WithPqSaltAsync

PBKDF2 con Salt rinforzato PQ.

byte[] key = await provider.Pbkdf2WithPqSaltAsync(
    password: "UserPassword",
    baseSalt: baseSalt,
    pqPublicKey: recipientPqPublicKey,
    iterations: 100000,
    outputLength: 32);

Argon2idAsync

KDF memory-hard tramite OpenSSL WASM.

byte[] key = await provider.Argon2idAsync(
    password: passwordBytes,
    salt: salt,
    outputLength: 32,
    iterations: 3,
    memoryKiB: 65536,
    parallelism: 4);

Crittografia Stream/Chunked

EncryptChunkedAsync / DecryptChunkedAsync

Per grandi quantita di dati con elaborazione a chunk.

byte[] key = await provider.RandomBytesAsync(32);
 
// Crittografare
byte[] encrypted = await provider.EncryptChunkedAsync(
    plaintext,
    key,
    chunkSize: 65536);  // Chunk da 64 KB
 
// Decrittografare
byte[] decrypted = await provider.DecryptChunkedAsync(encrypted, key);

EncryptStreamWithPqKeyAsync / DecryptStreamWithPqKeyAsync

Combina ML-KEM Key Exchange con crittografia chunked.

// Crittografare per destinatario
var (kemCiphertext, encryptedData) = await provider.EncryptStreamWithPqKeyAsync(
    plaintext,
    recipientPublicKey,
    chunkSize: 65536);
 
// Decrittografare
byte[] decrypted = await provider.DecryptStreamWithPqKeyAsync(
    kemCiphertext,
    encryptedData,
    privateKey);

Operazioni HKDF

HkdfExtractAsync

byte[] prk = await provider.HkdfExtractAsync(salt, inputKeyMaterial);

HkdfExpandAsync

byte[] okm = await provider.HkdfExpandAsync(prk, info, outputLength: 32);

HkdfDeriveKeyAsync

Extract + Expand in un unico passaggio.

byte[] key = await provider.HkdfDeriveKeyAsync(
    ikm: sharedSecret,
    length: 32,
    salt: optionalSalt,
    info: contextInfo);

DeriveHybridKeyAsync

Combina secret classico e PQ.

byte[] hybridKey = await provider.DeriveHybridKeyAsync(
    classicSecret: ecdhSecret,
    pqSecret: mlKemSecret,
    outputLength: 32);

TLS 1.3 Key Derivation

DeriveTls13KeysAsync

Tls13KeyMaterial keys = await provider.DeriveTls13KeysAsync(
    sharedSecret,
    clientHello,
    serverHello);
 
// Accesso ai secrets derivati
var clientKey = keys.ClientHandshakeTrafficSecret;
var serverKey = keys.ServerHandshakeTrafficSecret;

Firme ibride

CreateHybridSignatureAsync

Crea firma ibrida da firma classica e ML-DSA.

// Creare firma classica (es. ECDSA)
byte[] classicSig = CreateEcdsaSignature(data);
 
// Creare firma ibrida
byte[] hybridSig = await provider.CreateHybridSignatureAsync(
    data,
    classicSig,
    mlDsaPrivateKey);

Metodi utility

RandomBytesAsync

Numeri casuali crittograficamente sicuri tramite Web Crypto API.

byte[] random = await provider.RandomBytesAsync(32);

Operazioni certificati

CreateEphemeralCertificateAsync

byte[] certBytes = await provider.CreateEphemeralCertificateAsync(
    "CN=Browser Certificate",
    TimeSpan.FromHours(1),
    mlDsaPrivateKey);

SignCertificateAsync

byte[] signedCert = await provider.SignCertificateAsync(tbsCertificate, privateKey);

Panoramica metodi

ML-DSA

Metodo Parametri Ritorno
GenerateMlDsaKeyPairAsync string algorithm Task<(byte[], byte[])>
SignMlDsaAsync byte[] data, byte[] privateKey Task<byte[]>
VerifyMlDsaAsync byte[] data, byte[] signature, byte[] publicKey Task<bool>

ML-KEM

Metodo Parametri Ritorno
GenerateMlKemKeyPairAsync string algorithm Task<(byte[], byte[])>
EncapsulateAsync byte[] publicKey Task<(byte[], byte[])>
DecapsulateAsync byte[] ciphertext, byte[] privateKey Task<byte[]>

Key Derivation

Metodo Parametri Ritorno
Pbkdf2Async string password, byte[] salt, int iterations, int outputLength, string hash Task<byte[]>
Pbkdf2WithPqSaltAsync string password, byte[] baseSalt, byte[] pqPublicKey, int iterations, int outputLength Task<byte[]>
Argon2idAsync byte[]/string password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism Task<byte[]>

HKDF

Metodo Parametri Ritorno
HkdfExtractAsync byte[] salt, byte[] ikm Task<byte[]>
HkdfExpandAsync byte[] prk, byte[] info, int length Task<byte[]>
HkdfDeriveKeyAsync byte[] ikm, int length, byte[]? salt, byte[]? info Task<byte[]>
DeriveHybridKeyAsync byte[] classicSecret, byte[] pqSecret, int outputLength Task<byte[]>

Encryption

Metodo Parametri Ritorno
EncryptChunkedAsync byte[] plaintext, byte[] key, int chunkSize Task<byte[]>
DecryptChunkedAsync byte[] ciphertext, byte[] key Task<byte[]>
EncryptStreamWithPqKeyAsync byte[] plaintext, byte[] publicKey, int chunkSize Task<(byte[], byte[])>
DecryptStreamWithPqKeyAsync byte[] kemCiphertext, byte[] encryptedData, byte[] privateKey Task<byte[]>

Utility

Metodo Parametri Ritorno
RandomBytesAsync int length Task<byte[]>
CreateHybridSignatureAsync byte[] data, byte[] classicSig, byte[] pqPrivKey Task<byte[]>
DeriveTls13KeysAsync byte[] sharedSecret, byte[] clientHello, byte[] serverHello Task<Tls13KeyMaterial>

Esempio completo

// Componente Blazor
@page "/crypto-demo"
@inject ICryptoProvider CryptoProvider
 
<h3>PQ Crypto Demo</h3>
<p>Status: @_status</p>
 
@code {
    private string _status = "Inizializzazione...";
 
    protected override async Task OnInitializedAsync()
    {
        try
        {
            await CryptoProvider.InitializeAsync();
 
            // Demo Key Exchange
            var (alicePublic, alicePrivate) =
                await CryptoProvider.GenerateMlKemKeyPairAsync();
 
            var (sharedSecret, ciphertext) =
                await CryptoProvider.EncapsulateAsync(alicePublic);
 
            var decapsulated =
                await CryptoProvider.DecapsulateAsync(ciphertext, alicePrivate);
 
            bool keysMatch = sharedSecret.SequenceEqual(decapsulated);
 
            // Demo firma
            var (sigPub, sigPriv) =
                await CryptoProvider.GenerateMlDsaKeyPairAsync();
 
            byte[] message = Encoding.UTF8.GetBytes("Test Message");
            byte[] signature = await CryptoProvider.SignMlDsaAsync(message, sigPriv);
            bool isValid = await CryptoProvider.VerifyMlDsaAsync(message, signature, sigPub);
 
            _status = $"Keys match: {keysMatch}, Signature valid: {isValid}";
        }
        catch (Exception ex)
        {
            _status = $"Errore: {ex.Message}";
        }
    }
}

Note di sicurezza

  • Richiede .NET 8.0+ con Blazor WebAssembly
  • La memoria del browser e meno sicura della memoria del server
  • Le chiavi private non dovrebbero essere memorizzate a lungo termine nel browser
  • Per operazioni sensibili: preferire l'elaborazione lato server
  • openssl.wasm e wvds-crypto.js devono essere caricati correttamente
Best Practice per crittografia nel browser:
  • Utilizzare chiavi effimere per crittografia basata su sessione
  • Mantenere le chiavi private sensibili sul server
  • Non utilizzare IndexedDB/localStorage per chiavi non crittografate
  • Configurare correttamente gli header CSP per WASM

Vedi anche


Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional

Zuletzt geändert: il 30/01/2026 alle 00:14