1 | /* |
2 | * Copyright (C) 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 "SubtleCrypto.h" |
28 | |
29 | #if ENABLE(WEB_CRYPTO) |
30 | |
31 | #include "CryptoAlgorithm.h" |
32 | #include "CryptoAlgorithmRegistry.h" |
33 | #include "JSAesCbcCfbParams.h" |
34 | #include "JSAesCtrParams.h" |
35 | #include "JSAesGcmParams.h" |
36 | #include "JSAesKeyParams.h" |
37 | #include "JSCryptoAlgorithmParameters.h" |
38 | #include "JSCryptoKey.h" |
39 | #include "JSCryptoKeyPair.h" |
40 | #include "JSDOMPromiseDeferred.h" |
41 | #include "JSDOMWrapper.h" |
42 | #include "JSEcKeyParams.h" |
43 | #include "JSEcdhKeyDeriveParams.h" |
44 | #include "JSEcdsaParams.h" |
45 | #include "JSHkdfParams.h" |
46 | #include "JSHmacKeyParams.h" |
47 | #include "JSJsonWebKey.h" |
48 | #include "JSPbkdf2Params.h" |
49 | #include "JSRsaHashedImportParams.h" |
50 | #include "JSRsaHashedKeyGenParams.h" |
51 | #include "JSRsaKeyGenParams.h" |
52 | #include "JSRsaOaepParams.h" |
53 | #include "JSRsaPssParams.h" |
54 | #include <JavaScriptCore/JSONObject.h> |
55 | |
56 | namespace WebCore { |
57 | using namespace JSC; |
58 | |
59 | SubtleCrypto::SubtleCrypto(ScriptExecutionContext* context) |
60 | : ContextDestructionObserver(context) |
61 | , m_workQueue(WorkQueue::create("com.apple.WebKit.CryptoQueue" )) |
62 | { |
63 | } |
64 | |
65 | SubtleCrypto::~SubtleCrypto() = default; |
66 | |
67 | enum class Operations { |
68 | Encrypt, |
69 | Decrypt, |
70 | Sign, |
71 | Verify, |
72 | Digest, |
73 | GenerateKey, |
74 | DeriveBits, |
75 | ImportKey, |
76 | WrapKey, |
77 | UnwrapKey, |
78 | GetKeyLength |
79 | }; |
80 | |
81 | static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(ExecState&, WebCore::SubtleCrypto::AlgorithmIdentifier, Operations); |
82 | |
83 | static ExceptionOr<CryptoAlgorithmIdentifier> toHashIdentifier(ExecState& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier) |
84 | { |
85 | auto digestParams = normalizeCryptoAlgorithmParameters(state, algorithmIdentifier, Operations::Digest); |
86 | if (digestParams.hasException()) |
87 | return digestParams.releaseException(); |
88 | return digestParams.returnValue()->identifier; |
89 | } |
90 | |
91 | static ExceptionOr<std::unique_ptr<CryptoAlgorithmParameters>> normalizeCryptoAlgorithmParameters(ExecState& state, SubtleCrypto::AlgorithmIdentifier algorithmIdentifier, Operations operation) |
92 | { |
93 | VM& vm = state.vm(); |
94 | auto scope = DECLARE_THROW_SCOPE(vm); |
95 | |
96 | if (WTF::holds_alternative<String>(algorithmIdentifier)) { |
97 | auto newParams = Strong<JSObject>(vm, constructEmptyObject(&state)); |
98 | newParams->putDirect(vm, Identifier::fromString(&vm, "name" ), jsString(&state, WTF::get<String>(algorithmIdentifier))); |
99 | |
100 | return normalizeCryptoAlgorithmParameters(state, newParams, operation); |
101 | } |
102 | |
103 | auto& value = WTF::get<JSC::Strong<JSC::JSObject>>(algorithmIdentifier); |
104 | |
105 | auto params = convertDictionary<CryptoAlgorithmParameters>(state, value.get()); |
106 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
107 | |
108 | auto identifier = CryptoAlgorithmRegistry::singleton().identifier(params.name); |
109 | if (UNLIKELY(!identifier)) |
110 | return Exception { NotSupportedError }; |
111 | |
112 | std::unique_ptr<CryptoAlgorithmParameters> result; |
113 | switch (operation) { |
114 | case Operations::Encrypt: |
115 | case Operations::Decrypt: |
116 | switch (*identifier) { |
117 | case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: |
118 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
119 | break; |
120 | case CryptoAlgorithmIdentifier::RSA_OAEP: { |
121 | auto params = convertDictionary<CryptoAlgorithmRsaOaepParams>(state, value.get()); |
122 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
123 | result = std::make_unique<CryptoAlgorithmRsaOaepParams>(params); |
124 | break; |
125 | } |
126 | case CryptoAlgorithmIdentifier::AES_CBC: |
127 | case CryptoAlgorithmIdentifier::AES_CFB: { |
128 | auto params = convertDictionary<CryptoAlgorithmAesCbcCfbParams>(state, value.get()); |
129 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
130 | result = std::make_unique<CryptoAlgorithmAesCbcCfbParams>(params); |
131 | break; |
132 | } |
133 | case CryptoAlgorithmIdentifier::AES_CTR: { |
134 | auto params = convertDictionary<CryptoAlgorithmAesCtrParams>(state, value.get()); |
135 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
136 | result = std::make_unique<CryptoAlgorithmAesCtrParams>(params); |
137 | break; |
138 | } |
139 | case CryptoAlgorithmIdentifier::AES_GCM: { |
140 | auto params = convertDictionary<CryptoAlgorithmAesGcmParams>(state, value.get()); |
141 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
142 | result = std::make_unique<CryptoAlgorithmAesGcmParams>(params); |
143 | break; |
144 | } |
145 | default: |
146 | return Exception { NotSupportedError }; |
147 | } |
148 | break; |
149 | case Operations::Sign: |
150 | case Operations::Verify: |
151 | switch (*identifier) { |
152 | case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: |
153 | case CryptoAlgorithmIdentifier::HMAC: |
154 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
155 | break; |
156 | case CryptoAlgorithmIdentifier::ECDSA: { |
157 | auto params = convertDictionary<CryptoAlgorithmEcdsaParams>(state, value.get()); |
158 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
159 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
160 | if (hashIdentifier.hasException()) |
161 | return hashIdentifier.releaseException(); |
162 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
163 | result = std::make_unique<CryptoAlgorithmEcdsaParams>(params); |
164 | break; |
165 | } |
166 | case CryptoAlgorithmIdentifier::RSA_PSS: { |
167 | auto params = convertDictionary<CryptoAlgorithmRsaPssParams>(state, value.get()); |
168 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
169 | result = std::make_unique<CryptoAlgorithmRsaPssParams>(params); |
170 | break; |
171 | } |
172 | default: |
173 | return Exception { NotSupportedError }; |
174 | } |
175 | break; |
176 | case Operations::Digest: |
177 | switch (*identifier) { |
178 | case CryptoAlgorithmIdentifier::SHA_1: |
179 | case CryptoAlgorithmIdentifier::SHA_224: |
180 | case CryptoAlgorithmIdentifier::SHA_256: |
181 | case CryptoAlgorithmIdentifier::SHA_384: |
182 | case CryptoAlgorithmIdentifier::SHA_512: |
183 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
184 | break; |
185 | default: |
186 | return Exception { NotSupportedError }; |
187 | } |
188 | break; |
189 | case Operations::GenerateKey: |
190 | switch (*identifier) { |
191 | case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: { |
192 | auto params = convertDictionary<CryptoAlgorithmRsaKeyGenParams>(state, value.get()); |
193 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
194 | result = std::make_unique<CryptoAlgorithmRsaKeyGenParams>(params); |
195 | break; |
196 | } |
197 | case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: |
198 | case CryptoAlgorithmIdentifier::RSA_PSS: |
199 | case CryptoAlgorithmIdentifier::RSA_OAEP: { |
200 | auto params = convertDictionary<CryptoAlgorithmRsaHashedKeyGenParams>(state, value.get()); |
201 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
202 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
203 | if (hashIdentifier.hasException()) |
204 | return hashIdentifier.releaseException(); |
205 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
206 | result = std::make_unique<CryptoAlgorithmRsaHashedKeyGenParams>(params); |
207 | break; |
208 | } |
209 | case CryptoAlgorithmIdentifier::AES_CTR: |
210 | case CryptoAlgorithmIdentifier::AES_CBC: |
211 | case CryptoAlgorithmIdentifier::AES_GCM: |
212 | case CryptoAlgorithmIdentifier::AES_CFB: |
213 | case CryptoAlgorithmIdentifier::AES_KW: { |
214 | auto params = convertDictionary<CryptoAlgorithmAesKeyParams>(state, value.get()); |
215 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
216 | result = std::make_unique<CryptoAlgorithmAesKeyParams>(params); |
217 | break; |
218 | } |
219 | case CryptoAlgorithmIdentifier::HMAC: { |
220 | auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get()); |
221 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
222 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
223 | if (hashIdentifier.hasException()) |
224 | return hashIdentifier.releaseException(); |
225 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
226 | result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params); |
227 | break; |
228 | } |
229 | case CryptoAlgorithmIdentifier::ECDSA: |
230 | case CryptoAlgorithmIdentifier::ECDH: { |
231 | auto params = convertDictionary<CryptoAlgorithmEcKeyParams>(state, value.get()); |
232 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
233 | result = std::make_unique<CryptoAlgorithmEcKeyParams>(params); |
234 | break; |
235 | } |
236 | default: |
237 | return Exception { NotSupportedError }; |
238 | } |
239 | break; |
240 | case Operations::DeriveBits: |
241 | switch (*identifier) { |
242 | case CryptoAlgorithmIdentifier::ECDH: { |
243 | // Remove this hack once https://bugs.webkit.org/show_bug.cgi?id=169333 is fixed. |
244 | JSValue nameValue = value.get()->get(&state, Identifier::fromString(&state, "name" )); |
245 | JSValue publicValue = value.get()->get(&state, Identifier::fromString(&state, "public" )); |
246 | JSObject* newValue = constructEmptyObject(&state); |
247 | newValue->putDirect(vm, Identifier::fromString(&vm, "name" ), nameValue); |
248 | newValue->putDirect(vm, Identifier::fromString(&vm, "publicKey" ), publicValue); |
249 | |
250 | auto params = convertDictionary<CryptoAlgorithmEcdhKeyDeriveParams>(state, newValue); |
251 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
252 | result = std::make_unique<CryptoAlgorithmEcdhKeyDeriveParams>(params); |
253 | break; |
254 | } |
255 | case CryptoAlgorithmIdentifier::HKDF: { |
256 | auto params = convertDictionary<CryptoAlgorithmHkdfParams>(state, value.get()); |
257 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
258 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
259 | if (hashIdentifier.hasException()) |
260 | return hashIdentifier.releaseException(); |
261 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
262 | result = std::make_unique<CryptoAlgorithmHkdfParams>(params); |
263 | break; |
264 | } |
265 | case CryptoAlgorithmIdentifier::PBKDF2: { |
266 | auto params = convertDictionary<CryptoAlgorithmPbkdf2Params>(state, value.get()); |
267 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
268 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
269 | if (hashIdentifier.hasException()) |
270 | return hashIdentifier.releaseException(); |
271 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
272 | result = std::make_unique<CryptoAlgorithmPbkdf2Params>(params); |
273 | break; |
274 | } |
275 | default: |
276 | return Exception { NotSupportedError }; |
277 | } |
278 | break; |
279 | case Operations::ImportKey: |
280 | switch (*identifier) { |
281 | case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: |
282 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
283 | break; |
284 | case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: |
285 | case CryptoAlgorithmIdentifier::RSA_PSS: |
286 | case CryptoAlgorithmIdentifier::RSA_OAEP: { |
287 | auto params = convertDictionary<CryptoAlgorithmRsaHashedImportParams>(state, value.get()); |
288 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
289 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
290 | if (hashIdentifier.hasException()) |
291 | return hashIdentifier.releaseException(); |
292 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
293 | result = std::make_unique<CryptoAlgorithmRsaHashedImportParams>(params); |
294 | break; |
295 | } |
296 | case CryptoAlgorithmIdentifier::AES_CTR: |
297 | case CryptoAlgorithmIdentifier::AES_CBC: |
298 | case CryptoAlgorithmIdentifier::AES_GCM: |
299 | case CryptoAlgorithmIdentifier::AES_CFB: |
300 | case CryptoAlgorithmIdentifier::AES_KW: |
301 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
302 | break; |
303 | case CryptoAlgorithmIdentifier::HMAC: { |
304 | auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get()); |
305 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
306 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
307 | if (hashIdentifier.hasException()) |
308 | return hashIdentifier.releaseException(); |
309 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
310 | result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params); |
311 | break; |
312 | } |
313 | case CryptoAlgorithmIdentifier::ECDSA: |
314 | case CryptoAlgorithmIdentifier::ECDH: { |
315 | auto params = convertDictionary<CryptoAlgorithmEcKeyParams>(state, value.get()); |
316 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
317 | result = std::make_unique<CryptoAlgorithmEcKeyParams>(params); |
318 | break; |
319 | } |
320 | case CryptoAlgorithmIdentifier::HKDF: |
321 | case CryptoAlgorithmIdentifier::PBKDF2: |
322 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
323 | break; |
324 | default: |
325 | return Exception { NotSupportedError }; |
326 | } |
327 | break; |
328 | case Operations::WrapKey: |
329 | case Operations::UnwrapKey: |
330 | switch (*identifier) { |
331 | case CryptoAlgorithmIdentifier::AES_KW: |
332 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
333 | break; |
334 | default: |
335 | return Exception { NotSupportedError }; |
336 | } |
337 | break; |
338 | case Operations::GetKeyLength: |
339 | switch (*identifier) { |
340 | case CryptoAlgorithmIdentifier::AES_CTR: |
341 | case CryptoAlgorithmIdentifier::AES_CBC: |
342 | case CryptoAlgorithmIdentifier::AES_GCM: |
343 | case CryptoAlgorithmIdentifier::AES_CFB: |
344 | case CryptoAlgorithmIdentifier::AES_KW: { |
345 | auto params = convertDictionary<CryptoAlgorithmAesKeyParams>(state, value.get()); |
346 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
347 | result = std::make_unique<CryptoAlgorithmAesKeyParams>(params); |
348 | break; |
349 | } |
350 | case CryptoAlgorithmIdentifier::HMAC: { |
351 | auto params = convertDictionary<CryptoAlgorithmHmacKeyParams>(state, value.get()); |
352 | RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError }); |
353 | auto hashIdentifier = toHashIdentifier(state, params.hash); |
354 | if (hashIdentifier.hasException()) |
355 | return hashIdentifier.releaseException(); |
356 | params.hashIdentifier = hashIdentifier.releaseReturnValue(); |
357 | result = std::make_unique<CryptoAlgorithmHmacKeyParams>(params); |
358 | break; |
359 | } |
360 | case CryptoAlgorithmIdentifier::HKDF: |
361 | case CryptoAlgorithmIdentifier::PBKDF2: |
362 | result = std::make_unique<CryptoAlgorithmParameters>(params); |
363 | break; |
364 | default: |
365 | return Exception { NotSupportedError }; |
366 | } |
367 | break; |
368 | } |
369 | |
370 | result->identifier = *identifier; |
371 | return result; |
372 | } |
373 | |
374 | static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(CryptoKeyUsage usage) |
375 | { |
376 | switch (usage) { |
377 | case CryptoKeyUsage::Encrypt: |
378 | return CryptoKeyUsageEncrypt; |
379 | case CryptoKeyUsage::Decrypt: |
380 | return CryptoKeyUsageDecrypt; |
381 | case CryptoKeyUsage::Sign: |
382 | return CryptoKeyUsageSign; |
383 | case CryptoKeyUsage::Verify: |
384 | return CryptoKeyUsageVerify; |
385 | case CryptoKeyUsage::DeriveKey: |
386 | return CryptoKeyUsageDeriveKey; |
387 | case CryptoKeyUsage::DeriveBits: |
388 | return CryptoKeyUsageDeriveBits; |
389 | case CryptoKeyUsage::WrapKey: |
390 | return CryptoKeyUsageWrapKey; |
391 | case CryptoKeyUsage::UnwrapKey: |
392 | return CryptoKeyUsageUnwrapKey; |
393 | } |
394 | |
395 | RELEASE_ASSERT_NOT_REACHED(); |
396 | } |
397 | |
398 | static CryptoKeyUsageBitmap toCryptoKeyUsageBitmap(const Vector<CryptoKeyUsage>& usages) |
399 | { |
400 | CryptoKeyUsageBitmap result = 0; |
401 | // Maybe we shouldn't silently bypass duplicated usages? |
402 | for (auto usage : usages) |
403 | result |= toCryptoKeyUsageBitmap(usage); |
404 | |
405 | return result; |
406 | } |
407 | |
408 | // Maybe we want more specific error messages? |
409 | static void rejectWithException(Ref<DeferredPromise>&& passedPromise, ExceptionCode ec) |
410 | { |
411 | switch (ec) { |
412 | case NotSupportedError: |
413 | passedPromise->reject(ec, "The algorithm is not supported"_s ); |
414 | return; |
415 | case SyntaxError: |
416 | passedPromise->reject(ec, "A required parameter was missing or out-of-range"_s ); |
417 | return; |
418 | case InvalidStateError: |
419 | passedPromise->reject(ec, "The requested operation is not valid for the current state of the provided key"_s ); |
420 | return; |
421 | case InvalidAccessError: |
422 | passedPromise->reject(ec, "The requested operation is not valid for the provided key"_s ); |
423 | return; |
424 | case UnknownError: |
425 | passedPromise->reject(ec, "The operation failed for an unknown transient reason (e.g. out of memory)"_s ); |
426 | return; |
427 | case DataError: |
428 | passedPromise->reject(ec, "Data provided to an operation does not meet requirements"_s ); |
429 | return; |
430 | case OperationError: |
431 | passedPromise->reject(ec, "The operation failed for an operation-specific reason"_s ); |
432 | return; |
433 | default: |
434 | break; |
435 | } |
436 | ASSERT_NOT_REACHED(); |
437 | } |
438 | |
439 | static void normalizeJsonWebKey(JsonWebKey& webKey) |
440 | { |
441 | // Maybe we shouldn't silently bypass duplicated usages? |
442 | webKey.usages = webKey.key_ops ? toCryptoKeyUsageBitmap(webKey.key_ops.value()) : 0; |
443 | } |
444 | |
445 | // FIXME: This returns an Optional<KeyData> and takes a promise, rather than returning an |
446 | // ExceptionOr<KeyData> and letting the caller handle the promise, to work around an issue where |
447 | // Variant types (which KeyData is) in ExceptionOr<> cause compile issues on some platforms. This |
448 | // should be resolved by adopting a standards compliant std::variant (see https://webkit.org/b/175583) |
449 | static Optional<KeyData> toKeyData(SubtleCrypto::KeyFormat format, SubtleCrypto::KeyDataVariant&& keyDataVariant, Ref<DeferredPromise>& promise) |
450 | { |
451 | switch (format) { |
452 | case SubtleCrypto::KeyFormat::Spki: |
453 | case SubtleCrypto::KeyFormat::Pkcs8: |
454 | case SubtleCrypto::KeyFormat::Raw: |
455 | return WTF::switchOn(keyDataVariant, |
456 | [&promise] (JsonWebKey&) -> Optional<KeyData> { |
457 | promise->reject(Exception { TypeError }); |
458 | return WTF::nullopt; |
459 | }, |
460 | [] (auto& bufferSource) -> Optional<KeyData> { |
461 | Vector<uint8_t> result; |
462 | result.append(static_cast<const uint8_t*>(bufferSource->data()), bufferSource->byteLength()); |
463 | return KeyData { result }; |
464 | } |
465 | ); |
466 | case SubtleCrypto::KeyFormat::Jwk: |
467 | return WTF::switchOn(keyDataVariant, |
468 | [] (JsonWebKey& webKey) -> Optional<KeyData> { |
469 | normalizeJsonWebKey(webKey); |
470 | return KeyData { webKey }; |
471 | }, |
472 | [&promise] (auto&) -> Optional<KeyData> { |
473 | promise->reject(Exception { TypeError }); |
474 | return WTF::nullopt; |
475 | } |
476 | ); |
477 | } |
478 | |
479 | RELEASE_ASSERT_NOT_REACHED(); |
480 | } |
481 | |
482 | static Vector<uint8_t> copyToVector(BufferSource&& data) |
483 | { |
484 | Vector<uint8_t> dataVector; |
485 | dataVector.append(data.data(), data.length()); |
486 | return dataVector; |
487 | } |
488 | |
489 | static bool isSupportedExportKey(CryptoAlgorithmIdentifier identifier) |
490 | { |
491 | switch (identifier) { |
492 | case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: |
493 | case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: |
494 | case CryptoAlgorithmIdentifier::RSA_PSS: |
495 | case CryptoAlgorithmIdentifier::RSA_OAEP: |
496 | case CryptoAlgorithmIdentifier::AES_CTR: |
497 | case CryptoAlgorithmIdentifier::AES_CBC: |
498 | case CryptoAlgorithmIdentifier::AES_GCM: |
499 | case CryptoAlgorithmIdentifier::AES_CFB: |
500 | case CryptoAlgorithmIdentifier::AES_KW: |
501 | case CryptoAlgorithmIdentifier::HMAC: |
502 | case CryptoAlgorithmIdentifier::ECDSA: |
503 | case CryptoAlgorithmIdentifier::ECDH: |
504 | return true; |
505 | default: |
506 | return false; |
507 | } |
508 | } |
509 | |
510 | RefPtr<DeferredPromise> getPromise(DeferredPromise* index, WeakPtr<SubtleCrypto> subtleCryptoWeakPointer) |
511 | { |
512 | if (subtleCryptoWeakPointer) { |
513 | if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.take(index)) |
514 | return WTFMove(promise.value()); |
515 | } |
516 | return nullptr; |
517 | } |
518 | |
519 | static std::unique_ptr<CryptoAlgorithmParameters> crossThreadCopyImportParams(const CryptoAlgorithmParameters& importParams) |
520 | { |
521 | switch (importParams.parametersClass()) { |
522 | case CryptoAlgorithmParameters::Class::None: { |
523 | auto result = std::make_unique<CryptoAlgorithmParameters>(); |
524 | result->identifier = importParams.identifier; |
525 | return result; |
526 | } |
527 | case CryptoAlgorithmParameters::Class::EcKeyParams: |
528 | return std::make_unique<CryptoAlgorithmEcKeyParams>(crossThreadCopy(downcast<CryptoAlgorithmEcKeyParams>(importParams))); |
529 | case CryptoAlgorithmParameters::Class::HmacKeyParams: |
530 | return std::make_unique<CryptoAlgorithmHmacKeyParams>(crossThreadCopy(downcast<CryptoAlgorithmHmacKeyParams>(importParams))); |
531 | case CryptoAlgorithmParameters::Class::RsaHashedImportParams: |
532 | return std::make_unique<CryptoAlgorithmRsaHashedImportParams>(crossThreadCopy(downcast<CryptoAlgorithmRsaHashedImportParams>(importParams))); |
533 | default: |
534 | ASSERT_NOT_REACHED(); |
535 | return nullptr; |
536 | } |
537 | } |
538 | |
539 | // MARK: - Exposed functions. |
540 | |
541 | void SubtleCrypto::encrypt(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise) |
542 | { |
543 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Encrypt); |
544 | if (paramsOrException.hasException()) { |
545 | promise->reject(paramsOrException.releaseException()); |
546 | return; |
547 | } |
548 | auto params = paramsOrException.releaseReturnValue(); |
549 | |
550 | auto data = copyToVector(WTFMove(dataBufferSource)); |
551 | |
552 | if (params->identifier != key.algorithmIdentifier()) { |
553 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
554 | return; |
555 | } |
556 | |
557 | if (!key.allows(CryptoKeyUsageEncrypt)) { |
558 | promise->reject(InvalidAccessError, "CryptoKey doesn't support encryption"_s ); |
559 | return; |
560 | } |
561 | |
562 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
563 | |
564 | auto index = promise.ptr(); |
565 | m_pendingPromises.add(index, WTFMove(promise)); |
566 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
567 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& cipherText) mutable { |
568 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
569 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), cipherText.data(), cipherText.size()); |
570 | }; |
571 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
572 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
573 | rejectWithException(promise.releaseNonNull(), ec); |
574 | }; |
575 | |
576 | algorithm->encrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
577 | } |
578 | |
579 | void SubtleCrypto::decrypt(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise) |
580 | { |
581 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Decrypt); |
582 | if (paramsOrException.hasException()) { |
583 | promise->reject(paramsOrException.releaseException()); |
584 | return; |
585 | } |
586 | auto params = paramsOrException.releaseReturnValue(); |
587 | |
588 | auto data = copyToVector(WTFMove(dataBufferSource)); |
589 | |
590 | if (params->identifier != key.algorithmIdentifier()) { |
591 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
592 | return; |
593 | } |
594 | |
595 | if (!key.allows(CryptoKeyUsageDecrypt)) { |
596 | promise->reject(InvalidAccessError, "CryptoKey doesn't support decryption"_s ); |
597 | return; |
598 | } |
599 | |
600 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
601 | |
602 | auto index = promise.ptr(); |
603 | m_pendingPromises.add(index, WTFMove(promise)); |
604 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
605 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& plainText) mutable { |
606 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
607 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), plainText.data(), plainText.size()); |
608 | }; |
609 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
610 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
611 | rejectWithException(promise.releaseNonNull(), ec); |
612 | }; |
613 | |
614 | algorithm->decrypt(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
615 | } |
616 | |
617 | void SubtleCrypto::sign(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise) |
618 | { |
619 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Sign); |
620 | if (paramsOrException.hasException()) { |
621 | promise->reject(paramsOrException.releaseException()); |
622 | return; |
623 | } |
624 | auto params = paramsOrException.releaseReturnValue(); |
625 | |
626 | auto data = copyToVector(WTFMove(dataBufferSource)); |
627 | |
628 | if (params->identifier != key.algorithmIdentifier()) { |
629 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
630 | return; |
631 | } |
632 | |
633 | if (!key.allows(CryptoKeyUsageSign)) { |
634 | promise->reject(InvalidAccessError, "CryptoKey doesn't support signing"_s ); |
635 | return; |
636 | } |
637 | |
638 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
639 | |
640 | auto index = promise.ptr(); |
641 | m_pendingPromises.add(index, WTFMove(promise)); |
642 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
643 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& signature) mutable { |
644 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
645 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), signature.data(), signature.size()); |
646 | }; |
647 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
648 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
649 | rejectWithException(promise.releaseNonNull(), ec); |
650 | }; |
651 | |
652 | algorithm->sign(*params, key, WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
653 | } |
654 | |
655 | void SubtleCrypto::verify(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& key, BufferSource&& signatureBufferSource, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise) |
656 | { |
657 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Verify); |
658 | if (paramsOrException.hasException()) { |
659 | promise->reject(paramsOrException.releaseException()); |
660 | return; |
661 | } |
662 | auto params = paramsOrException.releaseReturnValue(); |
663 | |
664 | auto signature = copyToVector(WTFMove(signatureBufferSource)); |
665 | auto data = copyToVector(WTFMove(dataBufferSource)); |
666 | |
667 | if (params->identifier != key.algorithmIdentifier()) { |
668 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
669 | return; |
670 | } |
671 | |
672 | if (!key.allows(CryptoKeyUsageVerify)) { |
673 | promise->reject(InvalidAccessError, "CryptoKey doesn't support verification"_s ); |
674 | return; |
675 | } |
676 | |
677 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
678 | |
679 | auto index = promise.ptr(); |
680 | m_pendingPromises.add(index, WTFMove(promise)); |
681 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
682 | auto callback = [index, subtleCryptoWeakPointer](bool result) mutable { |
683 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
684 | promise->resolve<IDLBoolean>(result); |
685 | }; |
686 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
687 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
688 | rejectWithException(promise.releaseNonNull(), ec); |
689 | }; |
690 | |
691 | algorithm->verify(*params, key, WTFMove(signature), WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
692 | } |
693 | |
694 | void SubtleCrypto::digest(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, BufferSource&& dataBufferSource, Ref<DeferredPromise>&& promise) |
695 | { |
696 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::Digest); |
697 | if (paramsOrException.hasException()) { |
698 | promise->reject(paramsOrException.releaseException()); |
699 | return; |
700 | } |
701 | auto params = paramsOrException.releaseReturnValue(); |
702 | |
703 | auto data = copyToVector(WTFMove(dataBufferSource)); |
704 | |
705 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); |
706 | |
707 | auto index = promise.ptr(); |
708 | m_pendingPromises.add(index, WTFMove(promise)); |
709 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
710 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& digest) mutable { |
711 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
712 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), digest.data(), digest.size()); |
713 | }; |
714 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
715 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
716 | rejectWithException(promise.releaseNonNull(), ec); |
717 | }; |
718 | |
719 | algorithm->digest(WTFMove(data), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
720 | } |
721 | |
722 | void SubtleCrypto::generateKey(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, bool , Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise) |
723 | { |
724 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::GenerateKey); |
725 | if (paramsOrException.hasException()) { |
726 | promise->reject(paramsOrException.releaseException()); |
727 | return; |
728 | } |
729 | auto params = paramsOrException.releaseReturnValue(); |
730 | |
731 | auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); |
732 | |
733 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); |
734 | |
735 | auto index = promise.ptr(); |
736 | m_pendingPromises.add(index, WTFMove(promise)); |
737 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
738 | auto callback = [index, subtleCryptoWeakPointer](KeyOrKeyPair&& keyOrKeyPair) mutable { |
739 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { |
740 | WTF::switchOn(keyOrKeyPair, |
741 | [&promise] (RefPtr<CryptoKey>& key) { |
742 | if ((key->type() == CryptoKeyType::Private || key->type() == CryptoKeyType::Secret) && !key->usagesBitmap()) { |
743 | rejectWithException(promise.releaseNonNull(), SyntaxError); |
744 | return; |
745 | } |
746 | promise->resolve<IDLInterface<CryptoKey>>(*key); |
747 | }, |
748 | [&promise] (CryptoKeyPair& keyPair) { |
749 | if (!keyPair.privateKey->usagesBitmap()) { |
750 | rejectWithException(promise.releaseNonNull(), SyntaxError); |
751 | return; |
752 | } |
753 | promise->resolve<IDLDictionary<CryptoKeyPair>>(keyPair); |
754 | } |
755 | ); |
756 | } |
757 | }; |
758 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
759 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
760 | rejectWithException(promise.releaseNonNull(), ec); |
761 | }; |
762 | |
763 | // The 26 January 2017 version of the specification suggests we should perform the following task asynchronously |
764 | // regardless what kind of keys it produces: https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-generateKey |
765 | // That's simply not efficient for AES, HMAC and EC keys. Therefore, we perform it as an async task only for RSA keys. |
766 | algorithm->generateKey(*params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext()); |
767 | } |
768 | |
769 | void SubtleCrypto::deriveKey(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, AlgorithmIdentifier&& derivedKeyType, bool , Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise) |
770 | { |
771 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits); |
772 | if (paramsOrException.hasException()) { |
773 | promise->reject(paramsOrException.releaseException()); |
774 | return; |
775 | } |
776 | auto params = paramsOrException.releaseReturnValue(); |
777 | |
778 | auto importParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::ImportKey); |
779 | if (importParamsOrException.hasException()) { |
780 | promise->reject(importParamsOrException.releaseException()); |
781 | return; |
782 | } |
783 | auto importParams = importParamsOrException.releaseReturnValue(); |
784 | |
785 | auto getLengthParamsOrException = normalizeCryptoAlgorithmParameters(state, derivedKeyType, Operations::GetKeyLength); |
786 | if (getLengthParamsOrException.hasException()) { |
787 | promise->reject(getLengthParamsOrException.releaseException()); |
788 | return; |
789 | } |
790 | auto getLengthParams = getLengthParamsOrException.releaseReturnValue(); |
791 | |
792 | auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); |
793 | |
794 | if (params->identifier != baseKey.algorithmIdentifier()) { |
795 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
796 | return; |
797 | } |
798 | |
799 | if (!baseKey.allows(CryptoKeyUsageDeriveKey)) { |
800 | promise->reject(InvalidAccessError, "CryptoKey doesn't support CryptoKey derivation"_s ); |
801 | return; |
802 | } |
803 | |
804 | auto getLengthAlgorithm = CryptoAlgorithmRegistry::singleton().create(getLengthParams->identifier); |
805 | |
806 | auto result = getLengthAlgorithm->getKeyLength(*getLengthParams); |
807 | if (result.hasException()) { |
808 | promise->reject(result.releaseException().code(), "Cannot get key length from derivedKeyType"_s ); |
809 | return; |
810 | } |
811 | size_t length = result.releaseReturnValue(); |
812 | |
813 | auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(importParams->identifier); |
814 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); |
815 | |
816 | auto index = promise.ptr(); |
817 | m_pendingPromises.add(index, WTFMove(promise)); |
818 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
819 | auto callback = [index, subtleCryptoWeakPointer, importAlgorithm = WTFMove(importAlgorithm), importParams = crossThreadCopyImportParams(*importParams), extractable, keyUsagesBitmap](const Vector<uint8_t>& derivedKey) mutable { |
820 | // FIXME: https://bugs.webkit.org/show_bug.cgi?id=169395 |
821 | KeyData data = derivedKey; |
822 | auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { |
823 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { |
824 | if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { |
825 | rejectWithException(promise.releaseNonNull(), SyntaxError); |
826 | return; |
827 | } |
828 | promise->resolve<IDLInterface<CryptoKey>>(key); |
829 | } |
830 | }; |
831 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
832 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
833 | rejectWithException(promise.releaseNonNull(), ec); |
834 | }; |
835 | |
836 | importAlgorithm->importKey(SubtleCrypto::KeyFormat::Raw, WTFMove(data), *importParams, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); |
837 | }; |
838 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
839 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
840 | rejectWithException(promise.releaseNonNull(), ec); |
841 | }; |
842 | |
843 | algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
844 | } |
845 | |
846 | void SubtleCrypto::deriveBits(JSC::ExecState& state, AlgorithmIdentifier&& algorithmIdentifier, CryptoKey& baseKey, unsigned length, Ref<DeferredPromise>&& promise) |
847 | { |
848 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::DeriveBits); |
849 | if (paramsOrException.hasException()) { |
850 | promise->reject(paramsOrException.releaseException()); |
851 | return; |
852 | } |
853 | auto params = paramsOrException.releaseReturnValue(); |
854 | |
855 | if (params->identifier != baseKey.algorithmIdentifier()) { |
856 | promise->reject(InvalidAccessError, "CryptoKey doesn't match AlgorithmIdentifier"_s ); |
857 | return; |
858 | } |
859 | |
860 | if (!baseKey.allows(CryptoKeyUsageDeriveBits)) { |
861 | promise->reject(InvalidAccessError, "CryptoKey doesn't support bits derivation"_s ); |
862 | return; |
863 | } |
864 | |
865 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); |
866 | |
867 | auto index = promise.ptr(); |
868 | m_pendingPromises.add(index, WTFMove(promise)); |
869 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
870 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& derivedKey) mutable { |
871 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
872 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), derivedKey.data(), derivedKey.size()); |
873 | }; |
874 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
875 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
876 | rejectWithException(promise.releaseNonNull(), ec); |
877 | }; |
878 | |
879 | algorithm->deriveBits(*params, baseKey, length, WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
880 | } |
881 | |
882 | void SubtleCrypto::importKey(JSC::ExecState& state, KeyFormat format, KeyDataVariant&& keyDataVariant, AlgorithmIdentifier&& algorithmIdentifier, bool , Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise) |
883 | { |
884 | auto paramsOrException = normalizeCryptoAlgorithmParameters(state, WTFMove(algorithmIdentifier), Operations::ImportKey); |
885 | if (paramsOrException.hasException()) { |
886 | promise->reject(paramsOrException.releaseException()); |
887 | return; |
888 | } |
889 | auto params = paramsOrException.releaseReturnValue(); |
890 | |
891 | auto keyDataOrNull = toKeyData(format, WTFMove(keyDataVariant), promise); |
892 | if (!keyDataOrNull) { |
893 | // When toKeyData, it means the promise has been rejected, and we should return. |
894 | return; |
895 | } |
896 | |
897 | auto keyData = *keyDataOrNull; |
898 | auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); |
899 | |
900 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(params->identifier); |
901 | |
902 | auto index = promise.ptr(); |
903 | m_pendingPromises.add(index, WTFMove(promise)); |
904 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
905 | auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { |
906 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { |
907 | if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { |
908 | rejectWithException(promise.releaseNonNull(), SyntaxError); |
909 | return; |
910 | } |
911 | promise->resolve<IDLInterface<CryptoKey>>(key); |
912 | } |
913 | }; |
914 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
915 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
916 | rejectWithException(promise.releaseNonNull(), ec); |
917 | }; |
918 | |
919 | // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: |
920 | // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-importKey |
921 | // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. |
922 | algorithm->importKey(format, WTFMove(keyData), *params, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); |
923 | } |
924 | |
925 | void SubtleCrypto::exportKey(KeyFormat format, CryptoKey& key, Ref<DeferredPromise>&& promise) |
926 | { |
927 | if (!isSupportedExportKey(key.algorithmIdentifier())) { |
928 | promise->reject(Exception { NotSupportedError }); |
929 | return; |
930 | } |
931 | |
932 | if (!key.extractable()) { |
933 | promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s ); |
934 | return; |
935 | } |
936 | |
937 | auto algorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
938 | |
939 | auto index = promise.ptr(); |
940 | m_pendingPromises.add(index, WTFMove(promise)); |
941 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
942 | auto callback = [index, subtleCryptoWeakPointer](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { |
943 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { |
944 | switch (format) { |
945 | case SubtleCrypto::KeyFormat::Spki: |
946 | case SubtleCrypto::KeyFormat::Pkcs8: |
947 | case SubtleCrypto::KeyFormat::Raw: { |
948 | Vector<uint8_t>& rawKey = WTF::get<Vector<uint8_t>>(key); |
949 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), rawKey.data(), rawKey.size()); |
950 | return; |
951 | } |
952 | case SubtleCrypto::KeyFormat::Jwk: |
953 | promise->resolve<IDLDictionary<JsonWebKey>>(WTFMove(WTF::get<JsonWebKey>(key))); |
954 | return; |
955 | } |
956 | ASSERT_NOT_REACHED(); |
957 | } |
958 | }; |
959 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
960 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
961 | rejectWithException(promise.releaseNonNull(), ec); |
962 | }; |
963 | |
964 | // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: |
965 | // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-exportKey |
966 | // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. |
967 | algorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback)); |
968 | } |
969 | |
970 | void SubtleCrypto::wrapKey(JSC::ExecState& state, KeyFormat format, CryptoKey& key, CryptoKey& wrappingKey, AlgorithmIdentifier&& wrapAlgorithmIdentifier, Ref<DeferredPromise>&& promise) |
971 | { |
972 | bool isEncryption = false; |
973 | |
974 | auto wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::WrapKey); |
975 | if (wrapParamsOrException.hasException()) { |
976 | ASSERT(wrapParamsOrException.exception().code() != ExistingExceptionError); |
977 | |
978 | wrapParamsOrException = normalizeCryptoAlgorithmParameters(state, wrapAlgorithmIdentifier, Operations::Encrypt); |
979 | if (wrapParamsOrException.hasException()) { |
980 | promise->reject(wrapParamsOrException.releaseException()); |
981 | return; |
982 | } |
983 | |
984 | isEncryption = true; |
985 | } |
986 | auto wrapParams = wrapParamsOrException.releaseReturnValue(); |
987 | |
988 | if (wrapParams->identifier != wrappingKey.algorithmIdentifier()) { |
989 | promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't match AlgorithmIdentifier"_s ); |
990 | return; |
991 | } |
992 | |
993 | if (!wrappingKey.allows(CryptoKeyUsageWrapKey)) { |
994 | promise->reject(InvalidAccessError, "Wrapping CryptoKey doesn't support wrapKey operation"_s ); |
995 | return; |
996 | } |
997 | |
998 | if (!isSupportedExportKey(key.algorithmIdentifier())) { |
999 | promise->reject(Exception { NotSupportedError }); |
1000 | return; |
1001 | } |
1002 | |
1003 | if (!key.extractable()) { |
1004 | promise->reject(InvalidAccessError, "The CryptoKey is nonextractable"_s ); |
1005 | return; |
1006 | } |
1007 | |
1008 | auto exportAlgorithm = CryptoAlgorithmRegistry::singleton().create(key.algorithmIdentifier()); |
1009 | auto wrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(wrappingKey.algorithmIdentifier()); |
1010 | |
1011 | auto context = scriptExecutionContext(); |
1012 | |
1013 | auto index = promise.ptr(); |
1014 | m_pendingPromises.add(index, WTFMove(promise)); |
1015 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
1016 | auto callback = [index, subtleCryptoWeakPointer, wrapAlgorithm, wrappingKey = makeRef(wrappingKey), wrapParams = WTFMove(wrapParams), isEncryption, context, workQueue = m_workQueue.copyRef()](SubtleCrypto::KeyFormat format, KeyData&& key) mutable { |
1017 | if (subtleCryptoWeakPointer) { |
1018 | if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) { |
1019 | Vector<uint8_t> bytes; |
1020 | switch (format) { |
1021 | case SubtleCrypto::KeyFormat::Spki: |
1022 | case SubtleCrypto::KeyFormat::Pkcs8: |
1023 | case SubtleCrypto::KeyFormat::Raw: |
1024 | bytes = WTF::get<Vector<uint8_t>>(key); |
1025 | break; |
1026 | case SubtleCrypto::KeyFormat::Jwk: { |
1027 | // FIXME: Converting to JS just to JSON-Stringify seems inefficient. We should find a way to go directly from the struct to JSON. |
1028 | auto jwk = toJS<IDLDictionary<JsonWebKey>>(*(promise->globalObject()->globalExec()), *(promise->globalObject()), WTFMove(WTF::get<JsonWebKey>(key))); |
1029 | String jwkString = JSONStringify(promise->globalObject()->globalExec(), jwk, 0); |
1030 | CString jwkUtf8String = jwkString.utf8(StrictConversion); |
1031 | bytes.append(jwkUtf8String.data(), jwkUtf8String.length()); |
1032 | } |
1033 | } |
1034 | |
1035 | auto callback = [index, subtleCryptoWeakPointer](const Vector<uint8_t>& wrappedKey) mutable { |
1036 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
1037 | fulfillPromiseWithArrayBuffer(promise.releaseNonNull(), wrappedKey.data(), wrappedKey.size()); |
1038 | }; |
1039 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
1040 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
1041 | rejectWithException(promise.releaseNonNull(), ec); |
1042 | }; |
1043 | |
1044 | if (!isEncryption) { |
1045 | // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: |
1046 | // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey |
1047 | // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. |
1048 | wrapAlgorithm->wrapKey(wrappingKey.get(), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback)); |
1049 | return; |
1050 | } |
1051 | // The following operation should be performed asynchronously. |
1052 | wrapAlgorithm->encrypt(*wrapParams, WTFMove(wrappingKey), WTFMove(bytes), WTFMove(callback), WTFMove(exceptionCallback), *context, workQueue); |
1053 | } |
1054 | } |
1055 | }; |
1056 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
1057 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
1058 | rejectWithException(promise.releaseNonNull(), ec); |
1059 | }; |
1060 | |
1061 | // The following operation should be performed synchronously. |
1062 | exportAlgorithm->exportKey(format, key, WTFMove(callback), WTFMove(exceptionCallback)); |
1063 | } |
1064 | |
1065 | void SubtleCrypto::unwrapKey(JSC::ExecState& state, KeyFormat format, BufferSource&& wrappedKeyBufferSource, CryptoKey& unwrappingKey, AlgorithmIdentifier&& unwrapAlgorithmIdentifier, AlgorithmIdentifier&& unwrappedKeyAlgorithmIdentifier, bool , Vector<CryptoKeyUsage>&& keyUsages, Ref<DeferredPromise>&& promise) |
1066 | { |
1067 | auto wrappedKey = copyToVector(WTFMove(wrappedKeyBufferSource)); |
1068 | |
1069 | bool isDecryption = false; |
1070 | |
1071 | auto unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::UnwrapKey); |
1072 | if (unwrapParamsOrException.hasException()) { |
1073 | ASSERT(unwrapParamsOrException.exception().code() != ExistingExceptionError); |
1074 | |
1075 | unwrapParamsOrException = normalizeCryptoAlgorithmParameters(state, unwrapAlgorithmIdentifier, Operations::Decrypt); |
1076 | if (unwrapParamsOrException.hasException()) { |
1077 | promise->reject(unwrapParamsOrException.releaseException()); |
1078 | return; |
1079 | } |
1080 | |
1081 | isDecryption = true; |
1082 | } |
1083 | auto unwrapParams = unwrapParamsOrException.releaseReturnValue(); |
1084 | |
1085 | auto unwrappedKeyAlgorithmOrException = normalizeCryptoAlgorithmParameters(state, unwrappedKeyAlgorithmIdentifier, Operations::ImportKey); |
1086 | if (unwrappedKeyAlgorithmOrException.hasException()) { |
1087 | promise->reject(unwrappedKeyAlgorithmOrException.releaseException()); |
1088 | return; |
1089 | } |
1090 | auto unwrappedKeyAlgorithm = unwrappedKeyAlgorithmOrException.releaseReturnValue(); |
1091 | |
1092 | auto keyUsagesBitmap = toCryptoKeyUsageBitmap(keyUsages); |
1093 | |
1094 | if (unwrapParams->identifier != unwrappingKey.algorithmIdentifier()) { |
1095 | promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't match unwrap AlgorithmIdentifier"_s ); |
1096 | return; |
1097 | } |
1098 | |
1099 | if (!unwrappingKey.allows(CryptoKeyUsageUnwrapKey)) { |
1100 | promise->reject(InvalidAccessError, "Unwrapping CryptoKey doesn't support unwrapKey operation"_s ); |
1101 | return; |
1102 | } |
1103 | |
1104 | auto importAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappedKeyAlgorithm->identifier); |
1105 | if (UNLIKELY(!importAlgorithm)) { |
1106 | promise->reject(Exception { NotSupportedError }); |
1107 | return; |
1108 | } |
1109 | |
1110 | auto unwrapAlgorithm = CryptoAlgorithmRegistry::singleton().create(unwrappingKey.algorithmIdentifier()); |
1111 | if (UNLIKELY(!unwrapAlgorithm)) { |
1112 | promise->reject(Exception { NotSupportedError }); |
1113 | return; |
1114 | } |
1115 | |
1116 | auto index = promise.ptr(); |
1117 | m_pendingPromises.add(index, WTFMove(promise)); |
1118 | auto subtleCryptoWeakPointer = makeWeakPtr(*this); |
1119 | auto callback = [index, subtleCryptoWeakPointer, format, importAlgorithm, unwrappedKeyAlgorithm = crossThreadCopyImportParams(*unwrappedKeyAlgorithm), extractable, keyUsagesBitmap](const Vector<uint8_t>& bytes) mutable { |
1120 | if (subtleCryptoWeakPointer) { |
1121 | if (auto promise = subtleCryptoWeakPointer->m_pendingPromises.get(index)) { |
1122 | KeyData keyData; |
1123 | switch (format) { |
1124 | case SubtleCrypto::KeyFormat::Spki: |
1125 | case SubtleCrypto::KeyFormat::Pkcs8: |
1126 | case SubtleCrypto::KeyFormat::Raw: |
1127 | keyData = bytes; |
1128 | break; |
1129 | case SubtleCrypto::KeyFormat::Jwk: { |
1130 | auto& state = *(promise->globalObject()->globalExec()); |
1131 | auto& vm = state.vm(); |
1132 | auto scope = DECLARE_THROW_SCOPE(vm); |
1133 | |
1134 | String jwkString(reinterpret_cast_ptr<const char*>(bytes.data()), bytes.size()); |
1135 | JSLockHolder locker(vm); |
1136 | auto jwkObject = JSONParse(&state, jwkString); |
1137 | if (!jwkObject) { |
1138 | promise->reject(DataError, "WrappedKey cannot be converted to a JSON object"_s ); |
1139 | return; |
1140 | } |
1141 | auto jwk = convert<IDLDictionary<JsonWebKey>>(state, jwkObject); |
1142 | RETURN_IF_EXCEPTION(scope, void()); |
1143 | normalizeJsonWebKey(jwk); |
1144 | |
1145 | keyData = jwk; |
1146 | break; |
1147 | } |
1148 | } |
1149 | |
1150 | auto callback = [index, subtleCryptoWeakPointer](CryptoKey& key) mutable { |
1151 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) { |
1152 | if ((key.type() == CryptoKeyType::Private || key.type() == CryptoKeyType::Secret) && !key.usagesBitmap()) { |
1153 | rejectWithException(promise.releaseNonNull(), SyntaxError); |
1154 | return; |
1155 | } |
1156 | promise->resolve<IDLInterface<CryptoKey>>(key); |
1157 | } |
1158 | }; |
1159 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
1160 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
1161 | rejectWithException(promise.releaseNonNull(), ec); |
1162 | }; |
1163 | |
1164 | // The following operation should be performed synchronously. |
1165 | importAlgorithm->importKey(format, WTFMove(keyData), *unwrappedKeyAlgorithm, extractable, keyUsagesBitmap, WTFMove(callback), WTFMove(exceptionCallback)); |
1166 | } |
1167 | } |
1168 | }; |
1169 | auto exceptionCallback = [index, subtleCryptoWeakPointer](ExceptionCode ec) mutable { |
1170 | if (auto promise = getPromise(index, subtleCryptoWeakPointer)) |
1171 | rejectWithException(promise.releaseNonNull(), ec); |
1172 | }; |
1173 | |
1174 | if (!isDecryption) { |
1175 | // The 11 December 2014 version of the specification suggests we should perform the following task asynchronously: |
1176 | // https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-unwrapKey |
1177 | // It is not beneficial for less time consuming operations. Therefore, we perform it synchronously. |
1178 | unwrapAlgorithm->unwrapKey(unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback)); |
1179 | return; |
1180 | } |
1181 | |
1182 | unwrapAlgorithm->decrypt(*unwrapParams, unwrappingKey, WTFMove(wrappedKey), WTFMove(callback), WTFMove(exceptionCallback), *scriptExecutionContext(), m_workQueue); |
1183 | } |
1184 | |
1185 | } |
1186 | |
1187 | #endif |
1188 | |