Update SubtleCrypto::encrypt to match the latest spec
[WebKit-https.git] / Source / WebCore / bindings / js / JSSubtleCryptoCustom.cpp
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 "JSSubtleCrypto.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmRegistry.h"
33 #include "JSAesCbcParams.h"
34 #include "JSAesKeyGenParams.h"
35 #include "JSCryptoAlgorithmParameters.h"
36 #include "JSCryptoKey.h"
37 #include "JSCryptoKeyPair.h"
38 #include "JSDOMPromise.h"
39 #include "JSHmacKeyParams.h"
40 #include "JSJsonWebKey.h"
41 #include "JSRsaHashedImportParams.h"
42 #include "JSRsaHashedKeyGenParams.h"
43 #include "JSRsaKeyGenParams.h"
44 #include "JSRsaOaepParams.h"
45 #include "ScriptState.h"
46 #include <runtime/Error.h>
47 #include <runtime/IteratorOperations.h>
48 #include <runtime/JSArray.h>
49
50 using namespace JSC;
51
52 namespace WebCore {
53
54 enum class Operations {
55     Encrypt,
56     Digest,
57     GenerateKey,
58     ImportKey,
59 };
60
61 static std::unique_ptr<CryptoAlgorithmParameters> normalizeCryptoAlgorithmParameters(ExecState&, JSValue, Operations);
62
63 static CryptoAlgorithmIdentifier toHashIdentifier(ExecState& state, JSValue value)
64 {
65     VM& vm = state.vm();
66     auto scope = DECLARE_THROW_SCOPE(vm);
67
68     auto digestParams = normalizeCryptoAlgorithmParameters(state, value, Operations::Digest);
69     RETURN_IF_EXCEPTION(scope, { });
70     return digestParams->identifier;
71 }
72
73 static std::unique_ptr<CryptoAlgorithmParameters> normalizeCryptoAlgorithmParameters(ExecState& state, JSValue value, Operations operation)
74 {
75     VM& vm = state.vm();
76     auto scope = DECLARE_THROW_SCOPE(vm);
77
78     if (value.isString()) {
79         JSObject* newParams = constructEmptyObject(&state);
80         newParams->putDirect(vm, Identifier::fromString(&vm, "name"), value);
81         return normalizeCryptoAlgorithmParameters(state, newParams, operation);
82     }
83
84     if (value.isObject()) {
85         auto params = convertDictionary<CryptoAlgorithmParameters>(state, value);
86         RETURN_IF_EXCEPTION(scope, nullptr);
87
88         auto identifier = CryptoAlgorithmRegistry::singleton().identifier(params.name);
89         if (!identifier) {
90             setDOMException(&state, NOT_SUPPORTED_ERR);
91             return nullptr;
92         }
93
94         std::unique_ptr<CryptoAlgorithmParameters> result;
95         switch (operation) {
96         case Operations::Encrypt:
97             switch (*identifier) {
98             case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
99                 result = std::make_unique<CryptoAlgorithmParameters>(params);
100                 break;
101             case CryptoAlgorithmIdentifier::RSA_OAEP: {
102                 auto params = convertDictionary<CryptoAlgorithmRsaOaepParams>(state, value);
103                 RETURN_IF_EXCEPTION(scope, nullptr);
104                 result = std::make_unique<CryptoAlgorithmRsaOaepParams>(params);
105                 break;
106             }
107             case CryptoAlgorithmIdentifier::AES_CBC: {
108                 auto params = convertDictionary<CryptoAlgorithmAesCbcParams>(state, value);
109                 RETURN_IF_EXCEPTION(scope, nullptr);
110                 result = std::make_unique<CryptoAlgorithmAesCbcParams>(params);
111                 break;
112             }
113             default:
114                 setDOMException(&state, NOT_SUPPORTED_ERR);
115                 return nullptr;
116             }
117             break;
118         case Operations::Digest:
119             switch (*identifier) {
120             case CryptoAlgorithmIdentifier::SHA_1:
121             case CryptoAlgorithmIdentifier::SHA_224:
122             case CryptoAlgorithmIdentifier::SHA_256:
123             case CryptoAlgorithmIdentifier::SHA_384:
124             case CryptoAlgorithmIdentifier::SHA_512:
125                 result = std::make_unique<CryptoAlgorithmParameters>(params);
126                 break;
127             default:
128                 setDOMException(&state, NOT_SUPPORTED_ERR);
129                 return nullptr;
130             }
131             break;
132         case Operations::GenerateKey:
133             switch (*identifier) {
134             case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: {
135                 auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value);
136                 RETURN_IF_EXCEPTION(scope, nullptr);
137                 result = std::make_unique<CryptoAlgorithmRsaKeyGenParams>(params);
138                 break;
139             }
140             case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
141             case CryptoAlgorithmIdentifier::RSA_PSS:
142             case CryptoAlgorithmIdentifier::RSA_OAEP: {
143                 auto params = convertDictionary<CryptoAlgorithmRsaHashedKeyGenParams>(state, value);
144                 RETURN_IF_EXCEPTION(scope, nullptr);
145                 params.hashIdentifier = toHashIdentifier(state, params.hash);
146                 RETURN_IF_EXCEPTION(scope, nullptr);
147                 result = std::make_unique<CryptoAlgorithmRsaHashedKeyGenParams>(params);
148                 break;
149             }
150             case CryptoAlgorithmIdentifier::AES_CTR:
151             case CryptoAlgorithmIdentifier::AES_CBC:
152             case CryptoAlgorithmIdentifier::AES_CMAC:
153             case CryptoAlgorithmIdentifier::AES_GCM:
154             case CryptoAlgorithmIdentifier::AES_CFB:
155             case CryptoAlgorithmIdentifier::AES_KW: {
156                 auto params = convertDictionary<CryptoAlgorithmAesKeyGenParams>(state, value);
157                 RETURN_IF_EXCEPTION(scope, nullptr);
158                 result = std::make_unique<CryptoAlgorithmAesKeyGenParams>(params);
159                 break;
160             }
161             case CryptoAlgorithmIdentifier::HMAC: {
162                 auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value);
163                 RETURN_IF_EXCEPTION(scope, nullptr);
164                 params.hashIdentifier = toHashIdentifier(state, params.hash);
165                 RETURN_IF_EXCEPTION(scope, nullptr);
166                 result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params);
167                 break;
168             }
169             default:
170                 setDOMException(&state, NOT_SUPPORTED_ERR);
171                 return nullptr;
172             }
173             break;
174         case Operations::ImportKey:
175             switch (*identifier) {
176             case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
177                 result = std::make_unique<CryptoAlgorithmParameters>(params);
178                 break;
179             case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
180             case CryptoAlgorithmIdentifier::RSA_PSS:
181             case CryptoAlgorithmIdentifier::RSA_OAEP: {
182                 auto params = convertDictionary<CryptoAlgorithmRsaHashedImportParams>(state, value);
183                 RETURN_IF_EXCEPTION(scope, nullptr);
184                 params.hashIdentifier = toHashIdentifier(state, params.hash);
185                 RETURN_IF_EXCEPTION(scope, nullptr);
186                 result = std::make_unique<CryptoAlgorithmRsaHashedImportParams>(params);
187                 break;
188             }
189             case CryptoAlgorithmIdentifier::AES_CTR:
190             case CryptoAlgorithmIdentifier::AES_CBC:
191             case CryptoAlgorithmIdentifier::AES_CMAC:
192             case CryptoAlgorithmIdentifier::AES_GCM:
193             case CryptoAlgorithmIdentifier::AES_CFB:
194             case CryptoAlgorithmIdentifier::AES_KW:
195                 result = std::make_unique<CryptoAlgorithmParameters>(params);
196                 break;
197             case CryptoAlgorithmIdentifier::HMAC: {
198                 auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value);
199                 RETURN_IF_EXCEPTION(scope, nullptr);
200                 params.hashIdentifier = toHashIdentifier(state, params.hash);
201                 RETURN_IF_EXCEPTION(scope, nullptr);
202                 result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params);
203                 break;
204             }
205             default:
206                 setDOMException(&state, NOT_SUPPORTED_ERR);
207                 return nullptr;
208             }
209             break;
210         default:
211             ASSERT_NOT_REACHED();
212             return nullptr;
213         }
214
215         result->identifier = *identifier;
216         return result;
217     }
218
219     throwTypeError(&state, scope, ASCIILiteral("Invalid AlgorithmIdentifier"));
220     return nullptr;
221 }
222
223 static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(CryptoKeyUsage usage)
224 {
225     switch (usage) {
226     case CryptoKeyUsage::Encrypt:
227         return CryptoKeyUsageEncrypt;
228     case CryptoKeyUsage::Decrypt:
229         return CryptoKeyUsageDecrypt;
230     case CryptoKeyUsage::Sign:
231         return CryptoKeyUsageSign;
232     case CryptoKeyUsage::Verify:
233         return CryptoKeyUsageVerify;
234     case CryptoKeyUsage::DeriveKey:
235         return CryptoKeyUsageDeriveKey;
236     case CryptoKeyUsage::DeriveBits:
237         return CryptoKeyUsageDeriveBits;
238     case CryptoKeyUsage::WrapKey:
239         return CryptoKeyUsageWrapKey;
240     case CryptoKeyUsage::UnwrapKey:
241         return CryptoKeyUsageUnwrapKey;
242     }
243
244     ASSERT_NOT_REACHED();
245     return 0;
246 }
247
248 static CryptoKeyUsageBitmap cryptoKeyUsageBitmapFromJSValue(ExecState& state, JSValue value)
249 {
250     VM& vm = state.vm();
251     auto scope = DECLARE_THROW_SCOPE(vm);
252
253     CryptoKeyUsageBitmap result = 0;
254     auto usages = convert<IDLSequence<IDLEnumeration<CryptoKeyUsage>>>(state, value);
255     RETURN_IF_EXCEPTION(scope, 0);
256     // Maybe we shouldn't silently bypass duplicated usages?
257     for (auto usage : usages)
258         result |= toCryptoKeyUsageBitmap(usage);
259
260     return result;
261 }
262
263 static RefPtr<CryptoAlgorithm> createAlgorithm(ExecState& state, CryptoAlgorithmIdentifier identifier)
264 {
265     auto result = CryptoAlgorithmRegistry::singleton().create(identifier);
266     if (!result)
267         setDOMException(&state, NOT_SUPPORTED_ERR);
268     return result;
269 }
270
271 // Maybe we want more specific error messages?
272 static void rejectWithException(Ref<DeferredPromise>&& passedPromise, ExceptionCode ec)
273 {
274     switch (ec) {
275     case NOT_SUPPORTED_ERR:
276         passedPromise->reject(ec, ASCIILiteral("The algorithm is not supported"));
277         return;
278     case SYNTAX_ERR:
279         passedPromise->reject(ec, ASCIILiteral("A required parameter was missing or out-of-range"));
280         return;
281     case INVALID_STATE_ERR:
282         passedPromise->reject(ec, ASCIILiteral("The requested operation is not valid for the current state of the provided key"));
283         return;
284     case INVALID_ACCESS_ERR:
285         passedPromise->reject(ec, ASCIILiteral("The requested operation is not valid for the provided key"));
286         return;
287     case UnknownError:
288         passedPromise->reject(ec, ASCIILiteral("The operation failed for an unknown transient reason (e.g. out of memory)"));
289         return;
290     case DataError:
291         passedPromise->reject(ec, ASCIILiteral("Data provided to an operation does not meet requirements"));
292         return;
293     case OperationError:
294         passedPromise->reject(ec, ASCIILiteral("The operation failed for an operation-specific reason"));
295         return;
296     }
297     ASSERT_NOT_REACHED();
298 }
299
300 static KeyData toKeyData(ExecState& state, SubtleCrypto::KeyFormat format, JSValue value)
301 {
302     VM& vm = state.vm();
303     auto scope = DECLARE_THROW_SCOPE(vm);
304
305     KeyData result;
306     switch (format) {
307     case SubtleCrypto::KeyFormat::Spki:
308     case SubtleCrypto::KeyFormat::Pkcs8:
309         setDOMException(&state, NOT_SUPPORTED_ERR);
310         return result;
311     case SubtleCrypto::KeyFormat::Raw: {
312         BufferSource bufferSource = convert<IDLBufferSource>(state, value);
313         RETURN_IF_EXCEPTION(scope, result);
314         Vector<uint8_t> vector;
315         vector.append(bufferSource.data(), bufferSource.length());
316         result = WTFMove(vector);
317         return result;
318     }
319     case SubtleCrypto::KeyFormat::Jwk: {
320         result = convertDictionary<JsonWebKey>(state, value);
321         RETURN_IF_EXCEPTION(scope, result);
322         CryptoKeyUsageBitmap usages = 0;
323         if (WTF::get<JsonWebKey>(result).key_ops) {
324             // Maybe we shouldn't silently bypass duplicated usages?
325             for (auto usage : WTF::get<JsonWebKey>(result).key_ops.value())
326                 usages |= toCryptoKeyUsageBitmap(usage);
327         }
328         WTF::get<JsonWebKey>(result).usages = usages;
329         return result;
330     }
331     }
332     ASSERT_NOT_REACHED();
333     return result;
334 }
335
336 // FIXME: We should get rid of this once https://bugs.webkit.org/show_bug.cgi?id=163711 is fixed.
337 static JSValue toJSValueFromJsonWebKey(JSDOMGlobalObject& globalObject, JsonWebKey&& key)
338 {
339     ExecState& state = *globalObject.globalExec();
340     VM& vm = state.vm();
341
342     auto* result = constructEmptyObject(&state);
343     result->putDirect(vm, Identifier::fromString(&vm, "kty"), toJS<IDLDOMString>(state, key.kty));
344     if (key.use)
345         result->putDirect(vm, Identifier::fromString(&vm, "use"), toJS<IDLDOMString>(state, key.use.value()));
346     if (key.key_ops)
347         result->putDirect(vm, Identifier::fromString(&vm, "key_ops"), toJS<IDLSequence<IDLEnumeration<CryptoKeyUsage>>>(state, globalObject, key.key_ops.value()));
348     if (key.alg)
349         result->putDirect(vm, Identifier::fromString(&vm, "alg"), toJS<IDLDOMString>(state, key.alg.value()));
350     if (key.ext)
351         result->putDirect(vm, Identifier::fromString(&vm, "ext"), toJS<IDLBoolean>(state, key.ext.value()));
352     if (key.crv)
353         result->putDirect(vm, Identifier::fromString(&vm, "crv"), toJS<IDLDOMString>(state, key.crv.value()));
354     if (key.x)
355         result->putDirect(vm, Identifier::fromString(&vm, "x"), toJS<IDLDOMString>(state, key.x.value()));
356     if (key.y)
357         result->putDirect(vm, Identifier::fromString(&vm, "y"), toJS<IDLDOMString>(state, key.y.value()));
358     if (key.d)
359         result->putDirect(vm, Identifier::fromString(&vm, "d"), toJS<IDLDOMString>(state, key.d.value()));
360     if (key.n)
361         result->putDirect(vm, Identifier::fromString(&vm, "n"), toJS<IDLDOMString>(state, key.n.value()));
362     if (key.e)
363         result->putDirect(vm, Identifier::fromString(&vm, "e"), toJS<IDLDOMString>(state, key.e.value()));
364     if (key.p)
365         result->putDirect(vm, Identifier::fromString(&vm, "p"), toJS<IDLDOMString>(state, key.p.value()));
366     if (key.q)
367         result->putDirect(vm, Identifier::fromString(&vm, "q"), toJS<IDLDOMString>(state, key.q.value()));
368     if (key.dp)
369         result->putDirect(vm, Identifier::fromString(&vm, "dp"), toJS<IDLDOMString>(state, key.dp.value()));
370     if (key.dq)
371         result->putDirect(vm, Identifier::fromString(&vm, "dq"), toJS<IDLDOMString>(state, key.dq.value()));
372     if (key.qi)
373         result->putDirect(vm, Identifier::fromString(&vm, "qi"), toJS<IDLDOMString>(state, key.qi.value()));
374     if (key.oth) {
375         MarkedArgumentBuffer list;
376         for (auto& value : key.oth.value()) {
377             auto* info = constructEmptyObject(&state);
378             info->putDirect(vm, Identifier::fromString(&vm, "r"), toJS<IDLDOMString>(state, value.r));
379             info->putDirect(vm, Identifier::fromString(&vm, "d"), toJS<IDLDOMString>(state, value.d));
380             info->putDirect(vm, Identifier::fromString(&vm, "t"), toJS<IDLDOMString>(state, value.t));
381             list.append(info);
382         }
383         result->putDirect(vm, Identifier::fromString(&vm, "oth"), constructArray(&state, static_cast<Structure*>(nullptr), list));
384     }
385     if (key.k)
386         result->putDirect(vm, Identifier::fromString(&vm, "k"), toJS<IDLDOMString>(state, key.k.value()));
387
388     return result;
389 }
390
391 static void jsSubtleCryptoFunctionEncryptPromise(ExecState& state, Ref<DeferredPromise>&& promise)
392 {
393     VM& vm = state.vm();
394     auto scope = DECLARE_THROW_SCOPE(vm);
395
396     if (UNLIKELY(state.argumentCount() < 3)) {
397         promise->reject<JSValue>(createNotEnoughArgumentsError(&state));
398         return;
399     }
400
401     auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::Encrypt);
402     RETURN_IF_EXCEPTION(scope, void());
403
404     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
405     if (!key) {
406         promise->reject(TypeError, ASCIILiteral("Invalid CryptoKey"));
407         return;
408     }
409
410     BufferSource data = convert<IDLBufferSource>(state, state.uncheckedArgument(2));
411     RETURN_IF_EXCEPTION(scope, void());
412     Vector<uint8_t> dataVector;
413     dataVector.append(data.data(), data.length());
414
415     if (params->identifier != key->algorithmIdentifier()) {
416         promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't match AlgorithmIdentifier"));
417         return;
418     }
419
420     if (!key->allows(CryptoKeyUsageEncrypt)) {
421         promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("CryptoKey doesn't support encryption"));
422         return;
423     }
424
425     auto algorithm = createAlgorithm(state, key->algorithmIdentifier());
426     RETURN_IF_EXCEPTION(scope, void());
427
428     auto callback = [capturedPromise = promise.copyRef()](const Vector<uint8_t>& cipherText) mutable {
429         fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), cipherText.data(), cipherText.size());
430         return;
431     };
432     auto exceptionCallback = [capturedPromise = promise.copyRef()](ExceptionCode ec) mutable {
433         rejectWithException(WTFMove(capturedPromise), ec);
434     };
435
436     JSSubtleCrypto* subtle = jsDynamicDowncast<JSSubtleCrypto*>(state.thisValue());
437     ASSERT(subtle);
438     algorithm->encrypt(WTFMove(params), key.releaseNonNull(), WTFMove(dataVector), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state), subtle->wrapped().workQueue());
439 }
440
441 static void jsSubtleCryptoFunctionGenerateKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise)
442 {
443     VM& vm = state.vm();
444     auto scope = DECLARE_THROW_SCOPE(vm);
445
446     if (UNLIKELY(state.argumentCount() < 3)) {
447         promise->reject<JSValue>(createNotEnoughArgumentsError(&state));
448         return;
449     }
450
451     auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(0), Operations::GenerateKey);
452     RETURN_IF_EXCEPTION(scope, void());
453
454     auto extractable = state.uncheckedArgument(1).toBoolean(&state);
455     RETURN_IF_EXCEPTION(scope, void());
456
457     auto keyUsages = cryptoKeyUsageBitmapFromJSValue(state, state.uncheckedArgument(2));
458     RETURN_IF_EXCEPTION(scope, void());
459
460     auto algorithm = createAlgorithm(state, params->identifier);
461     RETURN_IF_EXCEPTION(scope, void());
462
463     auto callback = [capturedPromise = promise.copyRef()](CryptoKey* key, CryptoKeyPair* keyPair) mutable {
464         ASSERT(key || keyPair);
465         ASSERT(!key || !keyPair);
466         if (key) {
467             if ((key->type() == CryptoKeyType::Private || key->type() == CryptoKeyType::Secret) && !key->usagesBitmap()) {
468                 rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR);
469                 return;
470             }
471             capturedPromise->resolve(key);
472         } else {
473             if (!keyPair->privateKey()->usagesBitmap()) {
474                 rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR);
475                 return;
476             }
477             capturedPromise->resolve(keyPair);
478         }
479     };
480     auto exceptionCallback = [capturedPromise =  promise.copyRef()](ExceptionCode ec) mutable {
481         rejectWithException(WTFMove(capturedPromise), ec);
482     };
483
484     // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously
485     // regardless what kind of keys it produces: https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey
486     // That's simply not efficient for AES and HMAC keys. Therefore, we perform it as an async task conditionally.
487     algorithm->generateKey(WTFMove(params), extractable, keyUsages, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContextFromExecState(&state));
488 }
489
490 static void jsSubtleCryptoFunctionImportKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise)
491 {
492     VM& vm = state.vm();
493     auto scope = DECLARE_THROW_SCOPE(vm);
494
495     if (UNLIKELY(state.argumentCount() < 5)) {
496         promise->reject<JSValue>(createNotEnoughArgumentsError(&state));
497         return;
498     }
499
500     auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0));
501     RETURN_IF_EXCEPTION(scope, void());
502
503     auto keyData = toKeyData(state, format, state.uncheckedArgument(1));
504     RETURN_IF_EXCEPTION(scope, void());
505
506     auto params = normalizeCryptoAlgorithmParameters(state, state.uncheckedArgument(2), Operations::ImportKey);
507     RETURN_IF_EXCEPTION(scope, void());
508
509     auto extractable = state.uncheckedArgument(3).toBoolean(&state);
510     RETURN_IF_EXCEPTION(scope, void());
511
512     auto keyUsages = cryptoKeyUsageBitmapFromJSValue(state, state.uncheckedArgument(4));
513     RETURN_IF_EXCEPTION(scope, void());
514
515     auto algorithm = createAlgorithm(state, params->identifier);
516     RETURN_IF_EXCEPTION(scope, void());
517
518     auto callback = [capturedPromise = promise.copyRef()](CryptoKey& key) mutable {
519         if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) {
520             rejectWithException(WTFMove(capturedPromise), SYNTAX_ERR);
521             return;
522         }
523         capturedPromise->resolve(key);
524     };
525     auto exceptionCallback = [capturedPromise =  promise.copyRef()](ExceptionCode ec) mutable {
526         rejectWithException(WTFMove(capturedPromise), ec);
527     };
528
529     // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
530     // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey
531     // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
532     algorithm->importKey(format, WTFMove(keyData), WTFMove(params), extractable, keyUsages, WTFMove(callback), WTFMove(exceptionCallback));
533 }
534
535 static void jsSubtleCryptoFunctionExportKeyPromise(ExecState& state, Ref<DeferredPromise>&& promise)
536 {
537     VM& vm = state.vm();
538     auto scope = DECLARE_THROW_SCOPE(vm);
539
540     if (UNLIKELY(state.argumentCount() < 2)) {
541         promise->reject<JSValue>(createNotEnoughArgumentsError(&state));
542         return;
543     }
544
545     auto format = convertEnumeration<SubtleCrypto::KeyFormat>(state, state.uncheckedArgument(0));
546     RETURN_IF_EXCEPTION(scope, void());
547
548     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
549     if (!key) {
550         promise->reject(TypeError, ASCIILiteral("Invalid CryptoKey"));
551         return;
552     }
553
554     switch (key->algorithmIdentifier()) {
555     case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5:
556     case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5:
557     case CryptoAlgorithmIdentifier::RSA_PSS:
558     case CryptoAlgorithmIdentifier::RSA_OAEP:
559     case CryptoAlgorithmIdentifier::AES_CTR:
560     case CryptoAlgorithmIdentifier::AES_CBC:
561     case CryptoAlgorithmIdentifier::AES_CMAC:
562     case CryptoAlgorithmIdentifier::AES_GCM:
563     case CryptoAlgorithmIdentifier::AES_CFB:
564     case CryptoAlgorithmIdentifier::AES_KW:
565     case CryptoAlgorithmIdentifier::HMAC:
566         break;
567     default:
568         promise->reject(NOT_SUPPORTED_ERR, ASCIILiteral("The operation is not supported"));
569         return;
570     }
571
572     if (!key->extractable()) {
573         promise->reject(INVALID_ACCESS_ERR, ASCIILiteral("The CryptoKey is nonextractable"));
574         return;
575     }
576
577     auto algorithm = createAlgorithm(state, key->algorithmIdentifier());
578     RETURN_IF_EXCEPTION(scope, void());
579
580     auto callback = [capturedPromise = promise.copyRef()](SubtleCrypto::KeyFormat format, KeyData&& key) mutable {
581         switch (format) {
582         case SubtleCrypto::KeyFormat::Spki:
583         case SubtleCrypto::KeyFormat::Pkcs8:
584         case SubtleCrypto::KeyFormat::Raw: {
585             Vector<uint8_t>& rawKey = WTF::get<Vector<uint8_t>>(key);
586             fulfillPromiseWithArrayBuffer(WTFMove(capturedPromise), rawKey.data(), rawKey.size());
587             return;
588         }
589         case SubtleCrypto::KeyFormat::Jwk:
590             capturedPromise->resolve(toJSValueFromJsonWebKey(*(capturedPromise->globalObject()), WTFMove(WTF::get<JsonWebKey>(key))));
591             return;
592         }
593         ASSERT_NOT_REACHED();
594     };
595     auto exceptionCallback = [capturedPromise =  promise.copyRef()](ExceptionCode ec) mutable {
596         rejectWithException(WTFMove(capturedPromise), ec);
597     };
598
599     // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously:
600     // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey
601     // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously.
602     algorithm->exportKey(format, key.releaseNonNull(), WTFMove(callback), WTFMove(exceptionCallback));
603 }
604
605 JSValue JSSubtleCrypto::encrypt(ExecState& state)
606 {
607     return callPromiseFunction<jsSubtleCryptoFunctionEncryptPromise, PromiseExecutionScope::WindowOrWorker>(state);
608 }
609
610 JSValue JSSubtleCrypto::generateKey(ExecState& state)
611 {
612     return callPromiseFunction<jsSubtleCryptoFunctionGenerateKeyPromise, PromiseExecutionScope::WindowOrWorker>(state);
613 }
614
615 JSValue JSSubtleCrypto::importKey(ExecState& state)
616 {
617     return callPromiseFunction<jsSubtleCryptoFunctionImportKeyPromise, PromiseExecutionScope::WindowOrWorker>(state);
618 }
619
620 JSValue JSSubtleCrypto::exportKey(ExecState& state)
621 {
622     return callPromiseFunction<jsSubtleCryptoFunctionExportKeyPromise, PromiseExecutionScope::WindowOrWorker>(state);
623 }
624
625 } // namespace WebCore
626
627 #endif