1c1d8e4b0d8b21d1f4a6d4261e33232f8b8f3203
[WebKit-https.git] / Source / WebCore / bindings / js / JSSubtleCryptoCustom.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JSSubtleCrypto.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmParameters.h"
33 #include "CryptoAlgorithmRegistry.h"
34 #include "CryptoKeyData.h"
35 #include "CryptoKeySerializationRaw.h"
36 #include "Document.h"
37 #include "ExceptionCode.h"
38 #include "JSCryptoAlgorithmDictionary.h"
39 #include "JSCryptoKeySerializationJWK.h"
40 #include "JSCryptoOperationData.h"
41 #include "JSDOMPromise.h"
42 #include <runtime/Error.h>
43
44 using namespace JSC;
45
46 namespace WebCore {
47
48 ENUM_CLASS(CryptoKeyFormat) {
49     // An unformatted sequence of bytes. Intended for secret keys.
50     Raw,
51
52     // The DER encoding of the PrivateKeyInfo structure from RFC 5208.
53     PKCS8,
54
55     // The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
56     SPKI,
57
58     // The key is represented as JSON according to the JSON Web Key format.
59     JWK
60 };
61
62 static std::unique_ptr<CryptoAlgorithm> createAlgorithmFromJSValue(ExecState* exec, JSValue value)
63 {
64     CryptoAlgorithmIdentifier algorithmIdentifier;
65     if (!JSCryptoAlgorithmDictionary::getAlgorithmIdentifier(exec, value, algorithmIdentifier)) {
66         ASSERT(exec->hadException());
67         return nullptr;
68     }
69
70     auto result = CryptoAlgorithmRegistry::shared().create(algorithmIdentifier);
71     if (!result)
72         setDOMException(exec, NOT_SUPPORTED_ERR);
73     return result;
74 }
75
76 static bool cryptoKeyFormatFromJSValue(ExecState* exec, JSValue value, CryptoKeyFormat& result)
77 {
78     String keyFormatString = value.toString(exec)->value(exec);
79     if (exec->hadException())
80         return false;
81     if (keyFormatString == "raw")
82         result = CryptoKeyFormat::Raw;
83     else if (keyFormatString == "pkcs8")
84         result = CryptoKeyFormat::PKCS8;
85     else if (keyFormatString == "spki")
86         result = CryptoKeyFormat::SPKI;
87     else if (keyFormatString == "jwk")
88         result = CryptoKeyFormat::JWK;
89     else {
90         throwTypeError(exec, "Unknown key format");
91         return false;
92     }
93     return true;
94 }
95
96 static bool cryptoKeyUsagesFromJSValue(ExecState* exec, JSValue value, CryptoKeyUsage& result)
97 {
98     if (!isJSArray(value)) {
99         throwTypeError(exec);
100         return false;
101     }
102
103     result = 0;
104
105     JSC::JSArray* array = asArray(value);
106     for (size_t i = 0; i < array->length(); ++i) {
107         JSC::JSValue element = array->getIndex(exec, i);
108         String usageString = element.toString(exec)->value(exec);
109         if (exec->hadException())
110             return false;
111         if (usageString == "encrypt")
112             result |= CryptoKeyUsageEncrypt;
113         else if (usageString == "decrypt")
114             result |= CryptoKeyUsageDecrypt;
115         else if (usageString == "sign")
116             result |= CryptoKeyUsageSign;
117         else if (usageString == "verify")
118             result |= CryptoKeyUsageVerify;
119         else if (usageString == "deriveKey")
120             result |= CryptoKeyUsageDeriveKey;
121         else if (usageString == "deriveBits")
122             result |= CryptoKeyUsageDeriveBits;
123         else if (usageString == "wrapKey")
124             result |= CryptoKeyUsageWrapKey;
125         else if (usageString == "unwrapKey")
126             result |= CryptoKeyUsageUnwrapKey;
127     }
128     return true;
129 }
130
131 JSValue JSSubtleCrypto::encrypt(ExecState* exec)
132 {
133     if (exec->argumentCount() < 3)
134         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
135
136     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
137     if (!algorithm) {
138         ASSERT(exec->hadException());
139         return jsUndefined();
140     }
141
142     auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(exec, algorithm->identifier(), exec->uncheckedArgument(0));
143     if (!parameters) {
144         ASSERT(exec->hadException());
145         return jsUndefined();
146     }
147
148     RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
149     if (!key)
150         return throwTypeError(exec);
151
152     if (!key->allows(CryptoKeyUsageEncrypt)) {
153         m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages does not include 'encrypt'");
154         setDOMException(exec, NOT_SUPPORTED_ERR);
155         return jsUndefined();
156     }
157
158     Vector<CryptoOperationData> data;
159     if (!sequenceOfCryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
160         ASSERT(exec->hadException());
161         return jsUndefined();
162     }
163
164     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
165     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
166
167     ExceptionCode ec = 0;
168     algorithm->encrypt(*parameters, *key, data, std::move(promiseWrapper), ec);
169     if (ec) {
170         setDOMException(exec, ec);
171         return jsUndefined();
172     }
173
174     return promise;
175 }
176
177 JSValue JSSubtleCrypto::decrypt(ExecState* exec)
178 {
179     if (exec->argumentCount() < 3)
180         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
181
182     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
183     if (!algorithm) {
184         ASSERT(exec->hadException());
185         return jsUndefined();
186     }
187
188     auto parameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(exec, algorithm->identifier(), exec->uncheckedArgument(0));
189     if (!parameters) {
190         ASSERT(exec->hadException());
191         return jsUndefined();
192     }
193
194     RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
195     if (!key)
196         return throwTypeError(exec);
197
198     if (!key->allows(CryptoKeyUsageDecrypt)) {
199         m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages does not include 'decrypt'");
200         setDOMException(exec, NOT_SUPPORTED_ERR);
201         return jsUndefined();
202     }
203
204     Vector<CryptoOperationData> data;
205     if (!sequenceOfCryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
206         ASSERT(exec->hadException());
207         return jsUndefined();
208     }
209
210     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
211     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
212
213     ExceptionCode ec = 0;
214     algorithm->decrypt(*parameters, *key, data, std::move(promiseWrapper), ec);
215     if (ec) {
216         setDOMException(exec, ec);
217         return jsUndefined();
218     }
219
220     return promise;
221 }
222
223 JSValue JSSubtleCrypto::sign(ExecState* exec)
224 {
225     if (exec->argumentCount() < 3)
226         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
227
228     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
229     if (!algorithm) {
230         ASSERT(exec->hadException());
231         return jsUndefined();
232     }
233
234     auto parameters = JSCryptoAlgorithmDictionary::createParametersForSign(exec, algorithm->identifier(), exec->uncheckedArgument(0));
235     if (!parameters) {
236         ASSERT(exec->hadException());
237         return jsUndefined();
238     }
239
240     RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
241     if (!key)
242         return throwTypeError(exec);
243
244     if (!key->allows(CryptoKeyUsageSign)) {
245         m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages does not include 'sign'");
246         setDOMException(exec, NOT_SUPPORTED_ERR);
247         return jsUndefined();
248     }
249
250     Vector<CryptoOperationData> data;
251     if (!sequenceOfCryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
252         ASSERT(exec->hadException());
253         return jsUndefined();
254     }
255
256     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
257     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
258
259     ExceptionCode ec = 0;
260     algorithm->sign(*parameters, *key, data, std::move(promiseWrapper), ec);
261     if (ec) {
262         setDOMException(exec, ec);
263         return jsUndefined();
264     }
265
266     return promise;
267 }
268
269 JSValue JSSubtleCrypto::verify(ExecState* exec)
270 {
271     if (exec->argumentCount() < 4)
272         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
273
274     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
275     if (!algorithm) {
276         ASSERT(exec->hadException());
277         return jsUndefined();
278     }
279
280     auto parameters = JSCryptoAlgorithmDictionary::createParametersForVerify(exec, algorithm->identifier(), exec->uncheckedArgument(0));
281     if (!parameters) {
282         ASSERT(exec->hadException());
283         return jsUndefined();
284     }
285
286     RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
287     if (!key)
288         return throwTypeError(exec);
289
290     if (!key->allows(CryptoKeyUsageVerify)) {
291         m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages does not include 'verify'");
292         setDOMException(exec, NOT_SUPPORTED_ERR);
293         return jsUndefined();
294     }
295
296     CryptoOperationData signature;
297     if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), signature)) {
298         ASSERT(exec->hadException());
299         return jsUndefined();
300     }
301
302     Vector<CryptoOperationData> data;
303     if (!sequenceOfCryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(3), data)) {
304         ASSERT(exec->hadException());
305         return jsUndefined();
306     }
307
308     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
309     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
310
311     ExceptionCode ec = 0;
312     algorithm->verify(*parameters, *key, signature, data, std::move(promiseWrapper), ec);
313     if (ec) {
314         setDOMException(exec, ec);
315         return jsUndefined();
316     }
317
318     return promise;
319 }
320
321 JSValue JSSubtleCrypto::digest(ExecState* exec)
322 {
323     if (exec->argumentCount() < 2)
324         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
325
326     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
327     if (!algorithm) {
328         ASSERT(exec->hadException());
329         return jsUndefined();
330     }
331
332     auto parameters = JSCryptoAlgorithmDictionary::createParametersForDigest(exec, algorithm->identifier(), exec->uncheckedArgument(0));
333     if (!parameters) {
334         ASSERT(exec->hadException());
335         return jsUndefined();
336     }
337
338     Vector<CryptoOperationData> data;
339     if (!sequenceOfCryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), data)) {
340         ASSERT(exec->hadException());
341         return jsUndefined();
342     }
343
344     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
345     std::unique_ptr<PromiseWrapper> promiseWrapper = PromiseWrapper::create(globalObject(), promise);
346
347     ExceptionCode ec = 0;
348     algorithm->digest(*parameters, data, std::move(promiseWrapper), ec);
349     if (ec) {
350         setDOMException(exec, ec);
351         return jsUndefined();
352     }
353
354     return promise;
355 }
356
357 JSValue JSSubtleCrypto::generateKey(JSC::ExecState* exec)
358 {
359     if (exec->argumentCount() < 1)
360         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
361
362     auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
363     if (!algorithm) {
364         ASSERT(exec->hadException());
365         return jsUndefined();
366     }
367
368     auto parameters = JSCryptoAlgorithmDictionary::createParametersForGenerateKey(exec, algorithm->identifier(), exec->uncheckedArgument(0));
369     if (!parameters) {
370         ASSERT(exec->hadException());
371         return jsUndefined();
372     }
373
374     bool extractable = false;
375     if (exec->argumentCount() >= 2) {
376         extractable = exec->uncheckedArgument(1).toBoolean(exec);
377         if (exec->hadException())
378             return jsUndefined();
379     }
380
381     CryptoKeyUsage keyUsages = 0;
382     if (exec->argumentCount() >= 3) {
383         if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(2), keyUsages)) {
384             ASSERT(exec->hadException());
385             return jsUndefined();
386         }
387     }
388
389     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
390     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
391
392     ExceptionCode ec = 0;
393     algorithm->generateKey(*parameters, extractable, keyUsages, std::move(promiseWrapper), ec);
394     if (ec) {
395         setDOMException(exec, ec);
396         return jsUndefined();
397     }
398
399     return promise;
400 }
401
402 JSValue JSSubtleCrypto::importKey(JSC::ExecState* exec)
403 {
404     if (exec->argumentCount() < 3)
405         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
406
407     CryptoKeyFormat keyFormat;
408     if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
409         ASSERT(exec->hadException());
410         return jsUndefined();
411     }
412
413     CryptoOperationData data;
414     if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), data)) {
415         ASSERT(exec->hadException());
416         return jsUndefined();
417     }
418
419     std::unique_ptr<CryptoAlgorithm> algorithm;
420     std::unique_ptr<CryptoAlgorithmParameters> parameters;
421     if (!exec->uncheckedArgument(2).isNull()) {
422         algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(2));
423         if (!algorithm) {
424             ASSERT(exec->hadException());
425             return jsUndefined();
426         }
427         parameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(exec, algorithm->identifier(), exec->uncheckedArgument(2));
428         if (!parameters) {
429             ASSERT(exec->hadException());
430             return jsUndefined();
431         }
432     }
433
434     std::unique_ptr<CryptoKeySerialization> keySerialization;
435     switch (keyFormat) {
436     case CryptoKeyFormat::Raw:
437         keySerialization = CryptoKeySerializationRaw::create(data);
438         break;
439     case CryptoKeyFormat::JWK: {
440         String jwkString = String::fromUTF8(data.first, data.second);
441         if (jwkString.isNull()) {
442             throwTypeError(exec, "JWK JSON serialization is not valid UTF-8");
443             return jsUndefined();
444         }
445         keySerialization = JSCryptoKeySerializationJWK::create(exec, jwkString);
446         if (exec->hadException())
447             return jsUndefined();
448         break;
449     }
450     default:
451         throwTypeError(exec, "Unsupported key format for import");
452         return jsUndefined();
453     }
454
455     ASSERT(keySerialization);
456
457     if (!keySerialization->reconcileAlgorithm(algorithm, parameters)) {
458         if (!exec->hadException())
459             throwTypeError(exec, "Algorithm specified in key is not compatible with one passed to importKey as argument");
460         return jsUndefined();
461     }
462     if (exec->hadException())
463         return jsUndefined();
464
465     if (!algorithm) {
466         throwTypeError(exec, "Neither key nor function argument has crypto algorithm specified");
467         return jsUndefined();
468     }
469     ASSERT(parameters);
470
471     bool extractable = false;
472     if (exec->argumentCount() >= 4) {
473         extractable = exec->uncheckedArgument(3).toBoolean(exec);
474         if (exec->hadException())
475             return jsUndefined();
476     }
477     keySerialization->reconcileExtractable(extractable);
478     if (exec->hadException())
479         return jsUndefined();
480
481     CryptoKeyUsage keyUsages = 0;
482     if (exec->argumentCount() >= 5) {
483         if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(4), keyUsages)) {
484             ASSERT(exec->hadException());
485             return jsUndefined();
486         }
487     }
488     keySerialization->reconcileUsages(keyUsages);
489     if (exec->hadException())
490         return jsUndefined();
491
492     auto keyData = keySerialization->keyData();
493     if (exec->hadException())
494         return jsUndefined();
495
496     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
497     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
498
499     ExceptionCode ec = 0;
500     algorithm->importKey(*parameters, *keyData, extractable, keyUsages, std::move(promiseWrapper), ec);
501     if (ec) {
502         setDOMException(exec, ec);
503         return jsUndefined();
504     }
505
506     return promise;
507 }
508
509 JSValue JSSubtleCrypto::exportKey(JSC::ExecState* exec)
510 {
511     if (exec->argumentCount() < 2)
512         return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
513
514     CryptoKeyFormat keyFormat;
515     if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
516         ASSERT(exec->hadException());
517         return jsUndefined();
518     }
519
520     RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
521     if (!key)
522         return throwTypeError(exec);
523
524     JSPromise* promise = JSPromise::createWithResolver(exec->vm(), globalObject());
525     auto promiseWrapper = PromiseWrapper::create(globalObject(), promise);
526
527     if (!key->extractable()) {
528         m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key is not extractable");
529         promiseWrapper->reject(nullptr);
530         return promise;
531     }
532
533     switch (keyFormat) {
534     case CryptoKeyFormat::Raw: {
535         Vector<unsigned char> result;
536         if (CryptoKeySerializationRaw::serialize(*key, result))
537             promiseWrapper->fulfill(result);
538         else {
539             m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key cannot be exported to raw format");
540             promiseWrapper->reject(nullptr);
541         }
542         break;
543     }
544     case CryptoKeyFormat::JWK: {
545         String result = JSCryptoKeySerializationJWK::serialize(exec, *key);
546         if (exec->hadException())
547             return jsUndefined();
548         CString utf8String = result.utf8(StrictConversion);
549         Vector<unsigned char> resultBuffer;
550         resultBuffer.append(utf8String.data(), utf8String.length());
551         promiseWrapper->fulfill(result);
552         break;
553     }
554     default:
555         throwTypeError(exec, "Unsupported key format for export");
556         return jsUndefined();
557     }
558
559     return promise;
560 }
561
562 } // namespace WebCore
563
564 #endif