ca9e14b8b6646dd64a84a9293e6f9b89e93fb8c4
[WebKit-https.git] / Source / WebCore / bindings / js / JSWebKitSubtleCryptoCustom.cpp
1 /*
2  * Copyright (C) 2013, 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 "JSWebKitSubtleCrypto.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmParameters.h"
33 #include "CryptoAlgorithmRegistry.h"
34 #include "CryptoKeyData.h"
35 #include "CryptoKeySerializationRaw.h"
36 #include "Document.h"
37 #include "ExceptionCode.h"
38 #include "JSCryptoAlgorithmDictionary.h"
39 #include "JSCryptoKey.h"
40 #include "JSCryptoKeyPair.h"
41 #include "JSCryptoKeySerializationJWK.h"
42 #include "JSCryptoOperationData.h"
43 #include "JSDOMPromise.h"
44 #include <runtime/Error.h>
45
46 using namespace JSC;
47
48 namespace WebCore {
49
50 enum class CryptoKeyFormat {
51     // An unformatted sequence of bytes. Intended for secret keys.
52     Raw,
53
54     // The DER encoding of the PrivateKeyInfo structure from RFC 5208.
55     PKCS8,
56
57     // The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
58     SPKI,
59
60     // The key is represented as JSON according to the JSON Web Key format.
61     JWK
62 };
63
64 static RefPtr<CryptoAlgorithm> createAlgorithmFromJSValue(ExecState& state, JSValue value)
65 {
66     VM& vm = state.vm();
67     auto scope = DECLARE_THROW_SCOPE(vm);
68
69     CryptoAlgorithmIdentifier algorithmIdentifier;
70     auto success = JSCryptoAlgorithmDictionary::getAlgorithmIdentifier(&state, value, algorithmIdentifier);
71     ASSERT_UNUSED(scope, scope.exception() || success);
72     if (!success)
73         return nullptr;
74
75     auto result = CryptoAlgorithmRegistry::singleton().create(algorithmIdentifier);
76     if (!result)
77         setDOMException(&state, NOT_SUPPORTED_ERR);
78     return result;
79 }
80
81 static bool cryptoKeyFormatFromJSValue(ExecState& state, JSValue value, CryptoKeyFormat& result)
82 {
83     VM& vm = state.vm();
84     auto scope = DECLARE_THROW_SCOPE(vm);
85
86     String keyFormatString = value.toString(&state)->value(&state);
87     if (UNLIKELY(scope.exception()))
88         return false;
89     if (keyFormatString == "raw")
90         result = CryptoKeyFormat::Raw;
91     else if (keyFormatString == "pkcs8")
92         result = CryptoKeyFormat::PKCS8;
93     else if (keyFormatString == "spki")
94         result = CryptoKeyFormat::SPKI;
95     else if (keyFormatString == "jwk")
96         result = CryptoKeyFormat::JWK;
97     else {
98         throwTypeError(&state, scope, ASCIILiteral("Unknown key format"));
99         return false;
100     }
101     return true;
102 }
103
104 static bool cryptoKeyUsagesFromJSValue(ExecState& state, JSValue value, CryptoKeyUsage& result)
105 {
106     VM& vm = state.vm();
107     auto scope = DECLARE_THROW_SCOPE(vm);
108
109     if (!isJSArray(value)) {
110         throwTypeError(&state, scope);
111         return false;
112     }
113
114     result = 0;
115
116     JSArray* array = asArray(value);
117     for (size_t i = 0; i < array->length(); ++i) {
118         JSValue element = array->getIndex(&state, i);
119         String usageString = element.toString(&state)->value(&state);
120         if (UNLIKELY(scope.exception()))
121             return false;
122         if (usageString == "encrypt")
123             result |= CryptoKeyUsageEncrypt;
124         else if (usageString == "decrypt")
125             result |= CryptoKeyUsageDecrypt;
126         else if (usageString == "sign")
127             result |= CryptoKeyUsageSign;
128         else if (usageString == "verify")
129             result |= CryptoKeyUsageVerify;
130         else if (usageString == "deriveKey")
131             result |= CryptoKeyUsageDeriveKey;
132         else if (usageString == "deriveBits")
133             result |= CryptoKeyUsageDeriveBits;
134         else if (usageString == "wrapKey")
135             result |= CryptoKeyUsageWrapKey;
136         else if (usageString == "unwrapKey")
137             result |= CryptoKeyUsageUnwrapKey;
138     }
139     return true;
140 }
141
142 JSValue JSWebKitSubtleCrypto::encrypt(ExecState& state)
143 {
144     VM& vm = state.vm();
145     auto scope = DECLARE_THROW_SCOPE(vm);
146
147     if (state.argumentCount() < 3)
148         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
149
150     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
151     ASSERT(scope.exception() || algorithm);
152     if (!algorithm)
153         return jsUndefined();
154
155     auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(&state, algorithm->identifier(), state.uncheckedArgument(0));
156     ASSERT(scope.exception() || parameters);
157     if (!parameters)
158         return jsUndefined();
159
160     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
161     if (!key)
162         return throwTypeError(&state, scope);
163
164     if (!key->allows(CryptoKeyUsageEncrypt)) {
165         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'encrypt'"));
166         setDOMException(&state, NOT_SUPPORTED_ERR);
167         return jsUndefined();
168     }
169
170     CryptoOperationData data;
171     auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(2), data);
172     ASSERT(scope.exception() || success);
173     if (!success)
174         return jsUndefined();
175
176     
177     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
178     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
179     auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
180         fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
181     };
182     auto failureCallback = [wrapper]() mutable {
183         wrapper->reject(nullptr);
184     };
185
186     ExceptionCode ec = 0;
187     algorithm->encrypt(*parameters, *key, data, WTFMove(successCallback), WTFMove(failureCallback), ec);
188     if (ec) {
189         setDOMException(&state, ec);
190         return jsUndefined();
191     }
192
193     return promiseDeferred->promise();
194 }
195
196 JSValue JSWebKitSubtleCrypto::decrypt(ExecState& state)
197 {
198     VM& vm = state.vm();
199     auto scope = DECLARE_THROW_SCOPE(vm);
200
201     if (state.argumentCount() < 3)
202         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
203
204     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
205     ASSERT(scope.exception() || algorithm);
206     if (!algorithm)
207         return jsUndefined();
208
209     auto parameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(&state, algorithm->identifier(), state.uncheckedArgument(0));
210     ASSERT(scope.exception() || parameters);
211     if (!parameters)
212         return jsUndefined();
213
214     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
215     if (!key)
216         return throwTypeError(&state, scope);
217
218     if (!key->allows(CryptoKeyUsageDecrypt)) {
219         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'decrypt'"));
220         setDOMException(&state, NOT_SUPPORTED_ERR);
221         return jsUndefined();
222     }
223
224     CryptoOperationData data;
225     auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(2), data);
226     ASSERT(scope.exception() || success);
227     if (!success)
228         return jsUndefined();
229
230     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
231     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
232     auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
233         fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
234     };
235     auto failureCallback = [wrapper]() mutable {
236         wrapper->reject(nullptr);
237     };
238
239     ExceptionCode ec = 0;
240     algorithm->decrypt(*parameters, *key, data, WTFMove(successCallback), WTFMove(failureCallback), ec);
241     if (ec) {
242         setDOMException(&state, ec);
243         return jsUndefined();
244     }
245
246     return promiseDeferred->promise();
247 }
248
249 JSValue JSWebKitSubtleCrypto::sign(ExecState& state)
250 {
251     VM& vm = state.vm();
252     auto scope = DECLARE_THROW_SCOPE(vm);
253
254     if (state.argumentCount() < 3)
255         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
256
257     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
258     ASSERT(scope.exception() || algorithm);
259     if (!algorithm)
260         return jsUndefined();
261
262     auto parameters = JSCryptoAlgorithmDictionary::createParametersForSign(&state, algorithm->identifier(), state.uncheckedArgument(0));
263     ASSERT(scope.exception() || parameters);
264     if (!parameters)
265         return jsUndefined();
266
267     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
268     if (!key)
269         return throwTypeError(&state, scope);
270
271     if (!key->allows(CryptoKeyUsageSign)) {
272         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'sign'"));
273         setDOMException(&state, NOT_SUPPORTED_ERR);
274         return jsUndefined();
275     }
276
277     CryptoOperationData data;
278     auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(2), data);
279     ASSERT(scope.exception() || success);
280     if (!success)
281         return jsUndefined();
282
283     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
284     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
285     auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
286         fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
287     };
288     auto failureCallback = [wrapper]() mutable {
289         wrapper->reject(nullptr);
290     };
291
292     ExceptionCode ec = 0;
293     algorithm->sign(*parameters, *key, data, WTFMove(successCallback), WTFMove(failureCallback), ec);
294     if (ec) {
295         setDOMException(&state, ec);
296         return jsUndefined();
297     }
298
299     return promiseDeferred->promise();
300 }
301
302 JSValue JSWebKitSubtleCrypto::verify(ExecState& state)
303 {
304     VM& vm = state.vm();
305     auto scope = DECLARE_THROW_SCOPE(vm);
306
307     if (state.argumentCount() < 4)
308         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
309
310     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
311     ASSERT(scope.exception() || algorithm);
312     if (!algorithm)
313         return jsUndefined();
314
315     auto parameters = JSCryptoAlgorithmDictionary::createParametersForVerify(&state, algorithm->identifier(), state.uncheckedArgument(0));
316     ASSERT(scope.exception() || parameters);
317     if (!parameters)
318         return jsUndefined();
319
320     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
321     if (!key)
322         return throwTypeError(&state, scope);
323
324     if (!key->allows(CryptoKeyUsageVerify)) {
325         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'verify'"));
326         setDOMException(&state, NOT_SUPPORTED_ERR);
327         return jsUndefined();
328     }
329
330     CryptoOperationData signature;
331     auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(2), signature);
332     ASSERT(scope.exception() || success);
333     if (!success)
334         return jsUndefined();
335
336     CryptoOperationData data;
337     success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(3), data);
338     ASSERT(scope.exception() || success);
339     if (!success)
340         return jsUndefined();
341
342     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
343     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
344     auto successCallback = [wrapper](bool result) mutable {
345         wrapper->resolve(result);
346     };
347     auto failureCallback = [wrapper]() mutable {
348         wrapper->reject(nullptr);
349     };
350
351     ExceptionCode ec = 0;
352     algorithm->verify(*parameters, *key, signature, data, WTFMove(successCallback), WTFMove(failureCallback), ec);
353     if (ec) {
354         setDOMException(&state, ec);
355         return jsUndefined();
356     }
357
358     return promiseDeferred->promise();
359 }
360
361 JSValue JSWebKitSubtleCrypto::digest(ExecState& state)
362 {
363     VM& vm = state.vm();
364     auto scope = DECLARE_THROW_SCOPE(vm);
365
366     if (state.argumentCount() < 2)
367         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
368
369     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
370     ASSERT(scope.exception() || algorithm);
371     if (!algorithm)
372         return jsUndefined();
373
374     auto parameters = JSCryptoAlgorithmDictionary::createParametersForDigest(&state, algorithm->identifier(), state.uncheckedArgument(0));
375     ASSERT(scope.exception() || parameters);
376     if (!parameters)
377         return jsUndefined();
378
379     CryptoOperationData data;
380     auto success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(1), data);
381     ASSERT(scope.exception() || success);
382     if (!success)
383         return jsUndefined();
384
385     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
386     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
387     auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
388         fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
389     };
390     auto failureCallback = [wrapper]() mutable {
391         wrapper->reject(nullptr);
392     };
393
394     ExceptionCode ec = 0;
395     algorithm->digest(*parameters, data, WTFMove(successCallback), WTFMove(failureCallback), ec);
396     if (ec) {
397         setDOMException(&state, ec);
398         return jsUndefined();
399     }
400
401     return promiseDeferred->promise();
402 }
403
404 JSValue JSWebKitSubtleCrypto::generateKey(ExecState& state)
405 {
406     VM& vm = state.vm();
407     auto scope = DECLARE_THROW_SCOPE(vm);
408
409     if (state.argumentCount() < 1)
410         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
411
412     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(0));
413     ASSERT(scope.exception() || algorithm);
414     if (!algorithm)
415         return jsUndefined();
416
417     auto parameters = JSCryptoAlgorithmDictionary::createParametersForGenerateKey(&state, algorithm->identifier(), state.uncheckedArgument(0));
418     ASSERT(scope.exception() || parameters);
419     if (!parameters)
420         return jsUndefined();
421
422     bool extractable = false;
423     if (state.argumentCount() >= 2) {
424         extractable = state.uncheckedArgument(1).toBoolean(&state);
425         if (UNLIKELY(scope.exception()))
426             return jsUndefined();
427     }
428
429     CryptoKeyUsage keyUsages = 0;
430     if (state.argumentCount() >= 3) {
431         auto success = cryptoKeyUsagesFromJSValue(state, state.argument(2), keyUsages);
432         ASSERT(scope.exception() || success);
433         if (!success)
434             return jsUndefined();
435     }
436
437     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
438     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
439     auto successCallback = [wrapper](CryptoKey* key, CryptoKeyPair* keyPair) mutable {
440         ASSERT(key || keyPair);
441         ASSERT(!key || !keyPair);
442         if (key)
443             wrapper->resolve(key);
444         else
445             wrapper->resolve(keyPair);
446     };
447     auto failureCallback = [wrapper]() mutable {
448         wrapper->reject(nullptr);
449     };
450
451     ExceptionCode ec = 0;
452     algorithm->generateKey(*parameters, extractable, keyUsages, WTFMove(successCallback), WTFMove(failureCallback), ec);
453     if (ec) {
454         setDOMException(&state, ec);
455         return jsUndefined();
456     }
457
458     return promiseDeferred->promise();
459 }
460
461 static void importKey(ExecState& state, CryptoKeyFormat keyFormat, CryptoOperationData data, RefPtr<CryptoAlgorithm> algorithm, RefPtr<CryptoAlgorithmParameters> parameters, bool extractable, CryptoKeyUsage keyUsages, CryptoAlgorithm::KeyCallback callback, CryptoAlgorithm::VoidCallback failureCallback)
462 {
463     VM& vm = state.vm();
464     auto scope = DECLARE_THROW_SCOPE(vm);
465
466     std::unique_ptr<CryptoKeySerialization> keySerialization;
467     switch (keyFormat) {
468     case CryptoKeyFormat::Raw:
469         keySerialization = CryptoKeySerializationRaw::create(data);
470         break;
471     case CryptoKeyFormat::JWK: {
472         String jwkString = String::fromUTF8(data.first, data.second);
473         if (jwkString.isNull()) {
474             throwTypeError(&state, scope, ASCIILiteral("JWK JSON serialization is not valid UTF-8"));
475             return;
476         }
477         keySerialization = std::make_unique<JSCryptoKeySerializationJWK>(&state, jwkString);
478         if (UNLIKELY(scope.exception()))
479             return;
480         break;
481     }
482     default:
483         throwTypeError(&state, scope, ASCIILiteral("Unsupported key format for import"));
484         return;
485     }
486
487     ASSERT(keySerialization);
488
489     Optional<CryptoAlgorithmPair> reconciledResult = keySerialization->reconcileAlgorithm(algorithm.get(), parameters.get());
490     if (!reconciledResult) {
491         if (!scope.exception())
492             throwTypeError(&state, scope, ASCIILiteral("Algorithm specified in key is not compatible with one passed to importKey as argument"));
493         return;
494     }
495     if (UNLIKELY(scope.exception()))
496         return;
497
498     algorithm = reconciledResult->algorithm;
499     parameters = reconciledResult->parameters;
500     if (!algorithm) {
501         throwTypeError(&state, scope, ASCIILiteral("Neither key nor function argument has crypto algorithm specified"));
502         return;
503     }
504     ASSERT(parameters);
505
506     keySerialization->reconcileExtractable(extractable);
507     if (UNLIKELY(scope.exception()))
508         return;
509
510     keySerialization->reconcileUsages(keyUsages);
511     if (UNLIKELY(scope.exception()))
512         return;
513
514     auto keyData = keySerialization->keyData();
515     if (UNLIKELY(scope.exception()))
516         return;
517
518     ExceptionCode ec = 0;
519     algorithm->importKey(*parameters, *keyData, extractable, keyUsages, WTFMove(callback), WTFMove(failureCallback), ec);
520     if (ec)
521         setDOMException(&state, ec);
522 }
523
524 JSValue JSWebKitSubtleCrypto::importKey(ExecState& state)
525 {
526     VM& vm = state.vm();
527     auto scope = DECLARE_THROW_SCOPE(vm);
528
529     if (state.argumentCount() < 3)
530         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
531
532     CryptoKeyFormat keyFormat;
533     auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
534     ASSERT(scope.exception() || success);
535     if (!success)
536         return jsUndefined();
537
538     CryptoOperationData data;
539     success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(1), data);
540     ASSERT(scope.exception() || success);
541     if (!success)
542         return jsUndefined();
543
544     RefPtr<CryptoAlgorithm> algorithm;
545     RefPtr<CryptoAlgorithmParameters> parameters;
546     if (!state.uncheckedArgument(2).isNull()) {
547         algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(2));
548         ASSERT(scope.exception() || algorithm);
549         if (!algorithm)
550             return jsUndefined();
551
552         parameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(&state, algorithm->identifier(), state.uncheckedArgument(2));
553         ASSERT(scope.exception() || parameters);
554         if (!parameters)
555             return jsUndefined();
556     }
557
558     bool extractable = false;
559     if (state.argumentCount() >= 4) {
560         extractable = state.uncheckedArgument(3).toBoolean(&state);
561         if (UNLIKELY(scope.exception()))
562             return jsUndefined();
563     }
564
565     CryptoKeyUsage keyUsages = 0;
566     if (state.argumentCount() >= 5) {
567         auto success = cryptoKeyUsagesFromJSValue(state, state.argument(4), keyUsages);
568         ASSERT(scope.exception() || success);
569         if (!success)
570             return jsUndefined();
571     }
572
573     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
574     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
575     auto successCallback = [wrapper](CryptoKey& result) mutable {
576         wrapper->resolve(result);
577     };
578     auto failureCallback = [wrapper]() mutable {
579         wrapper->reject(nullptr);
580     };
581
582     WebCore::importKey(state, keyFormat, data, WTFMove(algorithm), WTFMove(parameters), extractable, keyUsages, WTFMove(successCallback), WTFMove(failureCallback));
583     if (UNLIKELY(scope.exception()))
584         return jsUndefined();
585
586     return promiseDeferred->promise();
587 }
588
589 static void exportKey(ExecState& state, CryptoKeyFormat keyFormat, const CryptoKey& key, CryptoAlgorithm::VectorCallback callback, CryptoAlgorithm::VoidCallback failureCallback)
590 {
591     VM& vm = state.vm();
592     auto scope = DECLARE_THROW_SCOPE(vm);
593
594     if (!key.extractable()) {
595         throwTypeError(&state, scope, ASCIILiteral("Key is not extractable"));
596         return;
597     }
598
599     switch (keyFormat) {
600     case CryptoKeyFormat::Raw: {
601         Vector<uint8_t> result;
602         if (CryptoKeySerializationRaw::serialize(key, result))
603             callback(result);
604         else
605             failureCallback();
606         break;
607     }
608     case CryptoKeyFormat::JWK: {
609         String result = JSCryptoKeySerializationJWK::serialize(&state, key);
610         if (UNLIKELY(scope.exception()))
611             return;
612         CString utf8String = result.utf8(StrictConversion);
613         Vector<uint8_t> resultBuffer;
614         resultBuffer.append(utf8String.data(), utf8String.length());
615         callback(resultBuffer);
616         break;
617     }
618     default:
619         throwTypeError(&state, scope, ASCIILiteral("Unsupported key format for export"));
620         break;
621     }
622 }
623
624 JSValue JSWebKitSubtleCrypto::exportKey(ExecState& state)
625 {
626     VM& vm = state.vm();
627     auto scope = DECLARE_THROW_SCOPE(vm);
628
629     if (state.argumentCount() < 2)
630         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
631
632     CryptoKeyFormat keyFormat;
633     auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
634     ASSERT(scope.exception() || success);
635     if (!success)
636         return jsUndefined();
637
638     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
639     if (!key)
640         return throwTypeError(&state, scope);
641
642     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
643     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
644     auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
645         fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), result.data(), result.size());
646     };
647     auto failureCallback = [wrapper]() mutable {
648         wrapper->reject(nullptr);
649     };
650
651     WebCore::exportKey(state, keyFormat, *key, WTFMove(successCallback), WTFMove(failureCallback));
652     if (UNLIKELY(scope.exception()))
653         return jsUndefined();
654
655     return promiseDeferred->promise();
656 }
657
658 JSValue JSWebKitSubtleCrypto::wrapKey(ExecState& state)
659 {
660     VM& vm = state.vm();
661     auto scope = DECLARE_THROW_SCOPE(vm);
662
663     if (state.argumentCount() < 4)
664         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
665
666     CryptoKeyFormat keyFormat;
667     auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
668     ASSERT(scope.exception() || success);
669     if (!success)
670         return jsUndefined();
671
672     RefPtr<CryptoKey> key = JSCryptoKey::toWrapped(state.uncheckedArgument(1));
673     if (!key)
674         return throwTypeError(&state, scope);
675
676     RefPtr<CryptoKey> wrappingKey = JSCryptoKey::toWrapped(state.uncheckedArgument(2));
677     if (!key)
678         return throwTypeError(&state, scope);
679
680     if (!wrappingKey->allows(CryptoKeyUsageWrapKey)) {
681         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'wrapKey'"));
682         setDOMException(&state, NOT_SUPPORTED_ERR);
683         return jsUndefined();
684     }
685
686     auto algorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(3));
687     ASSERT(scope.exception() || algorithm);
688     if (!algorithm)
689         return jsUndefined();
690
691     auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(&state, algorithm->identifier(), state.uncheckedArgument(3));
692     ASSERT(scope.exception() || parameters);
693     if (!parameters)
694         return jsUndefined();
695
696     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
697     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
698
699     auto exportSuccessCallback = [keyFormat, algorithm, parameters, wrappingKey, wrapper](const Vector<uint8_t>& exportedKeyData) mutable {
700         auto encryptSuccessCallback = [wrapper](const Vector<uint8_t>& encryptedData) mutable {
701             fulfillPromiseWithArrayBuffer(wrapper.releaseNonNull(), encryptedData.data(), encryptedData.size());
702         };
703         auto encryptFailureCallback = [wrapper]() mutable {
704             wrapper->reject(nullptr);
705         };
706         ExceptionCode ec = 0;
707         algorithm->encryptForWrapKey(*parameters, *wrappingKey, std::make_pair(exportedKeyData.data(), exportedKeyData.size()), WTFMove(encryptSuccessCallback), WTFMove(encryptFailureCallback), ec);
708         if (ec) {
709             // FIXME: Report failure details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
710             wrapper->reject(nullptr);
711         }
712     };
713
714     auto exportFailureCallback = [wrapper]() mutable {
715         wrapper->reject(nullptr);
716     };
717
718     ExceptionCode ec = 0;
719     WebCore::exportKey(state, keyFormat, *key, WTFMove(exportSuccessCallback), WTFMove(exportFailureCallback));
720     if (ec) {
721         setDOMException(&state, ec);
722         return jsUndefined();
723     }
724
725     return promiseDeferred->promise();
726 }
727
728 JSValue JSWebKitSubtleCrypto::unwrapKey(ExecState& state)
729 {
730     VM& vm = state.vm();
731     auto scope = DECLARE_THROW_SCOPE(vm);
732
733     if (state.argumentCount() < 5)
734         return throwException(&state, scope, createNotEnoughArgumentsError(&state));
735
736     CryptoKeyFormat keyFormat;
737     auto success = cryptoKeyFormatFromJSValue(state, state.argument(0), keyFormat);
738     ASSERT(scope.exception() || success);
739     if (!success)
740         return jsUndefined();
741
742     CryptoOperationData wrappedKeyData;
743     success = cryptoOperationDataFromJSValue(&state, state.uncheckedArgument(1), wrappedKeyData);
744     ASSERT(scope.exception() || success);
745     if (!success)
746         return jsUndefined();
747
748     RefPtr<CryptoKey> unwrappingKey = JSCryptoKey::toWrapped(state.uncheckedArgument(2));
749     if (!unwrappingKey)
750         return throwTypeError(&state, scope);
751
752     if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) {
753         wrapped().document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Key usages do not include 'unwrapKey'"));
754         setDOMException(&state, NOT_SUPPORTED_ERR);
755         return jsUndefined();
756     }
757
758     auto unwrapAlgorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(3));
759     ASSERT(scope.exception() || unwrapAlgorithm);
760     if (!unwrapAlgorithm)
761         return jsUndefined();
762     auto unwrapAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(&state, unwrapAlgorithm->identifier(), state.uncheckedArgument(3));
763     ASSERT(scope.exception() || unwrapAlgorithmParameters);
764     if (!unwrapAlgorithmParameters)
765         return jsUndefined();
766
767     RefPtr<CryptoAlgorithm> unwrappedKeyAlgorithm;
768     RefPtr<CryptoAlgorithmParameters> unwrappedKeyAlgorithmParameters;
769     if (!state.uncheckedArgument(4).isNull()) {
770         unwrappedKeyAlgorithm = createAlgorithmFromJSValue(state, state.uncheckedArgument(4));
771         ASSERT(scope.exception() || unwrappedKeyAlgorithm);
772         if (!unwrappedKeyAlgorithm)
773             return jsUndefined();
774
775         unwrappedKeyAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(&state, unwrappedKeyAlgorithm->identifier(), state.uncheckedArgument(4));
776         ASSERT(scope.exception() || unwrappedKeyAlgorithmParameters);
777         if (!unwrappedKeyAlgorithmParameters)
778             return jsUndefined();
779     }
780
781     bool extractable = false;
782     if (state.argumentCount() >= 6) {
783         extractable = state.uncheckedArgument(5).toBoolean(&state);
784         if (UNLIKELY(scope.exception()))
785             return jsUndefined();
786     }
787
788     CryptoKeyUsage keyUsages = 0;
789     if (state.argumentCount() >= 7) {
790         auto success = cryptoKeyUsagesFromJSValue(state, state.argument(6), keyUsages);
791         ASSERT(scope.exception() || success);
792         if (!success)
793             return jsUndefined();
794     }
795
796     JSPromiseDeferred* promiseDeferred = JSPromiseDeferred::create(&state, globalObject());
797     RefPtr<DeferredWrapper> wrapper = DeferredWrapper::create(&state, globalObject(), promiseDeferred);
798     Strong<JSDOMGlobalObject> domGlobalObject(state.vm(), globalObject());
799
800     auto decryptSuccessCallback = [domGlobalObject, keyFormat, unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, wrapper](const Vector<uint8_t>& result) mutable {
801         auto importSuccessCallback = [wrapper](CryptoKey& key) mutable {
802             wrapper->resolve(key);
803         };
804         auto importFailureCallback = [wrapper]() mutable {
805             wrapper->reject(nullptr);
806         };
807
808         VM& vm = domGlobalObject->vm();
809         auto scope = DECLARE_CATCH_SCOPE(vm);
810
811         ExecState& state = *domGlobalObject->globalExec();
812         WebCore::importKey(state, keyFormat, std::make_pair(result.data(), result.size()), unwrappedKeyAlgorithm, unwrappedKeyAlgorithmParameters, extractable, keyUsages, WTFMove(importSuccessCallback), WTFMove(importFailureCallback));
813         if (UNLIKELY(scope.exception())) {
814             // FIXME: Report exception details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
815             scope.clearException();
816             wrapper->reject(nullptr);
817         }
818     };
819
820     auto decryptFailureCallback = [wrapper]() mutable {
821         wrapper->reject(nullptr);
822     };
823
824     ExceptionCode ec = 0;
825     unwrapAlgorithm->decryptForUnwrapKey(*unwrapAlgorithmParameters, *unwrappingKey, wrappedKeyData, WTFMove(decryptSuccessCallback), WTFMove(decryptFailureCallback), ec);
826     if (ec) {
827         setDOMException(&state, ec);
828         return jsUndefined();
829     }
830
831     return promiseDeferred->promise();
832 }
833
834 } // namespace WebCore
835
836 #endif