crypto: harden WebCrypto jobs and promise handling#63363
Conversation
Add a WebCrypto-specific CryptoJob mode that returns a promise from run() and resolves it when native work is finished. Encode job output directly as Web Crypto values, including CryptoKey instances and CryptoKeyPair dictionaries. Convert operation-specific setup failures from AdditionalConfig into OperationError rejections. Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Remove async function wrappers from SubtleCrypto methods while keeping their public promise-returning behaviour. Route method entry points through a shared helper that converts synchronous validation errors into rejected promises. Let the internal implementations return native job promises directly. Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Pass CryptoKey handles directly into KDF jobs instead of exporting secret bytes in lib. Normalize HKDF, PBKDF2, and Argon2 around the same job construction pattern so WebCrypto derivation paths avoid extra key material copies and keep operation failures in native job handling. Signed-off-by: Filip Skokan <panva.ip@gmail.com>
|
Review requested:
|
|
First benchmark: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1847/ Second benchmark (same target): https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1848/ |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #63363 +/- ##
==========================================
- Coverage 90.06% 90.04% -0.02%
==========================================
Files 714 714
Lines 225502 225989 +487
Branches 42628 42760 +132
==========================================
+ Hits 203089 203496 +407
- Misses 14195 14231 +36
- Partials 8218 8262 +44
🚀 New features to boost your workflow:
|
|
@Renegade334 you are incorrect in your assessments. The public methods still reject, not throw. Nothing is changed on the surface as evidenced by the lack of test changes and pre-existing passing comprehensive test cov including WPTs. |
|
Oh good, code review hid webcrypto.js as a "large diff" 🤦♂️ I see the new machinery, apologies. (FWIW |
|
@Renegade334 i can do this across the board // WebCrypto methods return promises, including for synchronous validation
// failures. Keep that conversion in one place so method bodies stay readable.
function callSubtleCryptoMethod(fn, receiver, args) {
try {
const result = ReflectApply(fn, receiver, args);
// PromiseResolve(promise) reads promise.constructor. Avoid re-wrapping
// native promises so Promise.prototype.constructor pollution is ignored.
return isPromise(result) ? result : PromiseResolve(result);
} catch (err) {
return PromiseReject(err);
}
}That way PromiseResolve(result) is only called on what isn't a native job, which i now realize includes the key export jobs for which i can do a follow up to re-introduce a simpler native key export job now that everything goes through handles already, if key exports would go through jobs too then the only PromiseResolve that would be used would be for useless early returns. And it's fairly useless for PromiseReject. Oh and the composed methods (wrapKey, encapsulateKey, decapsulateKey)... I have think about those a bit. |
|
Nice thought! Any significant impact on benchmarks? |
🤷♂️ we'll see when i do it |
|
Third benchmark: https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1849/ wrt benchmark runs 1 and 2, the async results can't be trusted. |
e9ecc87 to
6f9aedc
Compare
8aad2b1 to
68a3a2b
Compare
Avoid re-wrapping native WebCrypto promises with PromiseResolve(), since resolving a promise can read its user-mutated constructor. Add a helper for chaining internal WebCrypto job promises without consulting Promise species state, and use it for intermediate job results. Also align JWK wrapping and unwrapping with the spec's fresh-global JSON handling by detaching internal JWK values from user prototypes. Use the internal UTF-8 encoder/decoder bindings instead of shared TextEncoder/TextDecoder prototype methods. Expand the WebCrypto prototype pollution regression test to cover SubtleCrypto methods, export formats, zero-length KDF results, JWK toJSON/kty pollution, and encoder/decoder prototype poisoning. Signed-off-by: Filip Skokan <panva.ip@gmail.com>
|
fix one find 2 more... the webcrypto job mode is a good start but this really shows we need all methods to end with a native promise to get rid of the js promise machinery. export, import, composite methods, ... 😭 |
After quite the BoringSSL detour I'm coming back to Web Cryptography hardening related to #59699 (comment) and #59699 (comment) in hopes that we can eventually mark #59699 as resolved.
crypto: add WebCrypto CryptoJob mode
Add a WebCrypto-specific CryptoJob mode that returns a promise from run() and resolves it when native work is finished.
Encode job output directly as Web Crypto values, including CryptoKey instances and CryptoKeyPair dictionaries. Convert operation-specific setup failures from AdditionalConfig into OperationError rejections.
crypto: remove async from WebCrypto methods
Remove async function wrappers from SubtleCrypto methods while keeping their public promise-returning behaviour.
Route method entry points through a shared helper that converts synchronous validation errors into rejected promises. Let the internal implementations return native job promises directly.
crypto: pass CryptoKey handles to KDF jobs
Pass CryptoKey handles directly into KDF jobs instead of exporting secret bytes in lib.
Normalize HKDF, PBKDF2, and Argon2 around the same job construction pattern so WebCrypto derivation paths avoid extra key material copies and keep operation failures in native job handling.
crypto: harden WebCrypto against prototype pollution
Avoid re-wrapping native WebCrypto promises with PromiseResolve(), since resolving a promise can read its user-mutated constructor.
Add a helper for chaining internal WebCrypto job promises without consulting Promise species state, and use it for intermediate job results.
Also align JWK wrapping and unwrapping with the spec's fresh-global JSON handling by detaching internal JWK values from user prototypes. Use the internal UTF-8 encoder/decoder bindings instead of shared TextEncoder/TextDecoder prototype methods.
Expand the WebCrypto prototype pollution regression test to cover SubtleCrypto methods, export formats, zero-length KDF results, JWK toJSON/kty pollution, and encoder/decoder prototype poisoning.