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