1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "SubtleCrypto.h"
28
29#if ENABLE(WEB_CRYPTO)
30
31#include "CryptoAlgorithm.h"
32#include "CryptoAlgorithmRegistry.h"
33#include "JSAesCbcCfbParams.h"
34#include "JSAesCtrParams.h"
35#include "JSAesGcmParams.h"
36#include "JSAesKeyParams.h"
37#include "JSCryptoAlgorithmParameters.h"
38#include "JSCryptoKey.h"
39#include "JSCryptoKeyPair.h"
40#include "JSDOMPromiseDeferred.h"
41#include "JSDOMWrapper.h"
42#include "JSEcKeyParams.h"
43#include "JSEcdhKeyDeriveParams.h"
44#include "JSEcdsaParams.h"
45#include "JSHkdfParams.h"
46#include "JSHmacKeyParams.h"
47#include "JSJsonWebKey.h"
48#include "JSPbkdf2Params.h"
49#include "JSRsaHashedImportParams.h"
50#include "JSRsaHashedKeyGenParams.h"
51#include "JSRsaKeyGenParams.h"
52#include "JSRsaOaepParams.h"
53#include "JSRsaPssParams.h"
54#include <JavaScriptCore/JSONObject.h>
55
56namespace WebCore {
57using namespace JSC;
58
59SubtleCrypto::SubtleCrypto(ScriptExecutionContext* context)
60 : ContextDestructionObserver(context)
61 , m_workQueue(WorkQueue::create("com.apple.WebKit.CryptoQueue"))
62{
63}
64
65SubtleCrypto::~SubtleCrypto() = default;
66
67enum class Operations {
68 Encrypt,
69 Decrypt,
70 Sign,
71 Verify,
72 Digest,
73 GenerateKey,
74 DeriveBits,
75 ImportKey,
76 WrapKey,
77 UnwrapKey,
78 GetKeyLength
79};
80
81static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(ExecState&, WebCore::SubtleCrypto::AlgorithmIdentifier, Operations);
82
83static ExceptionOr<CryptoAlgorithmIdentifier> toHashIdentifier(ExecState& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier)
84{
85 auto digestParams = normalizeCryptoAlgorithmParameters(state, algorithmIdentifier, Operations::Digest);
86 if (digestParams.hasException())
87 return digestParams.releaseException();
88 return digestParams.returnValue()->identifier;
89}
90
91static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(ExecState& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier, Operations operation)
92{
93 VM& vm = state.vm();
94 auto scope = DECLARE_THROW_SCOPE(vm);
95
96 if (WTF::holds_alternative<String>(algorithmIdentifier)) {
97 auto newParams = Strong<JSObject>(vm, constructEmptyObject(&state));
98 newParams->putDirect(vm, Identifier::fromString(&vm, "name"), jsString(&state, WTF::get<String>(algorithmIdentifier)));
99
100 return normalizeCryptoAlgorithmParameters(state, newParams, operation);
101 }
102
103 auto& value = WTF::get<JSC::Strong<JSC::JSObject>>(algorithmIdentifier);
104
105 auto params = convertDictionary<CryptoAlgorithmParameters>(state, value.get());
106 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
107
108 auto identifier = CryptoAlgorithmRegistry::singleton().identifier(params.name);
109 if (UNLIKELY(!identifier))
110 return Exception { NotSupportedError };
111
112 std::unique_ptr<CryptoAlgorithmParameters> result;
113 switch (operation) {
114 case Operations::Encrypt:
115 case Operations::Decrypt:
116 switch (*identifier) {
117 case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
118 result = std::make_unique<CryptoAlgorithmParameters>(params);
119 break;
120 case CryptoAlgorithmIdentifier::RSA_OAEP: {
121 auto params = convertDictionary<CryptoAlgorithmRsaOaepParams>(state, value.get());
122 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
123 result = std::make_unique<CryptoAlgorithmRsaOaepParams>(params);
124 break;
125 }
126 case CryptoAlgorithmIdentifier::AES_CBC:
127 case CryptoAlgorithmIdentifier::AES_CFB: {
128 auto params = convertDictionary<CryptoAlgorithmAesCbcCfbParams>(state, value.get());
129 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
130 result = std::make_unique<CryptoAlgorithmAesCbcCfbParams>(params);
131 break;
132 }
133 case CryptoAlgorithmIdentifier::AES_CTR: {
134 auto params = convertDictionary<CryptoAlgorithmAesCtrParams>(state, value.get());
135 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
136 result = std::make_unique<CryptoAlgorithmAesCtrParams>(params);
137 break;
138 }
139 case CryptoAlgorithmIdentifier::AES_GCM: {
140 auto params = convertDictionary<CryptoAlgorithmAesGcmParams>(state, value.get());
141 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
142 result = std::make_unique<CryptoAlgorithmAesGcmParams>(params);
143 break;
144 }
145 default:
146 return Exception { NotSupportedError };
147 }
148 break;
149 case Operations::Sign:
150 case Operations::Verify:
151 switch (*identifier) {
152 case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
153 case CryptoAlgorithmIdentifier::HMAC:
154 result = std::make_unique<CryptoAlgorithmParameters>(params);
155 break;
156 case CryptoAlgorithmIdentifier::ECDSA: {
157 auto params = convertDictionary<CryptoAlgorithmEcdsaParams>(state, value.get());
158 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
159 auto hashIdentifier = toHashIdentifier(state, params.hash);
160 if (hashIdentifier.hasException())
161 return hashIdentifier.releaseException();
162 params.hashIdentifier = hashIdentifier.releaseReturnValue();
163 result = std::make_unique<CryptoAlgorithmEcdsaParams>(params);
164 break;
165 }
166 case CryptoAlgorithmIdentifier::RSA_PSS: {
167 auto params = convertDictionary<CryptoAlgorithmRsaPssParams>(state, value.get());
168 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
169 result = std::make_unique<CryptoAlgorithmRsaPssParams>(params);
170 break;
171 }
172 default:
173 return Exception { NotSupportedError };
174 }
175 break;
176 case Operations::Digest:
177 switch (*identifier) {
178 case CryptoAlgorithmIdentifier::SHA_1:
179 case CryptoAlgorithmIdentifier::SHA_224:
180 case CryptoAlgorithmIdentifier::SHA_256:
181 case CryptoAlgorithmIdentifier::SHA_384:
182 case CryptoAlgorithmIdentifier::SHA_512:
183 result = std::make_unique<CryptoAlgorithmParameters>(params);
184 break;
185 default:
186 return Exception { NotSupportedError };
187 }
188 break;
189 case Operations::GenerateKey:
190 switch (*identifier) {
191 case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: {
192 auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value.get());
193 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
194 result = std::make_unique<CryptoAlgorithmRsaKeyGenParams>(params);
195 break;
196 }
197 case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
198 case CryptoAlgorithmIdentifier::RSA_PSS:
199 case CryptoAlgorithmIdentifier::RSA_OAEP: {
200 auto params = convertDictionary<CryptoAlgorithmRsaHashedKeyGenParams>(state, value.get());
201 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
202 auto hashIdentifier = toHashIdentifier(state, params.hash);
203 if (hashIdentifier.hasException())
204 return hashIdentifier.releaseException();
205 params.hashIdentifier = hashIdentifier.releaseReturnValue();
206 result = std::make_unique<CryptoAlgorithmRsaHashedKeyGenParams>(params);
207 break;
208 }
209 case CryptoAlgorithmIdentifier::AES_CTR:
210 case CryptoAlgorithmIdentifier::AES_CBC:
211 case CryptoAlgorithmIdentifier::AES_GCM:
212 case CryptoAlgorithmIdentifier::AES_CFB:
213 case CryptoAlgorithmIdentifier::AES_KW: {
214 auto params = convertDictionary<CryptoAlgorithmAesKeyParams>(state, value.get());
215 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
216 result = std::make_unique<CryptoAlgorithmAesKeyParams>(params);
217 break;
218 }
219 case CryptoAlgorithmIdentifier::HMAC: {
220 auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get());
221 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
222 auto hashIdentifier = toHashIdentifier(state, params.hash);
223 if (hashIdentifier.hasException())
224 return hashIdentifier.releaseException();
225 params.hashIdentifier = hashIdentifier.releaseReturnValue();
226 result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params);
227 break;
228 }
229 case CryptoAlgorithmIdentifier::ECDSA:
230 case CryptoAlgorithmIdentifier::ECDH: {
231 auto params = convertDictionary<CryptoAlgorithmEcKeyParams>(state, value.get());
232 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
233 result = std::make_unique<CryptoAlgorithmEcKeyParams>(params);
234 break;
235 }
236 default:
237 return Exception { NotSupportedError };
238 }
239 break;
240 case Operations::DeriveBits:
241 switch (*identifier) {
242 case CryptoAlgorithmIdentifier::ECDH: {
243 // Remove this hack once https://bugs.webkit.org/show_bug.cgi?id=169333 is fixed.
244 JSValue nameValue = value.get()->get(&state, Identifier::fromString(&state, "name"));
245 JSValue publicValue = value.get()->get(&state, Identifier::fromString(&state, "public"));
246 JSObject* newValue = constructEmptyObject(&state);
247 newValue->putDirect(vm, Identifier::fromString(&vm, "name"), nameValue);
248 newValue->putDirect(vm, Identifier::fromString(&vm, "publicKey"), publicValue);
249
250 auto params = convertDictionary<CryptoAlgorithmEcdhKeyDeriveParams>(state, newValue);
251 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
252 result = std::make_unique<CryptoAlgorithmEcdhKeyDeriveParams>(params);
253 break;
254 }
255 case CryptoAlgorithmIdentifier::HKDF: {
256 auto params = convertDictionary<CryptoAlgorithmHkdfParams>(state, value.get());
257 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
258 auto hashIdentifier = toHashIdentifier(state, params.hash);
259 if (hashIdentifier.hasException())
260 return hashIdentifier.releaseException();
261 params.hashIdentifier = hashIdentifier.releaseReturnValue();
262 result = std::make_unique<CryptoAlgorithmHkdfParams>(params);
263 break;
264 }
265 case CryptoAlgorithmIdentifier::PBKDF2: {
266 auto params = convertDictionary<CryptoAlgorithmPbkdf2Params>(state, value.get());
267 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
268 auto hashIdentifier = toHashIdentifier(state, params.hash);
269 if (hashIdentifier.hasException())
270 return hashIdentifier.releaseException();
271 params.hashIdentifier = hashIdentifier.releaseReturnValue();
272 result = std::make_unique<CryptoAlgorithmPbkdf2Params>(params);
273 break;
274 }
275 default:
276 return Exception { NotSupportedError };
277 }
278 break;
279 case Operations::ImportKey:
280 switch (*identifier) {
281 case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
282 result = std::make_unique<CryptoAlgorithmParameters>(params);
283 break;
284 case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
285 case CryptoAlgorithmIdentifier::RSA_PSS:
286 case CryptoAlgorithmIdentifier::RSA_OAEP: {
287 auto params = convertDictionary<CryptoAlgorithmRsaHashedImportParams>(state, value.get());
288 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
289 auto hashIdentifier = toHashIdentifier(state, params.hash);
290 if (hashIdentifier.hasException())
291 return hashIdentifier.releaseException();
292 params.hashIdentifier = hashIdentifier.releaseReturnValue();
293 result = std::make_unique<CryptoAlgorithmRsaHashedImportParams>(params);
294 break;
295 }
296 case CryptoAlgorithmIdentifier::AES_CTR:
297 case CryptoAlgorithmIdentifier::AES_CBC:
298 case CryptoAlgorithmIdentifier::AES_GCM:
299 case CryptoAlgorithmIdentifier::AES_CFB:
300 case CryptoAlgorithmIdentifier::AES_KW:
301 result = std::make_unique<CryptoAlgorithmParameters>(params);
302 break;
303 case CryptoAlgorithmIdentifier::HMAC: {
304 auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get());
305 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
306 auto hashIdentifier = toHashIdentifier(state, params.hash);
307 if (hashIdentifier.hasException())
308 return hashIdentifier.releaseException();
309 params.hashIdentifier = hashIdentifier.releaseReturnValue();
310 result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params);
311 break;
312 }
313 case CryptoAlgorithmIdentifier::ECDSA:
314 case CryptoAlgorithmIdentifier::ECDH: {
315 auto params = convertDictionary<CryptoAlgorithmEcKeyParams>(state, value.get());
316 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
317 result = std::make_unique<CryptoAlgorithmEcKeyParams>(params);
318 break;
319 }
320 case CryptoAlgorithmIdentifier::HKDF:
321 case CryptoAlgorithmIdentifier::PBKDF2:
322 result = std::make_unique<CryptoAlgorithmParameters>(params);
323 break;
324 default:
325 return Exception { NotSupportedError };
326 }
327 break;
328 case Operations::WrapKey:
329 case Operations::UnwrapKey:
330 switch (*identifier) {
331 case CryptoAlgorithmIdentifier::AES_KW:
332 result = std::make_unique<CryptoAlgorithmParameters>(params);
333 break;
334 default:
335 return Exception { NotSupportedError };
336 }
337 break;
338 case Operations::GetKeyLength:
339 switch (*identifier) {
340 case CryptoAlgorithmIdentifier::AES_CTR:
341 case CryptoAlgorithmIdentifier::AES_CBC:
342 case CryptoAlgorithmIdentifier::AES_GCM:
343 case CryptoAlgorithmIdentifier::AES_CFB:
344 case CryptoAlgorithmIdentifier::AES_KW: {
345 auto params = convertDictionary<CryptoAlgorithmAesKeyParams>(state, value.get());
346 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
347 result = std::make_unique<CryptoAlgorithmAesKeyParams>(params);
348 break;
349 }
350 case CryptoAlgorithmIdentifier::HMAC: {
351 auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get());
352 RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
353 auto hashIdentifier = toHashIdentifier(state, params.hash);
354 if (hashIdentifier.hasException())
355 return hashIdentifier.releaseException();
356 params.hashIdentifier = hashIdentifier.releaseReturnValue();
357 result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params);
358 break;
359 }
360 case CryptoAlgorithmIdentifier::HKDF:
361 case CryptoAlgorithmIdentifier::PBKDF2:
362 result = std::make_unique<CryptoAlgorithmParameters>(params);
363 break;
364 default:
365 return Exception { NotSupportedError };
366 }
367 break;
368 }
369
370 result->identifier = *identifier;
371 return result;
372}
373
374static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(CryptoKeyUsage usage)
375{
376 switch (usage) {
377 case CryptoKeyUsage::Encrypt:
378 return CryptoKeyUsageEncrypt;
379 case CryptoKeyUsage::Decrypt:
380 return CryptoKeyUsageDecrypt;
381 case CryptoKeyUsage::Sign:
382 return CryptoKeyUsageSign;
383 case CryptoKeyUsage::Verify:
384 return CryptoKeyUsageVerify;
385 case CryptoKeyUsage::DeriveKey:
386 return CryptoKeyUsageDeriveKey;
387 case CryptoKeyUsage::DeriveBits:
388 return CryptoKeyUsageDeriveBits;
389 case CryptoKeyUsage::WrapKey:
390 return CryptoKeyUsageWrapKey;
391 case CryptoKeyUsage::UnwrapKey:
392 return CryptoKeyUsageUnwrapKey;
393 }
394
395 RELEASE_ASSERT_NOT_REACHED();
396}
397
398static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(const Vector<CryptoKeyUsage>& usages)
399{
400 CryptoKeyUsageBitmap result = 0;
401 // Maybe we shouldn't silently bypass duplicated usages?
402 for (auto usage : usages)
403 result |= toCryptoKeyUsageBitmap(usage);
404
405 return result;
406}
407
408// Maybe we want more specific error messages?
409static void rejectWithException(Ref<DeferredPromise>&& passedPromise, ExceptionCode ec)
410{
411 switch (ec) {
412 case NotSupportedError:
413 passedPromise->reject(ec, "The algorithm is not supported"_s);
414 return;
415 case SyntaxError:
416 passedPromise->reject(ec, "A required parameter was missing or out-of-range"_s);
417 return;
418 case InvalidStateError:
419 passedPromise->reject(ec, "The requested operation is not valid for the current state of the provided key"_s);
420 return;
421 case InvalidAccessError:
422 passedPromise->reject(ec, "The requested operation is not valid for the provided key"_s);
423 return;
424 case UnknownError:
425 passedPromise->reject(ec, "The operation failed for an unknown transient reason (e.g. out of memory)"_s);
426 return;
427 case DataError:
428 passedPromise->reject(ec, "Data provided to an operation does not meet requirements"_s);
429 return;
430 case OperationError:
431 passedPromise->reject(ec, "The operation failed for an operation-specific reason"_s);
432 return;
433 default:
434 break;
435 }
436 ASSERT_NOT_REACHED();
437}
438
439static void normalizeJsonWebKey(JsonWebKey& webKey)
440{
441 // Maybe we shouldn't silently bypass duplicated usages?
442 webKey.usages = webKey.key_ops ? toCryptoKeyUsageBitmap(webKey.key_ops.value()) : 0;
443}
444
445// FIXME: This returns an Optional<KeyData> and takes a promise, rather than returning an
446// ExceptionOr<KeyData> and letting the caller handle the promise, to work around an issue where
447// Variant types (which KeyData is) in ExceptionOr<> cause compile issues on some platforms. This
448// should be resolved by adopting a standards compliant std::variant (see https://webkit.org/b/175583)
449static Optional<KeyData> toKeyData(SubtleCrypto::KeyFormat format, SubtleCrypto::KeyDataVariant&& keyDataVariant, Ref<DeferredPromise>& promise)
450{
451 switch (format) {
452 case SubtleCrypto::KeyFormat::Spki:
453 case SubtleCrypto::KeyFormat::Pkcs8:
454 case SubtleCrypto::KeyFormat::Raw:
455 return WTF::switchOn(keyDataVariant,
456 [&promise] (JsonWebKey&) -> Optional<KeyData> {
457 promise->reject(Exception { TypeError });
458 return WTF::nullopt;
459 },
460 [] (auto& bufferSource) -> Optional<KeyData> {
461 Vector<uint8_t> result;
462 result.append(static_cast<const uint8_t*>(bufferSource->data()), bufferSource->byteLength());
463 return KeyData { result };
464 }
465 );
466 case SubtleCrypto::KeyFormat::Jwk:
467 return WTF::switchOn(keyDataVariant,
468 [] (JsonWebKey& webKey) -> Optional<KeyData> {
469 normalizeJsonWebKey(webKey);
470 return KeyData { webKey };
471 },
472 [&promise] (auto&) -> Optional<KeyData> {
473 promise->reject(Exception { TypeError });
474 return WTF::nullopt;
475 }
476 );
477 }
478
479 RELEASE_ASSERT_NOT_REACHED();
480}
481
482static Vector<uint8_t> copyToVector(BufferSource&& data)
483{
484 Vector<uint8_t> dataVector;
485 dataVector.append(data.data(), data.length());
486 return dataVector;
487}
488
489static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier)
490{
491 switch (identifier) {
492 case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
493 case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
494 case CryptoAlgorithmIdentifier::RSA_PSS:
495 case CryptoAlgorithmIdentifier::RSA_OAEP:
496 case CryptoAlgorithmIdentifier::AES_CTR:
497 case CryptoAlgorithmIdentifier::AES_CBC:
498 case CryptoAlgorithmIdentifier::AES_GCM:
499 case CryptoAlgorithmIdentifier::AES_CFB:
500 case CryptoAlgorithmIdentifier::AES_KW:
501 case CryptoAlgorithmIdentifier::HMAC:
502 case CryptoAlgorithmIdentifier::ECDSA:
503 case CryptoAlgorithmIdentifier::ECDH:
504 return true;
505 default:
506 return false;
507 }
508}
509
510RefPtr<DeferredPromise> getPromise(DeferredPromise* index, WeakPtr<SubtleCrypto> subtleCryptoWeakPointer)
511{
512 if (subtleCryptoWeakPointer) {
513 if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.take(index))
514 return WTFMove(promise.value());
515 }
516 return nullptr;
517}
518
519static std::unique_ptr<CryptoAlgorithmParameters> crossThreadCopyImportParams(const CryptoAlgorithmParameters& importParams)
520{
521 switch (importParams.parametersClass()) {
522 case CryptoAlgorithmParameters::Class::None: {
523 auto result = std::make_unique<CryptoAlgorithmParameters>();
524 result->identifier = importParams.identifier;
525 return result;
526 }
527 case CryptoAlgorithmParameters::Class::EcKeyParams:
528 return std::make_unique<CryptoAlgorithmEcKeyParams>(crossThreadCopy(downcast<CryptoAlgorithmEcKeyParams>(importParams)));
529 case CryptoAlgorithmParameters::Class::HmacKeyParams:
530 return std::make_unique<CryptoAlgorithmHmacKeyParams>(crossThreadCopy(downcast<CryptoAlgorithmHmacKeyParams>(importParams)));
531 case CryptoAlgorithmParameters::Class::RsaHashedImportParams:
532 return std::make_unique<CryptoAlgorithmRsaHashedImportParams>(crossThreadCopy(downcast<CryptoAlgorithmRsaHashedImportParams>(importParams)));
533 default:
534 ASSERT_NOT_REACHED();
535 return nullptr;
536 }
537}
538
539// MARK: - Exposed functions.
540
541void SubtleCrypto::encrypt(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise)
542{
543 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Encrypt);
544 if (paramsOrException.hasException()) {
545 promise->reject(paramsOrException.releaseException());
546 return;
547 }
548 auto params = paramsOrException.releaseReturnValue();
549
550 auto data = copyToVector(WTFMove(dataBufferSource));
551
552 if (params->identifier != key.algorithmIdentifier()) {
553 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
554 return;
555 }
556
557 if (!key.allows(CryptoKeyUsageEncrypt)) {
558 promise->reject(InvalidAccessError, "CryptoKey doesn't support encryption"_s);
559 return;
560 }
561
562 auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
563
564 auto index = promise.ptr();
565 m_pendingPromises.add(index, WTFMove(promise));
566 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
567 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& cipherText) mutable {
568 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
569 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), cipherText.data(), cipherText.size());
570 };
571 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
572 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
573 rejectWithException(promise.releaseNonNull(), ec);
574 };
575
576 algorithm->encrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
577}
578
579void SubtleCrypto::decrypt(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise)
580{
581 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Decrypt);
582 if (paramsOrException.hasException()) {
583 promise->reject(paramsOrException.releaseException());
584 return;
585 }
586 auto params = paramsOrException.releaseReturnValue();
587
588 auto data = copyToVector(WTFMove(dataBufferSource));
589
590 if (params->identifier != key.algorithmIdentifier()) {
591 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
592 return;
593 }
594
595 if (!key.allows(CryptoKeyUsageDecrypt)) {
596 promise->reject(InvalidAccessError, "CryptoKey doesn't support decryption"_s);
597 return;
598 }
599
600 auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
601
602 auto index = promise.ptr();
603 m_pendingPromises.add(index, WTFMove(promise));
604 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
605 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& plainText) mutable {
606 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
607 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), plainText.data(), plainText.size());
608 };
609 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
610 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
611 rejectWithException(promise.releaseNonNull(), ec);
612 };
613
614 algorithm->decrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
615}
616
617void SubtleCrypto::sign(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise)
618{
619 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Sign);
620 if (paramsOrException.hasException()) {
621 promise->reject(paramsOrException.releaseException());
622 return;
623 }
624 auto params = paramsOrException.releaseReturnValue();
625
626 auto data = copyToVector(WTFMove(dataBufferSource));
627
628 if (params->identifier != key.algorithmIdentifier()) {
629 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
630 return;
631 }
632
633 if (!key.allows(CryptoKeyUsageSign)) {
634 promise->reject(InvalidAccessError, "CryptoKey doesn't support signing"_s);
635 return;
636 }
637
638 auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
639
640 auto index = promise.ptr();
641 m_pendingPromises.add(index, WTFMove(promise));
642 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
643 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& signature) mutable {
644 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
645 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), signature.data(), signature.size());
646 };
647 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
648 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
649 rejectWithException(promise.releaseNonNull(), ec);
650 };
651
652 algorithm->sign(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
653}
654
655void SubtleCrypto::verify(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& signatureBufferSource, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise)
656{
657 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Verify);
658 if (paramsOrException.hasException()) {
659 promise->reject(paramsOrException.releaseException());
660 return;
661 }
662 auto params = paramsOrException.releaseReturnValue();
663
664 auto signature = copyToVector(WTFMove(signatureBufferSource));
665 auto data = copyToVector(WTFMove(dataBufferSource));
666
667 if (params->identifier != key.algorithmIdentifier()) {
668 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
669 return;
670 }
671
672 if (!key.allows(CryptoKeyUsageVerify)) {
673 promise->reject(InvalidAccessError, "CryptoKey doesn't support verification"_s);
674 return;
675 }
676
677 auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
678
679 auto index = promise.ptr();
680 m_pendingPromises.add(index, WTFMove(promise));
681 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
682 auto callback = [index, subtleCryptoWeakPointer](bool result) mutable {
683 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
684 promise->resolve<IDLBoolean>(result);
685 };
686 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
687 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
688 rejectWithException(promise.releaseNonNull(), ec);
689 };
690
691 algorithm->verify(*params, key, WTFMove(signature), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
692}
693
694void SubtleCrypto::digest(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise)
695{
696 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Digest);
697 if (paramsOrException.hasException()) {
698 promise->reject(paramsOrException.releaseException());
699 return;
700 }
701 auto params = paramsOrException.releaseReturnValue();
702
703 auto data = copyToVector(WTFMove(dataBufferSource));
704
705 auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier);
706
707 auto index = promise.ptr();
708 m_pendingPromises.add(index, WTFMove(promise));
709 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
710 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& digest) mutable {
711 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
712 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), digest.data(), digest.size());
713 };
714 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
715 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
716 rejectWithException(promise.releaseNonNull(), ec);
717 };
718
719 algorithm->digest(WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
720}
721
722void SubtleCrypto::generateKey(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, bool extractable, Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise)
723{
724 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::GenerateKey);
725 if (paramsOrException.hasException()) {
726 promise->reject(paramsOrException.releaseException());
727 return;
728 }
729 auto params = paramsOrException.releaseReturnValue();
730
731 auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages);
732
733 auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier);
734
735 auto index = promise.ptr();
736 m_pendingPromises.add(index, WTFMove(promise));
737 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
738 auto callback = [index, subtleCryptoWeakPointer](KeyOrKeyPair&& keyOrKeyPair) mutable {
739 if (auto promise = getPromise(index, subtleCryptoWeakPointer)) {
740 WTF::switchOn(keyOrKeyPair,
741 [&promise] (RefPtr<CryptoKey>& key) {
742 if ((key->type() == CryptoKeyType::Private || key->type() == CryptoKeyType::Secret) && !key->usagesBitmap()) {
743 rejectWithException(promise.releaseNonNull(), SyntaxError);
744 return;
745 }
746 promise->resolve<IDLInterface<CryptoKey>>(*key);
747 },
748 [&promise] (CryptoKeyPair& keyPair) {
749 if (!keyPair.privateKey->usagesBitmap()) {
750 rejectWithException(promise.releaseNonNull(), SyntaxError);
751 return;
752 }
753 promise->resolve<IDLDictionary<CryptoKeyPair>>(keyPair);
754 }
755 );
756 }
757 };
758 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
759 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
760 rejectWithException(promise.releaseNonNull(), ec);
761 };
762
763 // The 26 January 2017 version of the specification suggests we should perform the following task asynchronously
764 // regardless what kind of keys it produces: https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey
765 // That's simply not efficient for AES, HMAC and EC keys. Therefore, we perform it as an async task only for RSA keys.
766 algorithm->generateKey(*params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext());
767}
768
769void SubtleCrypto::deriveKey(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, AlgorithmIdentifier&& derivedKeyType, bool extractable, Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise)
770{
771 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits);
772 if (paramsOrException.hasException()) {
773 promise->reject(paramsOrException.releaseException());
774 return;
775 }
776 auto params = paramsOrException.releaseReturnValue();
777
778 auto importParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::ImportKey);
779 if (importParamsOrException.hasException()) {
780 promise->reject(importParamsOrException.releaseException());
781 return;
782 }
783 auto importParams = importParamsOrException.releaseReturnValue();
784
785 auto getLengthParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::GetKeyLength);
786 if (getLengthParamsOrException.hasException()) {
787 promise->reject(getLengthParamsOrException.releaseException());
788 return;
789 }
790 auto getLengthParams = getLengthParamsOrException.releaseReturnValue();
791
792 auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages);
793
794 if (params->identifier != baseKey.algorithmIdentifier()) {
795 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
796 return;
797 }
798
799 if (!baseKey.allows(CryptoKeyUsageDeriveKey)) {
800 promise->reject(InvalidAccessError, "CryptoKey doesn't support CryptoKey derivation"_s);
801 return;
802 }
803
804 auto getLengthAlgorithm = CryptoAlgorithmRegistry::singleton().create(getLengthParams->identifier);
805
806 auto result = getLengthAlgorithm->getKeyLength(*getLengthParams);
807 if (result.hasException()) {
808 promise->reject(result.releaseException().code(), "Cannot get key length from derivedKeyType"_s);
809 return;
810 }
811 size_t length = result.releaseReturnValue();
812
813 auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(importParams->identifier);
814 auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier);
815
816 auto index = promise.ptr();
817 m_pendingPromises.add(index, WTFMove(promise));
818 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
819 auto callback = [index, subtleCryptoWeakPointer, importAlgorithm = WTFMove(importAlgorithm), importParams = crossThreadCopyImportParams(*importParams), extractable, keyUsagesBitmap](const Vector<uint8_t>& derivedKey) mutable {
820 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=169395
821 KeyData data = derivedKey;
822 auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable {
823 if (auto promise = getPromise(index, subtleCryptoWeakPointer)) {
824 if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) {
825 rejectWithException(promise.releaseNonNull(), SyntaxError);
826 return;
827 }
828 promise->resolve<IDLInterface<CryptoKey>>(key);
829 }
830 };
831 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
832 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
833 rejectWithException(promise.releaseNonNull(), ec);
834 };
835
836 importAlgorithm->importKey(SubtleCrypto::KeyFormat::Raw, WTFMove(data), *importParams, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback));
837 };
838 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
839 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
840 rejectWithException(promise.releaseNonNull(), ec);
841 };
842
843 algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
844}
845
846void SubtleCrypto::deriveBits(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, unsigned length, Ref<DeferredPromise>&& promise)
847{
848 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits);
849 if (paramsOrException.hasException()) {
850 promise->reject(paramsOrException.releaseException());
851 return;
852 }
853 auto params = paramsOrException.releaseReturnValue();
854
855 if (params->identifier != baseKey.algorithmIdentifier()) {
856 promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s);
857 return;
858 }
859
860 if (!baseKey.allows(CryptoKeyUsageDeriveBits)) {
861 promise->reject(InvalidAccessError, "CryptoKey doesn't support bits derivation"_s);
862 return;
863 }
864
865 auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier);
866
867 auto index = promise.ptr();
868 m_pendingPromises.add(index, WTFMove(promise));
869 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
870 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& derivedKey) mutable {
871 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
872 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), derivedKey.data(), derivedKey.size());
873 };
874 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
875 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
876 rejectWithException(promise.releaseNonNull(), ec);
877 };
878
879 algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
880}
881
882void SubtleCrypto::importKey(JSC::ExecState& state, KeyFormat format, KeyDataVariant&& keyDataVariant, AlgorithmIdentifier&& algorithmIdentifier, bool extractable, Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise)
883{
884 auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::ImportKey);
885 if (paramsOrException.hasException()) {
886 promise->reject(paramsOrException.releaseException());
887 return;
888 }
889 auto params = paramsOrException.releaseReturnValue();
890
891 auto keyDataOrNull = toKeyData(format, WTFMove(keyDataVariant), promise);
892 if (!keyDataOrNull) {
893 // When toKeyData, it means the promise has been rejected, and we should return.
894 return;
895 }
896
897 auto keyData = *keyDataOrNull;
898 auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages);
899
900 auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier);
901
902 auto index = promise.ptr();
903 m_pendingPromises.add(index, WTFMove(promise));
904 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
905 auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable {
906 if (auto promise = getPromise(index, subtleCryptoWeakPointer)) {
907 if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) {
908 rejectWithException(promise.releaseNonNull(), SyntaxError);
909 return;
910 }
911 promise->resolve<IDLInterface<CryptoKey>>(key);
912 }
913 };
914 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
915 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
916 rejectWithException(promise.releaseNonNull(), ec);
917 };
918
919 // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
920 // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey
921 // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
922 algorithm->importKey(format, WTFMove(keyData), *params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback));
923}
924
925void SubtleCrypto::exportKey(KeyFormat format, CryptoKey& key, Ref<DeferredPromise>&& promise)
926{
927 if (!isSupportedExportKey(key.algorithmIdentifier())) {
928 promise->reject(Exception { NotSupportedError });
929 return;
930 }
931
932 if (!key.extractable()) {
933 promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s);
934 return;
935 }
936
937 auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
938
939 auto index = promise.ptr();
940 m_pendingPromises.add(index, WTFMove(promise));
941 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
942 auto callback = [index, subtleCryptoWeakPointer](SubtleCrypto::KeyFormat format, KeyData&& key) mutable {
943 if (auto promise = getPromise(index, subtleCryptoWeakPointer)) {
944 switch (format) {
945 case SubtleCrypto::KeyFormat::Spki:
946 case SubtleCrypto::KeyFormat::Pkcs8:
947 case SubtleCrypto::KeyFormat::Raw: {
948 Vector<uint8_t>& rawKey = WTF::get<Vector<uint8_t>>(key);
949 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), rawKey.data(), rawKey.size());
950 return;
951 }
952 case SubtleCrypto::KeyFormat::Jwk:
953 promise->resolve<IDLDictionary<JsonWebKey>>(WTFMove(WTF::get<JsonWebKey>(key)));
954 return;
955 }
956 ASSERT_NOT_REACHED();
957 }
958 };
959 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
960 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
961 rejectWithException(promise.releaseNonNull(), ec);
962 };
963
964 // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
965 // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey
966 // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
967 algorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback));
968}
969
970void SubtleCrypto::wrapKey(JSC::ExecState& state, KeyFormat format, CryptoKey& key, CryptoKey& wrappingKey, AlgorithmIdentifier&& wrapAlgorithmIdentifier, Ref<DeferredPromise>&& promise)
971{
972 bool isEncryption = false;
973
974 auto wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::WrapKey);
975 if (wrapParamsOrException.hasException()) {
976 ASSERT(wrapParamsOrException.exception().code() != ExistingExceptionError);
977
978 wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::Encrypt);
979 if (wrapParamsOrException.hasException()) {
980 promise->reject(wrapParamsOrException.releaseException());
981 return;
982 }
983
984 isEncryption = true;
985 }
986 auto wrapParams = wrapParamsOrException.releaseReturnValue();
987
988 if (wrapParams->identifier != wrappingKey.algorithmIdentifier()) {
989 promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't match AlgorithmIdentifier"_s);
990 return;
991 }
992
993 if (!wrappingKey.allows(CryptoKeyUsageWrapKey)) {
994 promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't support wrapKey operation"_s);
995 return;
996 }
997
998 if (!isSupportedExportKey(key.algorithmIdentifier())) {
999 promise->reject(Exception { NotSupportedError });
1000 return;
1001 }
1002
1003 if (!key.extractable()) {
1004 promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s);
1005 return;
1006 }
1007
1008 auto exportAlgorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier());
1009 auto wrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(wrappingKey.algorithmIdentifier());
1010
1011 auto context = scriptExecutionContext();
1012
1013 auto index = promise.ptr();
1014 m_pendingPromises.add(index, WTFMove(promise));
1015 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
1016 auto callback = [index, subtleCryptoWeakPointer, wrapAlgorithm, wrappingKey = makeRef(wrappingKey), wrapParams = WTFMove(wrapParams), isEncryption, context, workQueue = m_workQueue.copyRef()](SubtleCrypto::KeyFormat format, KeyData&& key) mutable {
1017 if (subtleCryptoWeakPointer) {
1018 if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) {
1019 Vector<uint8_t> bytes;
1020 switch (format) {
1021 case SubtleCrypto::KeyFormat::Spki:
1022 case SubtleCrypto::KeyFormat::Pkcs8:
1023 case SubtleCrypto::KeyFormat::Raw:
1024 bytes = WTF::get<Vector<uint8_t>>(key);
1025 break;
1026 case SubtleCrypto::KeyFormat::Jwk: {
1027 // FIXME: Converting to JS just to JSON-Stringify seems inefficient. We should find a way to go directly from the struct to JSON.
1028 auto jwk = toJS<IDLDictionary<JsonWebKey>>(*(promise->globalObject()->globalExec()), *(promise->globalObject()), WTFMove(WTF::get<JsonWebKey>(key)));
1029 String jwkString = JSONStringify(promise->globalObject()->globalExec(), jwk, 0);
1030 CString jwkUtf8String = jwkString.utf8(StrictConversion);
1031 bytes.append(jwkUtf8String.data(), jwkUtf8String.length());
1032 }
1033 }
1034
1035 auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& wrappedKey) mutable {
1036 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
1037 fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), wrappedKey.data(), wrappedKey.size());
1038 };
1039 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
1040 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
1041 rejectWithException(promise.releaseNonNull(), ec);
1042 };
1043
1044 if (!isEncryption) {
1045 // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
1046 // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey
1047 // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
1048 wrapAlgorithm->wrapKey(wrappingKey.get(), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback));
1049 return;
1050 }
1051 // The following operation should be performed asynchronously.
1052 wrapAlgorithm->encrypt(*wrapParams, WTFMove(wrappingKey), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback), *context, workQueue);
1053 }
1054 }
1055 };
1056 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
1057 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
1058 rejectWithException(promise.releaseNonNull(), ec);
1059 };
1060
1061 // The following operation should be performed synchronously.
1062 exportAlgorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback));
1063}
1064
1065void SubtleCrypto::unwrapKey(JSC::ExecState& state, KeyFormat format, BufferSource&& wrappedKeyBufferSource, CryptoKey& unwrappingKey, AlgorithmIdentifier&& unwrapAlgorithmIdentifier, AlgorithmIdentifier&& unwrappedKeyAlgorithmIdentifier, bool extractable, Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise)
1066{
1067 auto wrappedKey = copyToVector(WTFMove(wrappedKeyBufferSource));
1068
1069 bool isDecryption = false;
1070
1071 auto unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::UnwrapKey);
1072 if (unwrapParamsOrException.hasException()) {
1073 ASSERT(unwrapParamsOrException.exception().code() != ExistingExceptionError);
1074
1075 unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::Decrypt);
1076 if (unwrapParamsOrException.hasException()) {
1077 promise->reject(unwrapParamsOrException.releaseException());
1078 return;
1079 }
1080
1081 isDecryption = true;
1082 }
1083 auto unwrapParams = unwrapParamsOrException.releaseReturnValue();
1084
1085 auto unwrappedKeyAlgorithmOrException = normalizeCryptoAlgorithmParameters(state, unwrappedKeyAlgorithmIdentifier, Operations::ImportKey);
1086 if (unwrappedKeyAlgorithmOrException.hasException()) {
1087 promise->reject(unwrappedKeyAlgorithmOrException.releaseException());
1088 return;
1089 }
1090 auto unwrappedKeyAlgorithm = unwrappedKeyAlgorithmOrException.releaseReturnValue();
1091
1092 auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages);
1093
1094 if (unwrapParams->identifier != unwrappingKey.algorithmIdentifier()) {
1095 promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't match unwrap AlgorithmIdentifier"_s);
1096 return;
1097 }
1098
1099 if (!unwrappingKey.allows(CryptoKeyUsageUnwrapKey)) {
1100 promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't support unwrapKey operation"_s);
1101 return;
1102 }
1103
1104 auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappedKeyAlgorithm->identifier);
1105 if (UNLIKELY(!importAlgorithm)) {
1106 promise->reject(Exception { NotSupportedError });
1107 return;
1108 }
1109
1110 auto unwrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappingKey.algorithmIdentifier());
1111 if (UNLIKELY(!unwrapAlgorithm)) {
1112 promise->reject(Exception { NotSupportedError });
1113 return;
1114 }
1115
1116 auto index = promise.ptr();
1117 m_pendingPromises.add(index, WTFMove(promise));
1118 auto subtleCryptoWeakPointer = makeWeakPtr(*this);
1119 auto callback = [index, subtleCryptoWeakPointer, format, importAlgorithm, unwrappedKeyAlgorithm = crossThreadCopyImportParams(*unwrappedKeyAlgorithm), extractable, keyUsagesBitmap](const Vector<uint8_t>& bytes) mutable {
1120 if (subtleCryptoWeakPointer) {
1121 if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) {
1122 KeyData keyData;
1123 switch (format) {
1124 case SubtleCrypto::KeyFormat::Spki:
1125 case SubtleCrypto::KeyFormat::Pkcs8:
1126 case SubtleCrypto::KeyFormat::Raw:
1127 keyData = bytes;
1128 break;
1129 case SubtleCrypto::KeyFormat::Jwk: {
1130 auto& state = *(promise->globalObject()->globalExec());
1131 auto& vm = state.vm();
1132 auto scope = DECLARE_THROW_SCOPE(vm);
1133
1134 String jwkString(reinterpret_cast_ptr<const char*>(bytes.data()), bytes.size());
1135 JSLockHolder locker(vm);
1136 auto jwkObject = JSONParse(&state, jwkString);
1137 if (!jwkObject) {
1138 promise->reject(DataError, "WrappedKey cannot be converted to a JSON object"_s);
1139 return;
1140 }
1141 auto jwk = convert<IDLDictionary<JsonWebKey>>(state, jwkObject);
1142 RETURN_IF_EXCEPTION(scope, void());
1143 normalizeJsonWebKey(jwk);
1144
1145 keyData = jwk;
1146 break;
1147 }
1148 }
1149
1150 auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable {
1151 if (auto promise = getPromise(index, subtleCryptoWeakPointer)) {
1152 if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) {
1153 rejectWithException(promise.releaseNonNull(), SyntaxError);
1154 return;
1155 }
1156 promise->resolve<IDLInterface<CryptoKey>>(key);
1157 }
1158 };
1159 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
1160 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
1161 rejectWithException(promise.releaseNonNull(), ec);
1162 };
1163
1164 // The following operation should be performed synchronously.
1165 importAlgorithm->importKey(format, WTFMove(keyData), *unwrappedKeyAlgorithm, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback));
1166 }
1167 }
1168 };
1169 auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable {
1170 if (auto promise = getPromise(index, subtleCryptoWeakPointer))
1171 rejectWithException(promise.releaseNonNull(), ec);
1172 };
1173
1174 if (!isDecryption) {
1175 // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
1176 // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey
1177 // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
1178 unwrapAlgorithm->unwrapKey(unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback));
1179 return;
1180 }
1181
1182 unwrapAlgorithm->decrypt(*unwrapParams, unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue);
1183}
1184
1185}
1186
1187#endif
1188