====== WasmCryptoProvider ====== **Namespace:** ''WvdS.System.Security.Cryptography.Providers'' JavaScript Interop-based crypto provider for Blazor WebAssembly. Communicates via ''IJSRuntime'' with ''openssl.wasm''. ===== Overview ===== The ''WasmCryptoProvider'' enables post-quantum cryptography in Blazor WebAssembly applications through: * JavaScript Interop to WebAssembly-compiled OpenSSL * Fully asynchronous API (required for JS Interop) * Identical functionality to ''NativeCryptoProvider'' ===== Architecture ===== Blazor WebAssembly | v +-----------------+ | WasmCrypto- | | Provider | | (C#) | +--------+--------+ | IJSRuntime.InvokeAsync v +-----------------+ | wvds-crypto.js | | (JavaScript) | +--------+--------+ | v +-----------------+ | openssl.wasm | | (WebAssembly) | +-----------------+ ===== Properties ===== ^ Property ^ Type ^ Description ^ | ''Name'' | string | ''%%"WASM (JS Interop)"%%'' | | ''IsAvailable'' | bool | ''true'' if initialized | ===== Dependency Injection ===== // Program.cs (Blazor WebAssembly) builder.Services.AddScoped(sp => new WasmCryptoProvider(sp.GetRequiredService())); ===== Initialization ===== @inject ICryptoProvider CryptoProvider @code { protected override async Task OnInitializedAsync() { await CryptoProvider.InitializeAsync(); if (CryptoProvider.IsAvailable) { var version = CryptoProvider.GetOpenSslVersion(); Console.WriteLine($"OpenSSL WASM: {version}"); } } } ===== Required JS/WASM Files ===== In ''wwwroot/index.html'': ===== ML-DSA Operations ===== ==== GenerateMlDsaKeyPairAsync ==== var (publicKey, privateKey) = await provider.GenerateMlDsaKeyPairAsync("ML-DSA-65"); ==== SignMlDsaAsync ==== byte[] data = Encoding.UTF8.GetBytes("Browser-signed data"); byte[] signature = await provider.SignMlDsaAsync(data, privateKey); ==== VerifyMlDsaAsync ==== bool isValid = await provider.VerifyMlDsaAsync(data, signature, publicKey); ===== ML-KEM Operations ===== ==== 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); ===== Key Derivation Operations ===== ==== Pbkdf2Async ==== PBKDF2 via 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 with PQ-enhanced salt. byte[] key = await provider.Pbkdf2WithPqSaltAsync( password: "UserPassword", baseSalt: baseSalt, pqPublicKey: recipientPqPublicKey, iterations: 100000, outputLength: 32); ==== Argon2idAsync ==== Memory-hard KDF via OpenSSL WASM. byte[] key = await provider.Argon2idAsync( password: passwordBytes, salt: salt, outputLength: 32, iterations: 3, memoryKiB: 65536, parallelism: 4); ===== Stream/Chunked Encryption ===== ==== EncryptChunkedAsync / DecryptChunkedAsync ==== For large data volumes with chunk processing. byte[] key = await provider.RandomBytesAsync(32); // Encrypt byte[] encrypted = await provider.EncryptChunkedAsync( plaintext, key, chunkSize: 65536); // 64 KB Chunks // Decrypt byte[] decrypted = await provider.DecryptChunkedAsync(encrypted, key); ==== EncryptStreamWithPqKeyAsync / DecryptStreamWithPqKeyAsync ==== Combines ML-KEM key exchange with chunked encryption. // Encrypt for recipient var (kemCiphertext, encryptedData) = await provider.EncryptStreamWithPqKeyAsync( plaintext, recipientPublicKey, chunkSize: 65536); // Decrypt byte[] decrypted = await provider.DecryptStreamWithPqKeyAsync( kemCiphertext, encryptedData, privateKey); ===== HKDF Operations ===== ==== HkdfExtractAsync ==== byte[] prk = await provider.HkdfExtractAsync(salt, inputKeyMaterial); ==== HkdfExpandAsync ==== byte[] okm = await provider.HkdfExpandAsync(prk, info, outputLength: 32); ==== HkdfDeriveKeyAsync ==== Extract + Expand in one step. byte[] key = await provider.HkdfDeriveKeyAsync( ikm: sharedSecret, length: 32, salt: optionalSalt, info: contextInfo); ==== DeriveHybridKeyAsync ==== Combines classic and PQ secret. 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); // Access derived secrets var clientKey = keys.ClientHandshakeTrafficSecret; var serverKey = keys.ServerHandshakeTrafficSecret; ===== Hybrid Signatures ===== ==== CreateHybridSignatureAsync ==== Creates hybrid signature from classic signature and ML-DSA. // Create classic signature (e.g., ECDSA) byte[] classicSig = CreateEcdsaSignature(data); // Create hybrid signature byte[] hybridSig = await provider.CreateHybridSignatureAsync( data, classicSig, mlDsaPrivateKey); ===== Utility Methods ===== ==== RandomBytesAsync ==== Cryptographically secure random numbers via Web Crypto API. byte[] random = await provider.RandomBytesAsync(32); ===== Certificate Operations ===== ==== CreateEphemeralCertificateAsync ==== byte[] certBytes = await provider.CreateEphemeralCertificateAsync( "CN=Browser Certificate", TimeSpan.FromHours(1), mlDsaPrivateKey); ==== SignCertificateAsync ==== byte[] signedCert = await provider.SignCertificateAsync(tbsCertificate, privateKey); ===== Method Overview ===== ==== ML-DSA ==== ^ Method ^ Parameters ^ Return ^ | ''GenerateMlDsaKeyPairAsync'' | string algorithm | Task<(byte[], byte[])> | | ''SignMlDsaAsync'' | byte[] data, byte[] privateKey | Task | | ''VerifyMlDsaAsync'' | byte[] data, byte[] signature, byte[] publicKey | Task | ==== ML-KEM ==== ^ Method ^ Parameters ^ Return ^ | ''GenerateMlKemKeyPairAsync'' | string algorithm | Task<(byte[], byte[])> | | ''EncapsulateAsync'' | byte[] publicKey | Task<(byte[], byte[])> | | ''DecapsulateAsync'' | byte[] ciphertext, byte[] privateKey | Task | ==== Key Derivation ==== ^ Method ^ Parameters ^ Return ^ | ''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 ==== ^ Method ^ Parameters ^ Return ^ | ''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 ==== ^ Method ^ Parameters ^ Return ^ | ''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 ==== ^ Method ^ Parameters ^ Return ^ | ''RandomBytesAsync'' | int length | Task | | ''CreateHybridSignatureAsync'' | byte[] data, byte[] classicSig, byte[] pqPrivKey | Task | | ''DeriveTls13KeysAsync'' | byte[] sharedSecret, byte[] clientHello, byte[] serverHello | Task | ===== Complete Example ===== // Blazor Component @page "/crypto-demo" @inject ICryptoProvider CryptoProvider

PQ Crypto Demo

Status: @_status

@code { private string _status = "Initializing..."; protected override async Task OnInitializedAsync() { try { await CryptoProvider.InitializeAsync(); // Key Exchange Demo 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); // Signature Demo 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 = $"Error: {ex.Message}"; } } }
===== Security Notes ===== * **Requires .NET 8.0+** with Blazor WebAssembly * Browser memory is less secure than server memory * Private keys should not be stored long-term in the browser * For sensitive operations: prefer server-side processing * ''openssl.wasm'' and ''wvds-crypto.js'' must be correctly loaded **Best Practices for Browser Crypto:** * Use ephemeral keys for session-based encryption * Keep sensitive private keys on server * Do not use IndexedDB/localStorage for unencrypted keys * Configure CSP headers correctly for WASM ===== See Also ===== * [[.:start|Providers Namespace]] * [[.:icryptoprovider|ICryptoProvider]] * [[.:nativecryptoprovider|NativeCryptoProvider]] * [[.:cryptoproviderfactory|CryptoProviderFactory]] * [[en:int:pqcrypt:developer:integration:blazor-wasm|Blazor WASM Integration]] ---- //Wolfgang van der Stille @ EMSR DATA d.o.o. - Post-Quantum Cryptography Professional//