Skip to content
File

Blob: src/workerd/api/crypto/crypto.h

cpp792 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6// WebCrypto API
7 
8#include <workerd/api/streams/writable.h>
9#include <workerd/io/features.h>
10#include <workerd/jsg/jsg.h>
11#include <workerd/jsg/jsvalue.h>
12 
13#include <openssl/base.h> // for EVP_MD_CTX, X509
14 
15#include <bit>
16 
17namespace workerd::api {
18namespace node {
19class CryptoImpl;
20}
21namespace {
22class EdDsaKey;
23class EllipticKey;
24} // namespace
25 
26// Subset of recognized key usage values.
27//
28// https://w3c.github.io/webcrypto/#dfn-RecognizedKeyUsage
29class CryptoKeyUsageSet {
30 public:
31 static constexpr CryptoKeyUsageSet encrypt() {
32 return 1 << 0;
33 }
34 static constexpr CryptoKeyUsageSet decrypt() {
35 return 1 << 1;
36 }
37 static constexpr CryptoKeyUsageSet sign() {
38 return 1 << 2;
39 }
40 static constexpr CryptoKeyUsageSet verify() {
41 return 1 << 3;
42 }
43 static constexpr CryptoKeyUsageSet deriveKey() {
44 return 1 << 4;
45 }
46 static constexpr CryptoKeyUsageSet deriveBits() {
47 return 1 << 5;
48 }
49 static constexpr CryptoKeyUsageSet wrapKey() {
50 return 1 << 6;
51 }
52 static constexpr CryptoKeyUsageSet unwrapKey() {
53 return 1 << 7;
54 }
55 
56 static constexpr CryptoKeyUsageSet publicKeyMask() {
57 return encrypt() | verify() | wrapKey();
58 }
59 
60 static constexpr CryptoKeyUsageSet privateKeyMask() {
61 return decrypt() | sign() | unwrapKey() | deriveKey() | deriveBits();
62 }
63 
64 static constexpr CryptoKeyUsageSet derivationKeyMask() {
65 return deriveKey() | deriveBits();
66 }
67 
68 CryptoKeyUsageSet(): set(0) {}
69 
70 CryptoKeyUsageSet operator&(CryptoKeyUsageSet other) const {
71 return set & other.set;
72 }
73 CryptoKeyUsageSet operator|(CryptoKeyUsageSet other) const {
74 return set | other.set;
75 }
76 
77 CryptoKeyUsageSet& operator&=(CryptoKeyUsageSet other) {
78 set &= other.set;
79 return *this;
80 }
81 
82 CryptoKeyUsageSet& operator|=(CryptoKeyUsageSet other) {
83 set |= other.set;
84 return *this;
85 }
86 
87 // True if and only if this is a subset of the given set.
88 inline bool operator<=(CryptoKeyUsageSet superset) const {
89 return (superset & *this) == *this;
90 }
91 
92 inline bool operator==(CryptoKeyUsageSet other) const {
93 return set == other.set;
94 }
95 
96 unsigned int size() const {
97 return std::popcount(set);
98 }
99 bool isSingleton() const {
100 return size() == 1;
101 }
102 
103 // The recognized name. This must be a singleton.
104 kj::StringPtr name() const;
105 
106 // A singleton with the given name.
107 static CryptoKeyUsageSet byName(kj::StringPtr name);
108 
109 // All singletons, in the order defined by the spec (encrypt, decrypt, sign, verify, ...).
110 static kj::ArrayPtr<const CryptoKeyUsageSet> singletons();
111 
112 enum class Context { generate, importSecret, importPublic, importPrivate };
113 
114 // Parses a list of key usage strings. Throws if any are not recognized or not in mask.
115 static CryptoKeyUsageSet validate(kj::StringPtr normalizedName,
116 Context ctx,
117 kj::ArrayPtr<const kj::String> actual,
118 CryptoKeyUsageSet mask);
119 
120 template <typename Func>
121 auto map(Func f) const -> kj::Array<decltype(f(*this))> {
122 auto strings = kj::heapArrayBuilder<decltype(f(*this))>(size());
123 for (auto& singleton: singletons()) {
124 if (singleton <= *this) strings.add(f(singleton));
125 }
126 return strings.finish();
127 }
128 
129 private:
130 constexpr CryptoKeyUsageSet(uint8_t set): set(set) {}
131 uint8_t set;
132};
133 
134// =======================================================================================
135// SubtleCrypto and CryptoKey
136 
137// Represents keying material. Users get an object of this type by calling SubtleCrypto's
138// `importKey()`, `generateKey()`, or `deriveKey()` methods. The user can then use the object by
139// passing it as a parameter to other SubtleCrypto methods.
140class CryptoKey: public jsg::Object {
141 public:
142 // KeyAlgorithm dictionaries
143 //
144 // These dictionaries implement CryptoKey's `algorithm` property. They allow user code to inspect
145 // which algorithm a particular CryptoKey is used for, and what algorithm-specific parameters it
146 // might have. These are similar to the Algorithm-derived dictionaries used as parameters to
147 // SubtleCrypto's interface (see the SubtleCrypto class below), but they are specific to
148 // CryptoKey. Like Algorithm, all of these dictionaries notionally derive from a KeyAlgorithm base
149 // class.
150 //
151 // One difference between CryptoKey::KeyAlgorithm dictionaries and SubtleCrypto::Algorithm
152 // dictionaries is that KeyAlgorithms use a kj::StringPtr to store their algorithm names, because
153 // we know that they will only ever point to internal static strings of normalized algorithm
154 // names.
155 
156 struct KeyAlgorithm {
157 kj::StringPtr name;
158 JSG_STRUCT(name);
159 JSG_MEMORY_INFO(KeyAlgorithm) {}
160 };
161 
162 struct AesKeyAlgorithm {
163 // "AES-CTR", "AES-GCM", "AES-CBC", "AES-KW"
164 kj::StringPtr name;
165 
166 // Length in bits of the key.
167 uint16_t length;
168 
169 JSG_STRUCT(name, length);
170 
171 JSG_MEMORY_INFO(AesKeyAlgorithm) {}
172 };
173 
174 struct HmacKeyAlgorithm {
175 // "HMAC"
176 kj::StringPtr name;
177 
178 // The inner hash function to use.
179 KeyAlgorithm hash;
180 
181 // Length in bits of the key. The spec wants this to be an unsigned long, but whatever.
182 // TODO(someday): Reexamine use of uint16_t in these algorithm structures.
183 // We picked uint16_t to work around ambiguous bindings for uint32_t in
184 // jsg::PrimitiveWrapper::wrap(). HMAC, at least, allows very long keys.
185 uint16_t length;
186 
187 JSG_STRUCT(name, hash, length);
188 JSG_MEMORY_INFO(HmacKeyAlgorithm) {}
189 };
190 
191 struct RsaKeyAlgorithm {
192 // "RSASSA-PKCS1-v1_5", "RSA-PSS", "RSA-OAEP"
193 kj::StringPtr name;
194 
195 // The length, in bits, of the RSA modulus. The spec would have this be an unsigned long.
196 uint16_t modulusLength;
197 
198 // The RSA public exponent (in unsigned big-endian form)
199 jsg::JsRef<jsg::JsBufferSource> publicExponent;
200 
201 // The hash algorithm that is used with this key.
202 jsg::Optional<KeyAlgorithm> hash;
203 
204 RsaKeyAlgorithm clone(jsg::Lock& js) const {
205 auto pe = publicExponent.getHandle(js);
206 auto data = pe.asArrayPtr();
207 // Should only happen if the flag is enabled and an algorithm field is cloned twice.
208 if (FeatureFlags::get(js).getCryptoPreservePublicExponent()) {
209 auto exp = jsg::JsUint8Array::create(js, data);
210 return {name, modulusLength, jsg::JsBufferSource(exp).addRef(js), hash};
211 } else {
212 auto exp = jsg::JsArrayBuffer::create(js, data);
213 return {name, modulusLength, jsg::JsBufferSource(exp).addRef(js), hash};
214 }
215 }
216 
217 JSG_STRUCT(name, modulusLength, publicExponent, hash);
218 
219 JSG_MEMORY_INFO(RsaKeyAlgorithm) {}
220 };
221 
222 struct EllipticKeyAlgorithm {
223 // "ECDSA" or "ECDH"
224 kj::StringPtr name;
225 
226 // "P-256", "P-384", or "P-521"
227 kj::StringPtr namedCurve;
228 
229 JSG_STRUCT(name, namedCurve);
230 
231 JSG_MEMORY_INFO(EllipticKeyAlgorithm) {}
232 };
233 
234 // Catch-all that can be used for extension algorithms. Combines fields of several known types.
235 struct ArbitraryKeyAlgorithm {
236 // TODO(cleanup): Should we just replace AlgorithmVariant with this? Note we'd have to add
237 // `publicExponent` which is currently a problem because it makes the type non-copyable...
238 // Alternatively, should we create some better way to abstract this?
239 
240 kj::StringPtr name;
241 jsg::Optional<KeyAlgorithm> hash;
242 jsg::Optional<kj::StringPtr> namedCurve;
243 jsg::Optional<uint16_t> length;
244 
245 JSG_STRUCT(name, hash, namedCurve, length);
246 };
247 
248 // Used as part of the Node.js crypto implementation of KeyObject.
249 // Defined here instead of api/node/crypto.h because it it is needed
250 // by CryptoKey::Impl to provide the actual implementation.
251 struct AsymmetricKeyDetails {
252 jsg::Optional<uint32_t> modulusLength;
253 jsg::Optional<jsg::JsRef<jsg::JsArrayBuffer>> publicExponent;
254 // TODO(later): BoringSSL does not currently support getting the RSA-PSS
255 // details for an RSA key. Once it does, we can update our impl and add
256 // these fields.
257 // jsg::Optional<kj::String> hashAlgorithm;
258 // jsg::Optional<kj::String> mgf1HashAlgorithm;
259 // jsg::Optional<uint32_t> saltLength;
260 jsg::Optional<uint32_t> divisorLength;
261 jsg::Optional<kj::String> namedCurve;
262 JSG_STRUCT(modulusLength,
263 publicExponent,
264 // hashAlgorithm,
265 // mgf1HashAlgorithm,
266 // saltLength,
267 divisorLength,
268 namedCurve);
269 };
270 AsymmetricKeyDetails getAsymmetricKeyDetails(jsg::Lock& js) const;
271 
272 ~CryptoKey() noexcept(false);
273 
274 // Returns the name of this CryptoKey's algorithm in a normalized, statically-allocated string.
275 kj::StringPtr getAlgorithmName() const;
276 
277 // JS API
278 
279 using AlgorithmVariant = kj::OneOf<KeyAlgorithm,
280 AesKeyAlgorithm,
281 HmacKeyAlgorithm,
282 RsaKeyAlgorithm,
283 EllipticKeyAlgorithm,
284 ArbitraryKeyAlgorithm>;
285 
286 AlgorithmVariant getAlgorithm(jsg::Lock& js) const;
287 kj::StringPtr getType() const;
288 bool getExtractable() const;
289 kj::Array<kj::StringPtr> getUsages() const;
290 CryptoKeyUsageSet getUsageSet() const;
291 
292 JSG_RESOURCE_TYPE(CryptoKey) {
293 JSG_READONLY_INSTANCE_PROPERTY(type, getType);
294 JSG_READONLY_INSTANCE_PROPERTY(extractable, getExtractable);
295 JSG_READONLY_INSTANCE_PROPERTY(algorithm, getAlgorithm);
296 JSG_READONLY_INSTANCE_PROPERTY(usages, getUsages);
297 }
298 
299 // HACK: Needs to be public so derived classes can inherit from it.
300 class Impl;
301 
302 // Treat as private -- needs to be public for js.alloc<T>()...
303 explicit CryptoKey(kj::Own<Impl> impl);
304 
305 // Compare the contents of this key with the other. Will return false if
306 // either key is not extractable or if the keys are a different type.
307 // For secret keys, we will compare only the actual key material and not
308 // the algorithm parameters or the algorithm name. We will also ensure
309 // that a timing-safe comparison is used for the key material.
310 bool operator==(const CryptoKey& other) const;
311 
312 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const;
313 
314 bool verifyX509Public(const X509* x509) const;
315 bool verifyX509Private(const X509* x509) const;
316 
317 private:
318 kj::Own<Impl> impl;
319 
320 void visitForGc(jsg::GcVisitor& visitor);
321 
322 friend class SubtleCrypto;
323 friend class EllipticKey;
324 friend class EdDsaKey;
325 friend class node::CryptoImpl;
326};
327 
328struct CryptoKeyPair {
329 jsg::Ref<CryptoKey> publicKey;
330 jsg::Ref<CryptoKey> privateKey;
331 
332 JSG_STRUCT(publicKey, privateKey);
333};
334 
335class SubtleCrypto: public jsg::Object {
336 public:
337 // Algorithm dictionaries
338 //
339 // Every method of SubtleCrypto except `exportKey()` takes an `algorithm` parameter, usually as the
340 // first argument. This can usually be a raw string algorithm name, or an object with a `name`
341 // field and other fields. The other fields differ based on which algorithm is named and which
342 // function is being called. We achieve polymorphism here by making all the fields except `name`
343 // be `jsg::Optional`... ugly, but it works.
344 
345 // Type of the `algorithm` parameter passed to `digest()`. Also used as the type of the `hash`
346 // parameter of many other algorithm structs.
347 struct HashAlgorithm {
348 kj::String name;
349 
350 JSG_STRUCT(name);
351 };
352 
353 // Type of the `algorithm` parameter passed to `encrypt()` and `decrypt()`. Different
354 // algorithms call for different fields.
355 struct EncryptAlgorithm {
356 // E.g. "AES-GCM"
357 kj::String name;
358 
359 // For AES: The initialization vector use. May be up to 2^64-1 bytes long.
360 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> iv;
361 
362 // The additional authentication data to include.
363 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> additionalData;
364 
365 // The desired length of the authentication tag. May be 0 - 128.
366 // Note: the spec specifies this as a Web IDL byte (== signed char in C++), not an int, but JS
367 // has no such 8-bit integer animal.
368 jsg::Optional<int> tagLength;
369 
370 // The initial value of the counter block for AES-CTR.
371 // https://www.w3.org/TR/WebCryptoAPI/#aes-ctr-params
372 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> counter;
373 
374 // The length, in bits, of the rightmost part of the counter block that is incremented.
375 // See above why we use int instead of int8_t.
376 // https://www.w3.org/TR/WebCryptoAPI/#aes-ctr-params
377 jsg::Optional<int> length;
378 
379 // The optional label/application data to associate with the message (for RSA-OAEP)
380 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> label;
381 
382 JSG_STRUCT(name, iv, additionalData, tagLength, counter, length, label);
383 };
384 
385 // Type of the `algorithm` parameter passed to `sign()` and `verify()`. Different
386 // algorithms call for different fields.
387 struct SignAlgorithm {
388 // E.g. "RSASSA-PKCS1-v1_5", "ECDSA"
389 kj::String name;
390 
391 // ECDSA wants the hash to be specified at call time rather than import
392 // time.
393 jsg::Optional<kj::OneOf<kj::String, HashAlgorithm>> hash;
394 
395 // Not part of the WebCrypto spec. Used by an extension.
396 jsg::Optional<int> dataLength;
397 
398 // Used for RSA-PSS
399 jsg::Optional<int> saltLength;
400 
401 JSG_STRUCT(name, hash, dataLength, saltLength);
402 };
403 
404 // Type of the `algorithm` parameter passed to `generateKey()`. Different algorithms call for
405 // different fields.
406 struct GenerateKeyAlgorithm {
407 // E.g. "HMAC", "RSASSA-PKCS1-v1_5", "ECDSA", ...
408 kj::String name;
409 
410 // For signing algorithms where the hash is specified at import time, identifies the hash
411 // function to use, e.g. "SHA-256".
412 jsg::Optional<kj::OneOf<kj::String, HashAlgorithm>> hash;
413 
414 // For RSA algorithms: The length in bits of the RSA modulus.
415 jsg::Optional<int> modulusLength;
416 
417 // For RSA algorithms
418 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> publicExponent;
419 
420 // For AES algorithms or when name == "HMAC": The length in bits of the key.
421 jsg::Optional<int> length;
422 
423 // When name == "ECDSA": "P-256", "P-384", or "P-521"
424 jsg::Optional<kj::String> namedCurve;
425 
426 JSG_STRUCT(name, hash, modulusLength, publicExponent, length, namedCurve);
427 };
428 
429 // Type of the `algorithm` parameter passed to `importKey()`, as well as the
430 // `derivedKeyAlgorithm` parameter to `deriveKey()`. Different algorithms call for different
431 // fields.
432 struct ImportKeyAlgorithm {
433 // E.g. "HMAC", "RSASSA-PKCS1-v1_5", "ECDSA", ...
434 kj::String name;
435 
436 // For signing algorithms where the hash is specified at import time, identifies the hash
437 // function to use, e.g. "SHA-256".
438 jsg::Optional<kj::OneOf<kj::String, HashAlgorithm>> hash;
439 
440 // When name == "HMAC": The length in bits of the key.
441 jsg::Optional<int> length;
442 
443 // When name == "ECDSA": "P-256", "P-384", or "P-521"
444 jsg::Optional<kj::String> namedCurve;
445 
446 // Not part of the WebCrypto spec. Used by an extension to indicate that curve points are in
447 // compressed format. (The standard algorithms do not recognize this option.)
448 jsg::Optional<bool> compressed;
449 
450 JSG_STRUCT(name, hash, length, namedCurve, compressed);
451 };
452 
453 // Type of the `algorithm` parameter passed to `deriveKey()`. Different algorithms call for
454 // different fields.
455 struct DeriveKeyAlgorithm {
456 // e.g. "PBKDF2", "ECDH", etc
457 kj::String name;
458 
459 // PBKDF2 parameters
460 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> salt;
461 jsg::Optional<int> iterations;
462 jsg::Optional<kj::OneOf<kj::String, HashAlgorithm>> hash;
463 
464 // ECDH parameters
465 jsg::Optional<jsg::Ref<CryptoKey>> $public;
466 
467 // HKDF parameters (some shared with PBKDF2)
468 
469 // Bit string that corresponds to the context and application specific context for the derived
470 // keying material
471 jsg::Optional<jsg::JsRef<jsg::JsBufferSource>> info;
472 
473 JSG_STRUCT(name, salt, iterations, hash, $public, info);
474 };
475 
476 // https://www.w3.org/TR/WebCryptoAPI/#JsonWebKey-dictionary
477 struct JsonWebKey {
478 
479 struct RsaOtherPrimesInfo {
480 // The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
481 jsg::Optional<kj::String> r;
482 jsg::Optional<kj::String> d;
483 jsg::Optional<kj::String> t;
484 
485 JSG_STRUCT(r, d, t);
486 JSG_STRUCT_TS_OVERRIDE(RsaOtherPrimesInfo); // Rename from SubtleCryptoJsonWebKeyRsaOtherPrimesInfo
487 };
488 
489 // The following fields are defined in Section 3.1 of JSON Web Key (RFC 7517).
490 // NOTE: The Web Crypto spec's IDL for JsonWebKey considers `kty` optional, yet the RFC lists it
491 // as required.
492 kj::String kty;
493 jsg::Optional<kj::String> use;
494 jsg::Optional<kj::Array<kj::String>> key_ops;
495 jsg::Optional<kj::String> alg;
496 
497 // The following fields are defined in JSON Web Key Parameters Registration
498 jsg::Optional<bool> ext;
499 
500 // The following fields are defined in Section 6 of JSON Web Algorithms
501 jsg::Optional<kj::String> crv;
502 jsg::Optional<kj::String> x;
503 jsg::Optional<kj::String> y;
504 jsg::Optional<kj::String> d;
505 jsg::Optional<kj::String> n;
506 jsg::Optional<kj::String> e;
507 jsg::Optional<kj::String> p;
508 jsg::Optional<kj::String> q;
509 jsg::Optional<kj::String> dp;
510 jsg::Optional<kj::String> dq;
511 jsg::Optional<kj::String> qi;
512 jsg::Optional<kj::Array<RsaOtherPrimesInfo>> oth;
513 // TODO(conform): Support multiprime RSA keys. This used to be jsg::Unimplemented but needs to
514 // be properly defined for exporting JWK of other keys. On the other hand, are we even going
515 // to bother adding support for multiprime RSA keys? Chromium doesn't AFAICT...
516 jsg::Optional<kj::String> k;
517 
518 JSG_STRUCT(kty, use, key_ops, alg, ext, crv, x, y, d, n, e, p, q, dp, dq, qi, oth, k);
519 JSG_STRUCT_TS_OVERRIDE(JsonWebKey); // Rename from SubtleCryptoJsonWebKey
520 };
521 
522 using ImportKeyData = kj::OneOf<kj::Array<kj::byte>, JsonWebKey>;
523 using ExportKeyData = kj::OneOf<jsg::JsRef<jsg::JsArrayBuffer>, JsonWebKey>;
524 
525 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> encrypt(jsg::Lock& js,
526 kj::OneOf<kj::String, EncryptAlgorithm> algorithm,
527 const CryptoKey& key,
528 kj::Array<const kj::byte> plainText);
529 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> decrypt(jsg::Lock& js,
530 kj::OneOf<kj::String, EncryptAlgorithm> algorithm,
531 const CryptoKey& key,
532 kj::Array<const kj::byte> cipherText);
533 
534 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> sign(jsg::Lock& js,
535 kj::OneOf<kj::String, SignAlgorithm> algorithm,
536 const CryptoKey& key,
537 kj::Array<const kj::byte> data);
538 jsg::Promise<bool> verify(jsg::Lock& js,
539 kj::OneOf<kj::String, SignAlgorithm> algorithm,
540 const CryptoKey& key,
541 kj::Array<const kj::byte> signature,
542 kj::Array<const kj::byte> data);
543 
544 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> digest(jsg::Lock& js,
545 kj::OneOf<kj::String, HashAlgorithm> algorithm,
546 kj::Array<const kj::byte> data);
547 
548 jsg::Promise<kj::OneOf<jsg::Ref<CryptoKey>, CryptoKeyPair>> generateKey(jsg::Lock& js,
549 kj::OneOf<kj::String, GenerateKeyAlgorithm> algorithm,
550 bool extractable,
551 kj::Array<kj::String> keyUsages);
552 
553 jsg::Promise<jsg::Ref<CryptoKey>> deriveKey(jsg::Lock& js,
554 kj::OneOf<kj::String, DeriveKeyAlgorithm> algorithm,
555 const CryptoKey& baseKey,
556 kj::OneOf<kj::String, ImportKeyAlgorithm> derivedKeyAlgorithm,
557 bool extractable,
558 kj::Array<kj::String> keyUsages);
559 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> deriveBits(jsg::Lock& js,
560 kj::OneOf<kj::String, DeriveKeyAlgorithm> algorithm,
561 const CryptoKey& baseKey,
562 // The operation needs to be able to take both undefined and null
563 // and handle them equivalently... if we just used jsg::Optional<int>
564 // here, null would be coerced to 0. If we just used kj::Maybe<int>
565 // here, undefined would be an error. So we need to use an optional
566 // maybe int in order to treat undefined and null as being equivalent.
567 jsg::Optional<kj::Maybe<int>> length);
568 
569 jsg::Promise<jsg::Ref<CryptoKey>> importKey(jsg::Lock& js,
570 kj::String format,
571 ImportKeyData keyData,
572 kj::OneOf<kj::String, ImportKeyAlgorithm> algorithm,
573 bool extractable,
574 kj::Array<kj::String> keyUsages);
575 
576 // NOT VISIBLE TO JS: like importKey() but return the key, not a promise.
577 jsg::Ref<CryptoKey> importKeySync(jsg::Lock& js,
578 kj::StringPtr format,
579 ImportKeyData keyData,
580 ImportKeyAlgorithm algorithm,
581 bool extractable,
582 kj::ArrayPtr<const kj::String> keyUsages);
583 
584 jsg::Promise<ExportKeyData> exportKey(jsg::Lock& js, kj::String format, const CryptoKey& key);
585 
586 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> wrapKey(jsg::Lock& js,
587 kj::String format,
588 const CryptoKey& key,
589 const CryptoKey& wrappingKey,
590 kj::OneOf<kj::String, EncryptAlgorithm> wrapAlgorithm,
591 const jsg::TypeHandler<JsonWebKey>& jwkHandler);
592 jsg::Promise<jsg::Ref<CryptoKey>> unwrapKey(jsg::Lock& js,
593 kj::String format,
594 kj::Array<const kj::byte> wrappedKey,
595 const CryptoKey& unwrappingKey,
596 kj::OneOf<kj::String, EncryptAlgorithm> unwrapAlgorithm,
597 kj::OneOf<kj::String, ImportKeyAlgorithm> unwrappedKeyAlgorithm,
598 bool extractable,
599 kj::Array<kj::String> keyUsages,
600 const jsg::TypeHandler<JsonWebKey>& jwkHandler);
601 
602 // This is a non-standard extension based off Node.js' implementation of crypto.timingSafeEqual.
603 bool timingSafeEqual(jsg::JsBufferSource a, jsg::JsBufferSource b);
604 
605 JSG_RESOURCE_TYPE(SubtleCrypto) {
606 JSG_METHOD(encrypt);
607 JSG_METHOD(decrypt);
608 JSG_METHOD(sign);
609 JSG_METHOD(verify);
610 JSG_METHOD(digest);
611 JSG_METHOD(generateKey);
612 JSG_METHOD(deriveKey);
613 JSG_METHOD(deriveBits);
614 JSG_METHOD(importKey);
615 JSG_METHOD(exportKey);
616 JSG_METHOD(wrapKey);
617 JSG_METHOD(unwrapKey);
618 JSG_METHOD(timingSafeEqual);
619 
620 JSG_TS_OVERRIDE({
621 wrapKey(format: string,
622 key: CryptoKey,
623 wrappingKey: CryptoKey,
624 wrapAlgorithm: string | SubtleCryptoEncryptAlgorithm)
625 : Promise<ArrayBuffer>;
626 deriveBits(algorithm: string | SubtleCryptoDeriveKeyAlgorithm,
627 baseKey : CryptoKey,
628 length? : number | null)
629 : Promise<ArrayBuffer>;
630 digest(algorithm: string | SubtleCryptoHashAlgorithm,
631 data: ArrayBuffer | ArrayBufferView)
632 : Promise<ArrayBuffer>;
633 sign(algorithm: string | SubtleCryptoSignAlgorithm,
634 key: CryptoKey,
635 data: ArrayBuffer | ArrayBufferView)
636 : Promise<ArrayBuffer>;
637 decrypt(algorithm: string | SubtleCryptoEncryptAlgorithm,
638 key: CryptoKey,
639 cipherText: ArrayBuffer | ArrayBufferView)
640 : Promise<ArrayBuffer>;
641 encrypt(algorithm: string | SubtleCryptoEncryptAlgorithm,
642 key: CryptoKey,
643 plainText: ArrayBuffer | ArrayBufferView)
644 : Promise<ArrayBuffer>;
645 exportKey(format: string, key: CryptoKey) : Promise<ArrayBuffer | JsonWebKey>;
646 });
647 }
648};
649 
650// =======================================================================================
651// DigestStream is a non-standard extension that provides a way of generating
652// a hash digest from streaming data. It combines Web Crypto concepts into a
653// WritableStream and is compatible with both APIs.
654class DigestContext {
655 public:
656 virtual ~DigestContext() noexcept = default;
657 virtual void write(kj::ArrayPtr<kj::byte> buffer) = 0;
658 virtual jsg::JsArrayBuffer close(jsg::Lock& js) = 0;
659};
660 
661class DigestStream: public WritableStream {
662 public:
663 using DigestContextPtr = kj::Own<DigestContext>;
664 using Algorithm = kj::OneOf<kj::String, SubtleCrypto::HashAlgorithm>;
665 
666 explicit DigestStream(kj::Own<WritableStreamController> controller,
667 SubtleCrypto::HashAlgorithm algorithm,
668 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>>::Resolver resolver,
669 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>> promise);
670 
671 static jsg::Ref<DigestStream> constructor(jsg::Lock& js, Algorithm algorithm);
672 
673 jsg::MemoizedIdentity<jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>>>& getDigest() {
674 return promise;
675 }
676 void dispose(jsg::Lock& js);
677 uint64_t getBytesWritten() const {
678 return bytesWritten;
679 }
680 
681 JSG_RESOURCE_TYPE(DigestStream, CompatibilityFlags::Reader flags) {
682 JSG_INHERIT(WritableStream);
683 if (flags.getJsgPropertyOnPrototypeTemplate()) {
684 JSG_READONLY_PROTOTYPE_PROPERTY(digest, getDigest);
685 } else {
686 JSG_READONLY_INSTANCE_PROPERTY(digest, getDigest);
687 }
688 JSG_READONLY_PROTOTYPE_PROPERTY(bytesWritten, getBytesWritten);
689 JSG_DISPOSE(dispose);
690 
691 JSG_TS_OVERRIDE(extends WritableStream<ArrayBuffer | ArrayBufferView> {
692 readonly digest: Promise<ArrayBuffer>;
693 });
694 }
695 
696 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const;
697 
698 private:
699 static DigestContextPtr initContext(SubtleCrypto::HashAlgorithm& algorithm);
700 
701 struct Ready {
702 SubtleCrypto::HashAlgorithm algorithm;
703 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>>::Resolver resolver;
704 DigestContextPtr context;
705 Ready(SubtleCrypto::HashAlgorithm algorithm,
706 jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>>::Resolver resolver)
707 : algorithm(kj::mv(algorithm)),
708 resolver(kj::mv(resolver)),
709 context(initContext(this->algorithm)) {}
710 };
711 jsg::MemoizedIdentity<jsg::Promise<jsg::JsRef<jsg::JsArrayBuffer>>> promise;
712 kj::OneOf<Ready, StreamStates::Closed, StreamStates::Errored> state;
713 uint64_t bytesWritten = 0;
714 
715 kj::Maybe<StreamStates::Errored> write(jsg::Lock& js, kj::ArrayPtr<kj::byte> buffer);
716 kj::Maybe<StreamStates::Errored> close(jsg::Lock& js);
717 void abort(jsg::Lock& js, jsg::JsValue reason);
718 
719 void visitForGc(jsg::GcVisitor& visitor);
720};
721 
722// =======================================================================================
723// Crypto
724 
725// Implements the Crypto interface as prescribed by:
726// https://www.w3.org/TR/WebCryptoAPI/#crypto-interface
727class Crypto: public jsg::Object {
728 public:
729 Crypto(jsg::Lock& js): subtle(js.alloc<SubtleCrypto>()) {}
730 
731 jsg::JsArrayBufferView getRandomValues(jsg::JsArrayBufferView buffer);
732 
733 kj::String randomUUID();
734 
735 jsg::Ref<SubtleCrypto> getSubtle() {
736 return subtle.addRef();
737 }
738 
739 JSG_RESOURCE_TYPE(Crypto, CompatibilityFlags::Reader flags) {
740 if (flags.getJsgPropertyOnPrototypeTemplate()) {
741 JSG_READONLY_PROTOTYPE_PROPERTY(subtle, getSubtle);
742 } else {
743 JSG_READONLY_INSTANCE_PROPERTY(subtle, getSubtle);
744 }
745 JSG_METHOD(getRandomValues);
746 JSG_METHOD(randomUUID);
747 
748 JSG_NESTED_TYPE(DigestStream);
749 
750 JSG_TS_OVERRIDE({
751 getRandomValues<
752 T extends
753 | Int8Array
754 | Uint8Array
755 | Int16Array
756 | Uint16Array
757 | Int32Array
758 | Uint32Array
759 | BigInt64Array
760 | BigUint64Array
761 >(buffer: T): T;
762 });
763 }
764 
765 void visitForGc(jsg::GcVisitor& visitor) {
766 visitor.visit(subtle);
767 }
768 
769 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
770 tracker.trackField("subtle", subtle);
771 }
772 
773 private:
774 jsg::Ref<SubtleCrypto> subtle;
775};
776 
777#define EW_CRYPTO_ISOLATE_TYPES \
778 api::Crypto, api::SubtleCrypto, api::CryptoKey, api::CryptoKeyPair, \
779 api::SubtleCrypto::JsonWebKey, api::SubtleCrypto::JsonWebKey::RsaOtherPrimesInfo, \
780 api::SubtleCrypto::DeriveKeyAlgorithm, api::SubtleCrypto::EncryptAlgorithm, \
781 api::SubtleCrypto::GenerateKeyAlgorithm, api::SubtleCrypto::HashAlgorithm, \
782 api::SubtleCrypto::ImportKeyAlgorithm, api::SubtleCrypto::SignAlgorithm, \
783 api::CryptoKey::KeyAlgorithm, api::CryptoKey::AesKeyAlgorithm, \
784 api::CryptoKey::HmacKeyAlgorithm, api::CryptoKey::RsaKeyAlgorithm, \
785 api::CryptoKey::EllipticKeyAlgorithm, api::CryptoKey::ArbitraryKeyAlgorithm, \
786 api::CryptoKey::AsymmetricKeyDetails, api::DigestStream
787 
788} // namespace workerd::api
789 
790KJ_DECLARE_NON_POLYMORPHIC(EVP_MD_CTX)
791KJ_DECLARE_NON_POLYMORPHIC(BIO);