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