denoland / std

The Deno Standard Library
https://jsr.io/@std
MIT License
3.01k stars 603 forks source link

Secure Curves in the Web Cryptography API - secp256k1, X25519, X448, Ed25519, Ed448 #4224

Open suchislife801 opened 8 months ago

suchislife801 commented 8 months ago

Is your feature request related to a problem? Please describe.

The current Web Cryptography API lacks support for Secure Curves such assecp256k1, X25519, X448, Ed25519, Ed448, which are essential for robust cryptographic operations. This limitation restricts the API's utility in scenarios requiring advanced security, such as in the implementation of certain modern cryptographic protocols like Signal's X3DH Key Agreement protocol for example.

Describe the solution you'd like

I propose the integration of Secure Curves into the Web Cryptography API, as outlined in the WICG draft (https://wicg.github.io/webcrypto-secure-curves/). This addition would enhance the cryptographic capabilities of the API, allowing developers to leverage these advanced curves for more secure and efficient cryptographic operations.

The following curves are currectly not available:

x25519 - The "X25519" algorithm identifier is used to perform key agreement using the X25519 algorithm specified in [RFC7748].

x448 - The "X448" algorithm identifier is used to perform key agreement using the X448 algorithm specified in [RFC7748].

ed25519 - The "Ed25519" algorithm identifier is used to perform signing and verification using the Ed25519 algorithm specified in [RFC8032].

ed448 - The "Ed448" algorithm identifier is used to perform signing and verification using the Ed448 algorithm specified in [RFC8032].

Describe alternatives you've considered

An alternative could be to use third-party libraries that implement Secure Curves. However, this approach may not be as efficient or secure as having native support within the Web Cryptography API itself. Native support ensures standardized implementation and better integration with the web platform.

iuioiua commented 8 months ago

I like the idea. I'm happy to hear other thoughts, too. Any opinions on this, @jeremyBanks?

kt3k commented 8 months ago

I propose the integration of Secure Curves into the Web Cryptography API

Do you propose this feature as addition to builtin Web Crypto API? Or do you suggest this feature as extension in std/crypto?

littledivy commented 8 months ago

We have partial support for Secure curves in WebCrypto. Here is the tracking issue: https://github.com/denoland/deno/issues/16145

suchislife801 commented 8 months ago

@kt3k The Web Crypto API looks so clean. Deno has integrated new digests via std/crypto into the Web Crypto API and the experience is seamless so that works too.

Example 1: X25519 key agreement

// Generate a key pair for Alice.
const alice_x25519_key = await crypto.subtle.generateKey('X25519', false /* extractable */, ['deriveKey']);
const alice_private_key = alice_x25519_key.privateKey;

// Normally, the public key would be sent by Bob to Alice in advance over some authenticated channel.
// In this example, we'll generate another key pair and use its public key instead.
const bob_x25519_key = await crypto.subtle.generateKey('X25519', false /* extractable */, ['deriveKey']);
const bob_public_key = bob_x25519_key.publicKey;

// Perform the key agreement.
const alice_x25519_params = { name: 'X25519', public: bob_public_key };
const alice_shared_key = await crypto.subtle.deriveKey(alice_x25519_params, alice_private_key, 'HKDF', false /* extractable */, ['deriveKey']);

// Derive a symmetric key from the result.
const salt = crypto.getRandomValues(new Uint8Array(32));
const info = new TextEncoder().encode('X25519 key agreement for an AES-GCM-256 key');
const hkdf_params = { name: 'HKDF', hash: 'SHA-256', salt, info };
const gcm_params = { name: 'AES-GCM', length: 256 };
const alice_symmetric_key = await crypto.subtle.deriveKey(hkdf_params, alice_shared_key, gcm_params, false /* extractable */, ['encrypt', 'decrypt']);

// Encrypt some data with the symmetric key, and send it to Bob. The IV must be passed along as well.
const iv = crypto.getRandomValues(new Uint8Array(12));
const message = new TextEncoder().encode('Hi Bob!');
const encrypted = await crypto.subtle.encrypt({ ...gcm_params, iv }, alice_symmetric_key, message);

// On Bob's side, Alice's public key and Bob's private key are used, instead.
// To get the same result, Alice and Bob must agree on using the same salt and info.
const alice_public_key = alice_x25519_key.publicKey;
const bob_private_key = bob_x25519_key.privateKey;
const bob_x25519_params = { name: 'X25519', public: alice_public_key };
const bob_shared_key = await crypto.subtle.deriveKey(bob_x25519_params, bob_private_key, 'HKDF', false /* extractable */, ['deriveKey']);
const bob_symmetric_key = await crypto.subtle.deriveKey(hkdf_params, bob_shared_key, gcm_params, false /* extractable */, ['encrypt', 'decrypt']);

// On Bob's side, the data can be decrypted.
const decrypted = await crypto.subtle.decrypt({ ...gcm_params, iv }, bob_symmetric_key, encrypted);
const decrypted_message = new TextDecoder().decode(decrypted);
jeremyBanks commented 7 months ago

I think this would be a valuable addition. I'm not sure whether the stage it's at in standardization is or is not sufficient for inclusion in the built-in WebCrypto implementation (maybe it is), but it's certainly widely used enough to justify inclusion in std/crypto. But non-extractable keys might not work appropriately in a library implementation, so maybe some trade-off would have to be made there... (can't dig in myself right now because I have a newborn but I like the idea if it can be practical).

suchislife801 commented 7 months ago

Aren't all keys currently extractable as JSON Web Key?