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