====== 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(sp => new WasmCryptoProvider(sp.GetRequiredService())); ===== 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'': ===== 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 | | ''VerifyMlDsaAsync'' | byte[] data, byte[] signature, byte[] publicKey | Task | ==== ML-KEM ==== ^ Metodo ^ Parametri ^ Ritorno ^ | ''GenerateMlKemKeyPairAsync'' | string algorithm | Task<(byte[], byte[])> | | ''EncapsulateAsync'' | byte[] publicKey | Task<(byte[], byte[])> | | ''DecapsulateAsync'' | byte[] ciphertext, byte[] privateKey | Task | ==== Key Derivation ==== ^ Metodo ^ Parametri ^ Ritorno ^ | ''Pbkdf2Async'' | string password, byte[] salt, int iterations, int outputLength, string hash | Task | | ''Pbkdf2WithPqSaltAsync'' | string password, byte[] baseSalt, byte[] pqPublicKey, int iterations, int outputLength | Task | | ''Argon2idAsync'' | byte[]/string password, byte[] salt, int outputLength, int iterations, int memoryKiB, int parallelism | Task | ==== HKDF ==== ^ Metodo ^ Parametri ^ Ritorno ^ | ''HkdfExtractAsync'' | byte[] salt, byte[] ikm | Task | | ''HkdfExpandAsync'' | byte[] prk, byte[] info, int length | Task | | ''HkdfDeriveKeyAsync'' | byte[] ikm, int length, byte[]? salt, byte[]? info | Task | | ''DeriveHybridKeyAsync'' | byte[] classicSecret, byte[] pqSecret, int outputLength | Task | ==== Encryption ==== ^ Metodo ^ Parametri ^ Ritorno ^ | ''EncryptChunkedAsync'' | byte[] plaintext, byte[] key, int chunkSize | Task | | ''DecryptChunkedAsync'' | byte[] ciphertext, byte[] key | Task | | ''EncryptStreamWithPqKeyAsync'' | byte[] plaintext, byte[] publicKey, int chunkSize | Task<(byte[], byte[])> | | ''DecryptStreamWithPqKeyAsync'' | byte[] kemCiphertext, byte[] encryptedData, byte[] privateKey | Task | ==== Utility ==== ^ Metodo ^ Parametri ^ Ritorno ^ | ''RandomBytesAsync'' | int length | Task | | ''CreateHybridSignatureAsync'' | byte[] data, byte[] classicSig, byte[] pqPrivKey | Task | | ''DeriveTls13KeysAsync'' | byte[] sharedSecret, byte[] clientHello, byte[] serverHello | Task | ===== Esempio completo ===== // Componente Blazor @page "/crypto-demo" @inject ICryptoProvider CryptoProvider

PQ Crypto Demo

Status: @_status

@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 ===== * [[.:start|Namespace Providers]] * [[.:icryptoprovider|ICryptoProvider]] * [[.:nativecryptoprovider|NativeCryptoProvider]] * [[.:cryptoproviderfactory|CryptoProviderFactory]] * [[it:int:pqcrypt:developer:integration:blazor-wasm|Integrazione Blazor WASM]] ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//