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