1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "pk11pub.h"
8 #include "cryptohi.h"
9 #include "secerr.h"
10 #include "nsNSSComponent.h"
11 #include "nsProxyRelease.h"
12
13 #include "jsapi.h"
14 #include "mozilla/Telemetry.h"
15 #include "mozilla/Utf8.h"
16 #include "mozilla/dom/CryptoBuffer.h"
17 #include "mozilla/dom/CryptoKey.h"
18 #include "mozilla/dom/KeyAlgorithmProxy.h"
19 #include "mozilla/dom/TypedArray.h"
20 #include "mozilla/dom/WebCryptoCommon.h"
21 #include "mozilla/dom/WebCryptoTask.h"
22 #include "mozilla/dom/WorkerRef.h"
23 #include "mozilla/dom/WorkerPrivate.h"
24
25 // Template taken from security/nss/lib/util/templates.c
26 // This (or SGN_EncodeDigestInfo) would ideally be exported
27 // by NSS and until that happens we have to keep our own copy.
28 const SEC_ASN1Template SGN_DigestInfoTemplate[] = {
29 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SGNDigestInfo)},
30 {SEC_ASN1_INLINE, offsetof(SGNDigestInfo, digestAlgorithm),
31 SEC_ASN1_GET(SECOID_AlgorithmIDTemplate)},
32 {SEC_ASN1_OCTET_STRING, offsetof(SGNDigestInfo, digest)},
33 {
34 0,
35 }};
36
37 namespace mozilla {
38 namespace dom {
39
40 // Pre-defined identifiers for telemetry histograms
41
42 enum TelemetryMethod {
43 TM_ENCRYPT = 0,
44 TM_DECRYPT = 1,
45 TM_SIGN = 2,
46 TM_VERIFY = 3,
47 TM_DIGEST = 4,
48 TM_GENERATEKEY = 5,
49 TM_DERIVEKEY = 6,
50 TM_DERIVEBITS = 7,
51 TM_IMPORTKEY = 8,
52 TM_EXPORTKEY = 9,
53 TM_WRAPKEY = 10,
54 TM_UNWRAPKEY = 11
55 };
56
57 enum TelemetryAlgorithm {
58 // Please make additions at the end of the list,
59 // to preserve comparability of histograms over time
60 TA_UNKNOWN = 0,
61 // encrypt / decrypt
62 TA_AES_CBC = 1,
63 TA_AES_CFB = 2,
64 TA_AES_CTR = 3,
65 TA_AES_GCM = 4,
66 TA_RSAES_PKCS1 = 5, // NB: This algorithm has been removed
67 TA_RSA_OAEP = 6,
68 // sign/verify
69 TA_RSASSA_PKCS1 = 7,
70 TA_RSA_PSS = 8,
71 TA_HMAC_SHA_1 = 9,
72 TA_HMAC_SHA_224 = 10,
73 TA_HMAC_SHA_256 = 11,
74 TA_HMAC_SHA_384 = 12,
75 TA_HMAC_SHA_512 = 13,
76 // digest
77 TA_SHA_1 = 14,
78 TA_SHA_224 = 15,
79 TA_SHA_256 = 16,
80 TA_SHA_384 = 17,
81 TA_SHA_512 = 18,
82 // Later additions
83 TA_AES_KW = 19,
84 TA_ECDH = 20,
85 TA_PBKDF2 = 21,
86 TA_ECDSA = 22,
87 TA_HKDF = 23,
88 TA_DH = 24,
89 };
90
91 // Convenience functions for extracting / converting information
92
93 // OOM-safe CryptoBuffer initialization, suitable for constructors
94 #define ATTEMPT_BUFFER_INIT(dst, src) \
95 if (!dst.Assign(src)) { \
96 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR; \
97 return; \
98 }
99
100 // OOM-safe CryptoBuffer-to-SECItem copy, suitable for DoCrypto
101 #define ATTEMPT_BUFFER_TO_SECITEM(arena, dst, src) \
102 if (!src.ToSECItem(arena, dst)) { \
103 return NS_ERROR_DOM_UNKNOWN_ERR; \
104 }
105
106 // OOM-safe CryptoBuffer copy, suitable for DoCrypto
107 #define ATTEMPT_BUFFER_ASSIGN(dst, src) \
108 if (!dst.Assign(src)) { \
109 return NS_ERROR_DOM_UNKNOWN_ERR; \
110 }
111
112 // Safety check for algorithms that use keys, suitable for constructors
113 #define CHECK_KEY_ALGORITHM(keyAlg, algName) \
114 { \
115 if (!NORMALIZED_EQUALS(keyAlg.mName, algName)) { \
116 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR; \
117 return; \
118 } \
119 }
120
121 class ClearException {
122 public:
ClearException(JSContext * aCx)123 explicit ClearException(JSContext* aCx) : mCx(aCx) {}
124
~ClearException()125 ~ClearException() { JS_ClearPendingException(mCx); }
126
127 private:
128 JSContext* mCx;
129 };
130
131 template <class OOS>
GetAlgorithmName(JSContext * aCx,const OOS & aAlgorithm,nsString & aName)132 static nsresult GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
133 nsString& aName) {
134 ClearException ce(aCx);
135
136 if (aAlgorithm.IsString()) {
137 // If string, then treat as algorithm name
138 aName.Assign(aAlgorithm.GetAsString());
139 } else {
140 // Coerce to algorithm and extract name
141 JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
142 Algorithm alg;
143
144 if (!alg.Init(aCx, value)) {
145 return NS_ERROR_DOM_SYNTAX_ERR;
146 }
147
148 aName = alg.mName;
149 }
150
151 if (!NormalizeToken(aName, aName)) {
152 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
153 }
154
155 return NS_OK;
156 }
157
158 template <class T, class OOS>
Coerce(JSContext * aCx,T & aTarget,const OOS & aAlgorithm)159 static nsresult Coerce(JSContext* aCx, T& aTarget, const OOS& aAlgorithm) {
160 ClearException ce(aCx);
161
162 if (!aAlgorithm.IsObject()) {
163 return NS_ERROR_DOM_SYNTAX_ERR;
164 }
165
166 JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
167 if (!aTarget.Init(aCx, value)) {
168 return NS_ERROR_DOM_SYNTAX_ERR;
169 }
170
171 return NS_OK;
172 }
173
MapHashAlgorithmNameToBlockSize(const nsString & aName)174 inline size_t MapHashAlgorithmNameToBlockSize(const nsString& aName) {
175 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
176 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
177 return 512;
178 }
179
180 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
181 aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
182 return 1024;
183 }
184
185 return 0;
186 }
187
GetKeyLengthForAlgorithm(JSContext * aCx,const ObjectOrString & aAlgorithm,size_t & aLength)188 inline nsresult GetKeyLengthForAlgorithm(JSContext* aCx,
189 const ObjectOrString& aAlgorithm,
190 size_t& aLength) {
191 aLength = 0;
192
193 // Extract algorithm name
194 nsString algName;
195 if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
196 return NS_ERROR_DOM_SYNTAX_ERR;
197 }
198
199 // Read AES key length from given algorithm object.
200 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
201 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
202 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
203 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
204 RootedDictionary<AesDerivedKeyParams> params(aCx);
205 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
206 return NS_ERROR_DOM_SYNTAX_ERR;
207 }
208
209 if (params.mLength != 128 && params.mLength != 192 &&
210 params.mLength != 256) {
211 return NS_ERROR_DOM_DATA_ERR;
212 }
213
214 aLength = params.mLength;
215 return NS_OK;
216 }
217
218 // Read HMAC key length from given algorithm object or
219 // determine key length as the block size of the given hash.
220 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
221 RootedDictionary<HmacDerivedKeyParams> params(aCx);
222 if (NS_FAILED(Coerce(aCx, params, aAlgorithm))) {
223 return NS_ERROR_DOM_SYNTAX_ERR;
224 }
225
226 // Return the passed length, if any.
227 if (params.mLength.WasPassed()) {
228 aLength = params.mLength.Value();
229 return NS_OK;
230 }
231
232 nsString hashName;
233 if (NS_FAILED(GetAlgorithmName(aCx, params.mHash, hashName))) {
234 return NS_ERROR_DOM_SYNTAX_ERR;
235 }
236
237 // Return the given hash algorithm's block size as the key length.
238 size_t length = MapHashAlgorithmNameToBlockSize(hashName);
239 if (length == 0) {
240 return NS_ERROR_DOM_SYNTAX_ERR;
241 }
242
243 aLength = length;
244 return NS_OK;
245 }
246
247 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
248 }
249
MapOIDTagToNamedCurve(SECOidTag aOIDTag,nsString & aResult)250 inline bool MapOIDTagToNamedCurve(SECOidTag aOIDTag, nsString& aResult) {
251 switch (aOIDTag) {
252 case SEC_OID_SECG_EC_SECP256R1:
253 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256);
254 break;
255 case SEC_OID_SECG_EC_SECP384R1:
256 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384);
257 break;
258 case SEC_OID_SECG_EC_SECP521R1:
259 aResult.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521);
260 break;
261 default:
262 return false;
263 }
264
265 return true;
266 }
267
MapHashAlgorithmNameToOID(const nsString & aName)268 inline SECOidTag MapHashAlgorithmNameToOID(const nsString& aName) {
269 SECOidTag hashOID(SEC_OID_UNKNOWN);
270
271 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
272 hashOID = SEC_OID_SHA1;
273 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
274 hashOID = SEC_OID_SHA256;
275 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
276 hashOID = SEC_OID_SHA384;
277 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
278 hashOID = SEC_OID_SHA512;
279 }
280
281 return hashOID;
282 }
283
MapHashAlgorithmNameToMgfMechanism(const nsString & aName)284 inline CK_MECHANISM_TYPE MapHashAlgorithmNameToMgfMechanism(
285 const nsString& aName) {
286 CK_MECHANISM_TYPE mech(UNKNOWN_CK_MECHANISM);
287
288 if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
289 mech = CKG_MGF1_SHA1;
290 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
291 mech = CKG_MGF1_SHA256;
292 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
293 mech = CKG_MGF1_SHA384;
294 } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
295 mech = CKG_MGF1_SHA512;
296 }
297
298 return mech;
299 }
300
301 // Implementation of WebCryptoTask methods
302
DispatchWithPromise(Promise * aResultPromise)303 void WebCryptoTask::DispatchWithPromise(Promise* aResultPromise) {
304 mResultPromise = aResultPromise;
305
306 // Fail if an error was set during the constructor
307 MAYBE_EARLY_FAIL(mEarlyRv)
308
309 // Perform pre-NSS operations, and fail if they fail
310 mEarlyRv = BeforeCrypto();
311 MAYBE_EARLY_FAIL(mEarlyRv)
312
313 // Skip dispatch if we're already done. Otherwise launch a CryptoTask
314 if (mEarlyComplete) {
315 CallCallback(mEarlyRv);
316 return;
317 }
318
319 // Store calling thread
320 mOriginalEventTarget = GetCurrentThreadSerialEventTarget();
321
322 // If we are running on a worker thread we must hold the worker
323 // alive while we work on the thread pool. Otherwise the worker
324 // private may get torn down before we dispatch back to complete
325 // the transaction.
326 if (!NS_IsMainThread()) {
327 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
328 MOZ_ASSERT(workerPrivate);
329
330 RefPtr<StrongWorkerRef> workerRef =
331 StrongWorkerRef::Create(workerPrivate, "WebCryptoTask");
332 if (NS_WARN_IF(!workerRef)) {
333 mEarlyRv = NS_BINDING_ABORTED;
334 } else {
335 mWorkerRef = new ThreadSafeWorkerRef(workerRef);
336 }
337 }
338 MAYBE_EARLY_FAIL(mEarlyRv);
339
340 // dispatch to thread pool
341
342 if (!EnsureNSSInitializedChromeOrContent()) {
343 mEarlyRv = NS_ERROR_FAILURE;
344 }
345 MAYBE_EARLY_FAIL(mEarlyRv);
346
347 mEarlyRv = NS_DispatchBackgroundTask(this);
348 MAYBE_EARLY_FAIL(mEarlyRv)
349 }
350
351 NS_IMETHODIMP
Run()352 WebCryptoTask::Run() {
353 // Run heavy crypto operations on the thread pool, off the original thread.
354 if (!IsOnOriginalThread()) {
355 mRv = CalculateResult();
356
357 // Back to the original thread, i.e. continue below.
358 mOriginalEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
359 return NS_OK;
360 }
361
362 // We're now back on the calling thread.
363 CallCallback(mRv);
364
365 // Stop holding the worker thread alive now that the async work has
366 // been completed.
367 mWorkerRef = nullptr;
368
369 return NS_OK;
370 }
371
Cancel()372 nsresult WebCryptoTask::Cancel() {
373 MOZ_ASSERT(IsOnOriginalThread());
374 FailWithError(NS_BINDING_ABORTED);
375 return NS_OK;
376 }
377
FailWithError(nsresult aRv)378 void WebCryptoTask::FailWithError(nsresult aRv) {
379 MOZ_ASSERT(IsOnOriginalThread());
380 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
381
382 // Blindly convert nsresult to DOMException
383 // Individual tasks must ensure they pass the right values
384 mResultPromise->MaybeReject(aRv);
385 // Manually release mResultPromise while we're on the main thread
386 mResultPromise = nullptr;
387 mWorkerRef = nullptr;
388 Cleanup();
389 }
390
CalculateResult()391 nsresult WebCryptoTask::CalculateResult() {
392 MOZ_ASSERT(!IsOnOriginalThread());
393
394 return DoCrypto();
395 }
396
CallCallback(nsresult rv)397 void WebCryptoTask::CallCallback(nsresult rv) {
398 MOZ_ASSERT(IsOnOriginalThread());
399 if (NS_FAILED(rv)) {
400 FailWithError(rv);
401 return;
402 }
403
404 nsresult rv2 = AfterCrypto();
405 if (NS_FAILED(rv2)) {
406 FailWithError(rv2);
407 return;
408 }
409
410 Resolve();
411 Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, true);
412
413 // Manually release mResultPromise while we're on the main thread
414 mResultPromise = nullptr;
415 Cleanup();
416 }
417
418 // Some generic utility classes
419
420 class FailureTask : public WebCryptoTask {
421 public:
FailureTask(nsresult aRv)422 explicit FailureTask(nsresult aRv) { mEarlyRv = aRv; }
423 };
424
425 class ReturnArrayBufferViewTask : public WebCryptoTask {
426 protected:
427 CryptoBuffer mResult;
428
429 private:
430 // Returns mResult as an ArrayBufferView, or an error
Resolve()431 virtual void Resolve() override {
432 TypedArrayCreator<ArrayBuffer> ret(mResult);
433 mResultPromise->MaybeResolve(ret);
434 }
435 };
436
437 class DeferredData {
438 public:
439 template <class T>
SetData(const T & aData)440 void SetData(const T& aData) {
441 mDataIsSet = mData.Assign(aData);
442 }
443
444 protected:
DeferredData()445 DeferredData() : mDataIsSet(false) {}
446
447 CryptoBuffer mData;
448 bool mDataIsSet;
449 };
450
451 class AesTask : public ReturnArrayBufferViewTask, public DeferredData {
452 public:
AesTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)453 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
454 bool aEncrypt)
455 : mMechanism(CKM_INVALID_MECHANISM),
456 mTagLength(0),
457 mCounterLength(0),
458 mEncrypt(aEncrypt) {
459 Init(aCx, aAlgorithm, aKey, aEncrypt);
460 }
461
AesTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aData,bool aEncrypt)462 AesTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
463 const CryptoOperationData& aData, bool aEncrypt)
464 : mMechanism(CKM_INVALID_MECHANISM),
465 mTagLength(0),
466 mCounterLength(0),
467 mEncrypt(aEncrypt) {
468 Init(aCx, aAlgorithm, aKey, aEncrypt);
469 SetData(aData);
470 }
471
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)472 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
473 bool aEncrypt) {
474 nsString algName;
475 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
476 if (NS_FAILED(mEarlyRv)) {
477 return;
478 }
479
480 if (!mSymKey.Assign(aKey.GetSymKey())) {
481 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
482 return;
483 }
484
485 // Check that we got a reasonable key
486 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
487 (mSymKey.Length() != 32)) {
488 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
489 return;
490 }
491
492 // Cache parameters depending on the specific algorithm
493 TelemetryAlgorithm telemetryAlg;
494 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
495 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CBC);
496
497 mMechanism = CKM_AES_CBC_PAD;
498 telemetryAlg = TA_AES_CBC;
499 RootedDictionary<AesCbcParams> params(aCx);
500 nsresult rv = Coerce(aCx, params, aAlgorithm);
501 if (NS_FAILED(rv)) {
502 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
503 return;
504 }
505
506 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
507 if (mIv.Length() != 16) {
508 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
509 return;
510 }
511 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
512 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_CTR);
513
514 mMechanism = CKM_AES_CTR;
515 telemetryAlg = TA_AES_CTR;
516 RootedDictionary<AesCtrParams> params(aCx);
517 nsresult rv = Coerce(aCx, params, aAlgorithm);
518 if (NS_FAILED(rv)) {
519 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
520 return;
521 }
522
523 ATTEMPT_BUFFER_INIT(mIv, params.mCounter)
524 if (mIv.Length() != 16) {
525 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
526 return;
527 }
528
529 mCounterLength = params.mLength;
530 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
531 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_GCM);
532
533 mMechanism = CKM_AES_GCM;
534 telemetryAlg = TA_AES_GCM;
535 RootedDictionary<AesGcmParams> params(aCx);
536 nsresult rv = Coerce(aCx, params, aAlgorithm);
537 if (NS_FAILED(rv)) {
538 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
539 return;
540 }
541
542 ATTEMPT_BUFFER_INIT(mIv, params.mIv)
543
544 if (params.mAdditionalData.WasPassed()) {
545 ATTEMPT_BUFFER_INIT(mAad, params.mAdditionalData.Value())
546 }
547
548 // 32, 64, 96, 104, 112, 120 or 128
549 mTagLength = 128;
550 if (params.mTagLength.WasPassed()) {
551 mTagLength = params.mTagLength.Value();
552 if ((mTagLength > 128) ||
553 !(mTagLength == 32 || mTagLength == 64 ||
554 (mTagLength >= 96 && mTagLength % 8 == 0))) {
555 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
556 return;
557 }
558 }
559 } else {
560 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
561 return;
562 }
563 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
564 }
565
566 private:
567 CK_MECHANISM_TYPE mMechanism;
568 CryptoBuffer mSymKey;
569 CryptoBuffer mIv; // Initialization vector
570 CryptoBuffer mAad; // Additional Authenticated Data
571 uint8_t mTagLength;
572 uint8_t mCounterLength;
573 bool mEncrypt;
574
DoCrypto()575 virtual nsresult DoCrypto() override {
576 nsresult rv;
577
578 if (!mDataIsSet) {
579 return NS_ERROR_DOM_OPERATION_ERR;
580 }
581
582 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
583 if (!arena) {
584 return NS_ERROR_DOM_OPERATION_ERR;
585 }
586
587 // Construct the parameters object depending on algorithm
588 SECItem param = {siBuffer, nullptr, 0};
589 CK_AES_CTR_PARAMS ctrParams;
590 CK_GCM_PARAMS gcmParams;
591 switch (mMechanism) {
592 case CKM_AES_CBC_PAD:
593 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), ¶m, mIv);
594 break;
595 case CKM_AES_CTR:
596 ctrParams.ulCounterBits = mCounterLength;
597 MOZ_ASSERT(mIv.Length() == 16);
598 memcpy(&ctrParams.cb, mIv.Elements(), 16);
599 param.type = siBuffer;
600 param.data = (unsigned char*)&ctrParams;
601 param.len = sizeof(ctrParams);
602 break;
603 case CKM_AES_GCM:
604 gcmParams.pIv = mIv.Elements();
605 gcmParams.ulIvLen = mIv.Length();
606 gcmParams.ulIvBits = gcmParams.ulIvLen * 8;
607 gcmParams.pAAD = mAad.Elements();
608 gcmParams.ulAADLen = mAad.Length();
609 gcmParams.ulTagBits = mTagLength;
610 param.type = siBuffer;
611 param.data = (unsigned char*)&gcmParams;
612 param.len = sizeof(gcmParams);
613 break;
614 default:
615 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
616 }
617
618 // Import the key
619 SECItem keyItem = {siBuffer, nullptr, 0};
620 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
621 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
622 MOZ_ASSERT(slot.get());
623 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
624 PK11_OriginUnwrap, CKA_ENCRYPT,
625 &keyItem, nullptr));
626 if (!symKey) {
627 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
628 }
629
630 // Check whether the integer addition would overflow.
631 if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 <
632 mData.Length()) {
633 return NS_ERROR_DOM_DATA_ERR;
634 }
635
636 // Initialize the output buffer (enough space for padding / a full tag)
637 if (!mResult.SetLength(mData.Length() + 16, fallible)) {
638 return NS_ERROR_DOM_UNKNOWN_ERR;
639 }
640
641 uint32_t outLen = 0;
642
643 // Perform the encryption/decryption
644 if (mEncrypt) {
645 rv = MapSECStatus(PK11_Encrypt(
646 symKey.get(), mMechanism, ¶m, mResult.Elements(), &outLen,
647 mResult.Length(), mData.Elements(), mData.Length()));
648 } else {
649 rv = MapSECStatus(PK11_Decrypt(
650 symKey.get(), mMechanism, ¶m, mResult.Elements(), &outLen,
651 mResult.Length(), mData.Elements(), mData.Length()));
652 }
653 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
654
655 mResult.TruncateLength(outLen);
656 return rv;
657 }
658 };
659
660 // This class looks like an encrypt/decrypt task, like AesTask,
661 // but it is only exposed to wrapKey/unwrapKey, not encrypt/decrypt
662 class AesKwTask : public ReturnArrayBufferViewTask, public DeferredData {
663 public:
AesKwTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)664 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
665 bool aEncrypt)
666 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
667 Init(aCx, aAlgorithm, aKey, aEncrypt);
668 }
669
AesKwTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aData,bool aEncrypt)670 AesKwTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
671 const CryptoOperationData& aData, bool aEncrypt)
672 : mMechanism(CKM_NSS_AES_KEY_WRAP), mEncrypt(aEncrypt) {
673 Init(aCx, aAlgorithm, aKey, aEncrypt);
674 SetData(aData);
675 }
676
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)677 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
678 bool aEncrypt) {
679 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_AES_KW);
680
681 nsString algName;
682 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
683 if (NS_FAILED(mEarlyRv)) {
684 return;
685 }
686
687 if (!mSymKey.Assign(aKey.GetSymKey())) {
688 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
689 return;
690 }
691
692 // Check that we got a reasonable key
693 if ((mSymKey.Length() != 16) && (mSymKey.Length() != 24) &&
694 (mSymKey.Length() != 32)) {
695 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
696 return;
697 }
698
699 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_AES_KW);
700 }
701
702 private:
703 CK_MECHANISM_TYPE mMechanism;
704 CryptoBuffer mSymKey;
705 bool mEncrypt;
706
DoCrypto()707 virtual nsresult DoCrypto() override {
708 nsresult rv;
709
710 if (!mDataIsSet) {
711 return NS_ERROR_DOM_OPERATION_ERR;
712 }
713
714 // Check that the input is a multiple of 64 bits long
715 if (mData.Length() == 0 || mData.Length() % 8 != 0) {
716 return NS_ERROR_DOM_DATA_ERR;
717 }
718
719 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
720 if (!arena) {
721 return NS_ERROR_DOM_OPERATION_ERR;
722 }
723
724 // Import the key
725 SECItem keyItem = {siBuffer, nullptr, 0};
726 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
727 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
728 MOZ_ASSERT(slot.get());
729 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
730 PK11_OriginUnwrap, CKA_WRAP,
731 &keyItem, nullptr));
732 if (!symKey) {
733 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
734 }
735
736 // Import the data to a SECItem
737 SECItem dataItem = {siBuffer, nullptr, 0};
738 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &dataItem, mData);
739
740 // Parameters for the fake keys
741 CK_MECHANISM_TYPE fakeMechanism = CKM_SHA_1_HMAC;
742 CK_ATTRIBUTE_TYPE fakeOperation = CKA_SIGN;
743
744 if (mEncrypt) {
745 // Import the data into a fake PK11SymKey structure
746 UniquePK11SymKey keyToWrap(
747 PK11_ImportSymKey(slot.get(), fakeMechanism, PK11_OriginUnwrap,
748 fakeOperation, &dataItem, nullptr));
749 if (!keyToWrap) {
750 return NS_ERROR_DOM_OPERATION_ERR;
751 }
752
753 // Encrypt and return the wrapped key
754 // AES-KW encryption results in a wrapped key 64 bits longer
755 if (!mResult.SetLength(mData.Length() + 8, fallible)) {
756 return NS_ERROR_DOM_OPERATION_ERR;
757 }
758 SECItem resultItem = {siBuffer, mResult.Elements(),
759 (unsigned int)mResult.Length()};
760 rv = MapSECStatus(PK11_WrapSymKey(mMechanism, nullptr, symKey.get(),
761 keyToWrap.get(), &resultItem));
762 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
763 } else {
764 // Decrypt the ciphertext into a temporary PK11SymKey
765 // Unwrapped key should be 64 bits shorter
766 int keySize = mData.Length() - 8;
767 UniquePK11SymKey unwrappedKey(
768 PK11_UnwrapSymKey(symKey.get(), mMechanism, nullptr, &dataItem,
769 fakeMechanism, fakeOperation, keySize));
770 if (!unwrappedKey) {
771 return NS_ERROR_DOM_OPERATION_ERR;
772 }
773
774 // Export the key to get the cleartext
775 rv = MapSECStatus(PK11_ExtractKeyValue(unwrappedKey.get()));
776 if (NS_FAILED(rv)) {
777 return NS_ERROR_DOM_UNKNOWN_ERR;
778 }
779 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(unwrappedKey.get()));
780 }
781
782 return rv;
783 }
784 };
785
786 class RsaOaepTask : public ReturnArrayBufferViewTask, public DeferredData {
787 public:
RsaOaepTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)788 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
789 bool aEncrypt)
790 : mPrivKey(aKey.GetPrivateKey()),
791 mPubKey(aKey.GetPublicKey()),
792 mEncrypt(aEncrypt) {
793 Init(aCx, aAlgorithm, aKey, aEncrypt);
794 }
795
RsaOaepTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aData,bool aEncrypt)796 RsaOaepTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
797 const CryptoOperationData& aData, bool aEncrypt)
798 : mPrivKey(aKey.GetPrivateKey()),
799 mPubKey(aKey.GetPublicKey()),
800 mEncrypt(aEncrypt) {
801 Init(aCx, aAlgorithm, aKey, aEncrypt);
802 SetData(aData);
803 }
804
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,bool aEncrypt)805 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
806 bool aEncrypt) {
807 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_OAEP);
808
809 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_OAEP);
810
811 if (mEncrypt) {
812 if (!mPubKey) {
813 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
814 return;
815 }
816 mStrength = SECKEY_PublicKeyStrength(mPubKey.get());
817 } else {
818 if (!mPrivKey) {
819 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
820 return;
821 }
822 mStrength = PK11_GetPrivateModulusLen(mPrivKey.get());
823 }
824
825 // The algorithm could just be given as a string
826 // in which case there would be no label specified.
827 if (!aAlgorithm.IsString()) {
828 RootedDictionary<RsaOaepParams> params(aCx);
829 mEarlyRv = Coerce(aCx, params, aAlgorithm);
830 if (NS_FAILED(mEarlyRv)) {
831 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
832 return;
833 }
834
835 if (params.mLabel.WasPassed()) {
836 ATTEMPT_BUFFER_INIT(mLabel, params.mLabel.Value());
837 }
838 }
839 // Otherwise mLabel remains the empty octet string, as intended
840
841 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
842 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
843 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlg.mName);
844
845 // Check we found appropriate mechanisms.
846 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
847 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
848 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
849 return;
850 }
851 }
852
853 private:
854 CK_MECHANISM_TYPE mHashMechanism;
855 CK_MECHANISM_TYPE mMgfMechanism;
856 UniqueSECKEYPrivateKey mPrivKey;
857 UniqueSECKEYPublicKey mPubKey;
858 CryptoBuffer mLabel;
859 uint32_t mStrength;
860 bool mEncrypt;
861
DoCrypto()862 virtual nsresult DoCrypto() override {
863 nsresult rv;
864
865 if (!mDataIsSet) {
866 return NS_ERROR_DOM_OPERATION_ERR;
867 }
868
869 // Ciphertext is an integer mod the modulus, so it will be
870 // no longer than mStrength octets
871 if (!mResult.SetLength(mStrength, fallible)) {
872 return NS_ERROR_DOM_UNKNOWN_ERR;
873 }
874
875 CK_RSA_PKCS_OAEP_PARAMS oaepParams;
876 oaepParams.source = CKZ_DATA_SPECIFIED;
877
878 oaepParams.pSourceData = mLabel.Length() ? mLabel.Elements() : nullptr;
879 oaepParams.ulSourceDataLen = mLabel.Length();
880
881 oaepParams.mgf = mMgfMechanism;
882 oaepParams.hashAlg = mHashMechanism;
883
884 SECItem param;
885 param.type = siBuffer;
886 param.data = (unsigned char*)&oaepParams;
887 param.len = sizeof(oaepParams);
888
889 uint32_t outLen = 0;
890 if (mEncrypt) {
891 // PK11_PubEncrypt() checks the plaintext's length and fails if it is too
892 // long to encrypt, i.e. if it is longer than (k - 2hLen - 2) with 'k'
893 // being the length in octets of the RSA modulus n and 'hLen' being the
894 // output length in octets of the chosen hash function.
895 // <https://tools.ietf.org/html/rfc3447#section-7.1>
896 rv = MapSECStatus(PK11_PubEncrypt(
897 mPubKey.get(), CKM_RSA_PKCS_OAEP, ¶m, mResult.Elements(), &outLen,
898 mResult.Length(), mData.Elements(), mData.Length(), nullptr));
899 } else {
900 rv = MapSECStatus(PK11_PrivDecrypt(
901 mPrivKey.get(), CKM_RSA_PKCS_OAEP, ¶m, mResult.Elements(),
902 &outLen, mResult.Length(), mData.Elements(), mData.Length()));
903 }
904 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
905
906 mResult.TruncateLength(outLen);
907 return NS_OK;
908 }
909 };
910
911 class HmacTask : public WebCryptoTask {
912 public:
HmacTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aSignature,const CryptoOperationData & aData,bool aSign)913 HmacTask(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
914 const CryptoOperationData& aSignature,
915 const CryptoOperationData& aData, bool aSign)
916 : mMechanism(aKey.Algorithm().Mechanism()), mSign(aSign) {
917 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HMAC);
918
919 ATTEMPT_BUFFER_INIT(mData, aData);
920 if (!aSign) {
921 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
922 }
923
924 if (!mSymKey.Assign(aKey.GetSymKey())) {
925 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
926 return;
927 }
928
929 // Check that we got a symmetric key
930 if (mSymKey.Length() == 0) {
931 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
932 return;
933 }
934
935 TelemetryAlgorithm telemetryAlg;
936 switch (mMechanism) {
937 case CKM_SHA_1_HMAC:
938 telemetryAlg = TA_HMAC_SHA_1;
939 break;
940 case CKM_SHA224_HMAC:
941 telemetryAlg = TA_HMAC_SHA_224;
942 break;
943 case CKM_SHA256_HMAC:
944 telemetryAlg = TA_HMAC_SHA_256;
945 break;
946 case CKM_SHA384_HMAC:
947 telemetryAlg = TA_HMAC_SHA_384;
948 break;
949 case CKM_SHA512_HMAC:
950 telemetryAlg = TA_HMAC_SHA_512;
951 break;
952 default:
953 telemetryAlg = TA_UNKNOWN;
954 }
955 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
956 }
957
958 private:
959 CK_MECHANISM_TYPE mMechanism;
960 CryptoBuffer mSymKey;
961 CryptoBuffer mData;
962 CryptoBuffer mSignature;
963 CryptoBuffer mResult;
964 bool mSign;
965
DoCrypto()966 virtual nsresult DoCrypto() override {
967 // Initialize the output buffer
968 if (!mResult.SetLength(HASH_LENGTH_MAX, fallible)) {
969 return NS_ERROR_DOM_UNKNOWN_ERR;
970 }
971
972 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
973 if (!arena) {
974 return NS_ERROR_DOM_OPERATION_ERR;
975 }
976
977 // Import the key
978 uint32_t outLen;
979 SECItem keyItem = {siBuffer, nullptr, 0};
980 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
981 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
982 MOZ_ASSERT(slot.get());
983 UniquePK11SymKey symKey(PK11_ImportSymKey(slot.get(), mMechanism,
984 PK11_OriginUnwrap, CKA_SIGN,
985 &keyItem, nullptr));
986 if (!symKey) {
987 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
988 }
989
990 // Compute the MAC
991 SECItem param = {siBuffer, nullptr, 0};
992 UniquePK11Context ctx(
993 PK11_CreateContextBySymKey(mMechanism, CKA_SIGN, symKey.get(), ¶m));
994 if (!ctx.get()) {
995 return NS_ERROR_DOM_OPERATION_ERR;
996 }
997 nsresult rv = MapSECStatus(PK11_DigestBegin(ctx.get()));
998 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
999 rv = MapSECStatus(
1000 PK11_DigestOp(ctx.get(), mData.Elements(), mData.Length()));
1001 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1002 rv = MapSECStatus(PK11_DigestFinal(ctx.get(), mResult.Elements(), &outLen,
1003 mResult.Length()));
1004 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
1005
1006 mResult.TruncateLength(outLen);
1007 return rv;
1008 }
1009
1010 // Returns mResult as an ArrayBufferView, or an error
Resolve()1011 virtual void Resolve() override {
1012 if (mSign) {
1013 // Return the computed MAC
1014 TypedArrayCreator<ArrayBuffer> ret(mResult);
1015 mResultPromise->MaybeResolve(ret);
1016 } else {
1017 // Compare the MAC to the provided signature
1018 // No truncation allowed
1019 bool equal = (mResult.Length() == mSignature.Length());
1020 if (equal) {
1021 int cmp = NSS_SecureMemcmp(mSignature.Elements(), mResult.Elements(),
1022 mSignature.Length());
1023 equal = (cmp == 0);
1024 }
1025 mResultPromise->MaybeResolve(equal);
1026 }
1027 }
1028 };
1029
1030 class AsymmetricSignVerifyTask : public WebCryptoTask {
1031 public:
AsymmetricSignVerifyTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aSignature,const CryptoOperationData & aData,bool aSign)1032 AsymmetricSignVerifyTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1033 CryptoKey& aKey,
1034 const CryptoOperationData& aSignature,
1035 const CryptoOperationData& aData, bool aSign)
1036 : mOidTag(SEC_OID_UNKNOWN),
1037 mHashMechanism(UNKNOWN_CK_MECHANISM),
1038 mMgfMechanism(UNKNOWN_CK_MECHANISM),
1039 mPrivKey(aKey.GetPrivateKey()),
1040 mPubKey(aKey.GetPublicKey()),
1041 mSaltLength(0),
1042 mSign(aSign),
1043 mVerified(false),
1044 mAlgorithm(Algorithm::UNKNOWN) {
1045 ATTEMPT_BUFFER_INIT(mData, aData);
1046 if (!aSign) {
1047 ATTEMPT_BUFFER_INIT(mSignature, aSignature);
1048 }
1049
1050 nsString algName;
1051 nsString hashAlgName;
1052 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1053 if (NS_FAILED(mEarlyRv)) {
1054 return;
1055 }
1056
1057 if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
1058 mAlgorithm = Algorithm::RSA_PKCS1;
1059 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSASSA_PKCS1);
1060 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSASSA_PKCS1);
1061 hashAlgName = aKey.Algorithm().mRsa.mHash.mName;
1062 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1063 mAlgorithm = Algorithm::RSA_PSS;
1064 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_RSA_PSS);
1065 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_RSA_PSS);
1066
1067 KeyAlgorithm& hashAlg = aKey.Algorithm().mRsa.mHash;
1068 hashAlgName = hashAlg.mName;
1069 mHashMechanism = KeyAlgorithmProxy::GetMechanism(hashAlg);
1070 mMgfMechanism = MapHashAlgorithmNameToMgfMechanism(hashAlgName);
1071
1072 // Check we found appropriate mechanisms.
1073 if (mHashMechanism == UNKNOWN_CK_MECHANISM ||
1074 mMgfMechanism == UNKNOWN_CK_MECHANISM) {
1075 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1076 return;
1077 }
1078
1079 RootedDictionary<RsaPssParams> params(aCx);
1080 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1081 if (NS_FAILED(mEarlyRv)) {
1082 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1083 return;
1084 }
1085
1086 mSaltLength = params.mSaltLength;
1087 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1088 mAlgorithm = Algorithm::ECDSA;
1089 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDSA);
1090 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDSA);
1091
1092 // For ECDSA, the hash name comes from the algorithm parameter
1093 RootedDictionary<EcdsaParams> params(aCx);
1094 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1095 if (NS_FAILED(mEarlyRv)) {
1096 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1097 return;
1098 }
1099
1100 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashAlgName);
1101 if (NS_FAILED(mEarlyRv)) {
1102 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1103 return;
1104 }
1105 } else {
1106 // This shouldn't happen; CreateSignVerifyTask shouldn't create
1107 // one of these unless it's for the above algorithms.
1108 MOZ_ASSERT(false);
1109 }
1110
1111 // Must have a valid algorithm by now.
1112 MOZ_ASSERT(mAlgorithm != Algorithm::UNKNOWN);
1113
1114 // Determine hash algorithm to use.
1115 mOidTag = MapHashAlgorithmNameToOID(hashAlgName);
1116 if (mOidTag == SEC_OID_UNKNOWN) {
1117 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1118 return;
1119 }
1120
1121 // Check that we have the appropriate key
1122 if ((mSign && !mPrivKey) || (!mSign && !mPubKey)) {
1123 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
1124 return;
1125 }
1126 }
1127
1128 private:
1129 SECOidTag mOidTag;
1130 CK_MECHANISM_TYPE mHashMechanism;
1131 CK_MECHANISM_TYPE mMgfMechanism;
1132 UniqueSECKEYPrivateKey mPrivKey;
1133 UniqueSECKEYPublicKey mPubKey;
1134 CryptoBuffer mSignature;
1135 CryptoBuffer mData;
1136 uint32_t mSaltLength;
1137 bool mSign;
1138 bool mVerified;
1139
1140 // The signature algorithm to use.
1141 enum class Algorithm : uint8_t { ECDSA, RSA_PKCS1, RSA_PSS, UNKNOWN };
1142 Algorithm mAlgorithm;
1143
DoCrypto()1144 virtual nsresult DoCrypto() override {
1145 SECStatus rv;
1146 UniqueSECItem hash(
1147 ::SECITEM_AllocItem(nullptr, nullptr, HASH_ResultLenByOidTag(mOidTag)));
1148 if (!hash) {
1149 return NS_ERROR_DOM_OPERATION_ERR;
1150 }
1151
1152 // Compute digest over given data.
1153 rv = PK11_HashBuf(mOidTag, hash->data, mData.Elements(), mData.Length());
1154 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1155
1156 // Wrap hash in a digest info template (RSA-PKCS1 only).
1157 if (mAlgorithm == Algorithm::RSA_PKCS1) {
1158 UniqueSGNDigestInfo di(
1159 SGN_CreateDigestInfo(mOidTag, hash->data, hash->len));
1160 if (!di) {
1161 return NS_ERROR_DOM_OPERATION_ERR;
1162 }
1163
1164 // Reuse |hash|.
1165 SECITEM_FreeItem(hash.get(), false);
1166 if (!SEC_ASN1EncodeItem(nullptr, hash.get(), di.get(),
1167 SGN_DigestInfoTemplate)) {
1168 return NS_ERROR_DOM_OPERATION_ERR;
1169 }
1170 }
1171
1172 SECItem* params = nullptr;
1173 CK_MECHANISM_TYPE mech =
1174 PK11_MapSignKeyType((mSign ? mPrivKey->keyType : mPubKey->keyType));
1175
1176 CK_RSA_PKCS_PSS_PARAMS rsaPssParams;
1177 SECItem rsaPssParamsItem = {
1178 siBuffer,
1179 };
1180
1181 // Set up parameters for RSA-PSS.
1182 if (mAlgorithm == Algorithm::RSA_PSS) {
1183 rsaPssParams.hashAlg = mHashMechanism;
1184 rsaPssParams.mgf = mMgfMechanism;
1185 rsaPssParams.sLen = mSaltLength;
1186
1187 rsaPssParamsItem.data = (unsigned char*)&rsaPssParams;
1188 rsaPssParamsItem.len = sizeof(rsaPssParams);
1189 params = &rsaPssParamsItem;
1190
1191 mech = CKM_RSA_PKCS_PSS;
1192 }
1193
1194 // Allocate SECItem to hold the signature.
1195 uint32_t len = mSign ? PK11_SignatureLen(mPrivKey.get()) : 0;
1196 UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
1197 if (!sig) {
1198 return NS_ERROR_DOM_OPERATION_ERR;
1199 }
1200
1201 if (mSign) {
1202 // Sign the hash.
1203 rv = PK11_SignWithMechanism(mPrivKey.get(), mech, params, sig.get(),
1204 hash.get());
1205 NS_ENSURE_SUCCESS(MapSECStatus(rv), NS_ERROR_DOM_OPERATION_ERR);
1206 ATTEMPT_BUFFER_ASSIGN(mSignature, sig.get());
1207 } else {
1208 // Copy the given signature to the SECItem.
1209 if (!mSignature.ToSECItem(nullptr, sig.get())) {
1210 return NS_ERROR_DOM_OPERATION_ERR;
1211 }
1212
1213 // Verify the signature.
1214 rv = PK11_VerifyWithMechanism(mPubKey.get(), mech, params, sig.get(),
1215 hash.get(), nullptr);
1216 mVerified = NS_SUCCEEDED(MapSECStatus(rv));
1217 }
1218
1219 return NS_OK;
1220 }
1221
Resolve()1222 virtual void Resolve() override {
1223 if (mSign) {
1224 TypedArrayCreator<ArrayBuffer> ret(mSignature);
1225 mResultPromise->MaybeResolve(ret);
1226 } else {
1227 mResultPromise->MaybeResolve(mVerified);
1228 }
1229 }
1230 };
1231
1232 class DigestTask : public ReturnArrayBufferViewTask {
1233 public:
DigestTask(JSContext * aCx,const ObjectOrString & aAlgorithm,const CryptoOperationData & aData)1234 DigestTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
1235 const CryptoOperationData& aData) {
1236 ATTEMPT_BUFFER_INIT(mData, aData);
1237
1238 nsString algName;
1239 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
1240 if (NS_FAILED(mEarlyRv)) {
1241 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1242 return;
1243 }
1244
1245 TelemetryAlgorithm telemetryAlg;
1246 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
1247 telemetryAlg = TA_SHA_1;
1248 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
1249 telemetryAlg = TA_SHA_224;
1250 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
1251 telemetryAlg = TA_SHA_256;
1252 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
1253 telemetryAlg = TA_SHA_384;
1254 } else {
1255 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1256 return;
1257 }
1258 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, telemetryAlg);
1259 mOidTag = MapHashAlgorithmNameToOID(algName);
1260 }
1261
1262 private:
1263 SECOidTag mOidTag;
1264 CryptoBuffer mData;
1265
DoCrypto()1266 virtual nsresult DoCrypto() override {
1267 // Resize the result buffer
1268 uint32_t hashLen = HASH_ResultLenByOidTag(mOidTag);
1269 if (!mResult.SetLength(hashLen, fallible)) {
1270 return NS_ERROR_DOM_UNKNOWN_ERR;
1271 }
1272
1273 // Compute the hash
1274 nsresult rv = MapSECStatus(PK11_HashBuf(mOidTag, mResult.Elements(),
1275 mData.Elements(), mData.Length()));
1276 if (NS_FAILED(rv)) {
1277 return NS_ERROR_DOM_UNKNOWN_ERR;
1278 }
1279
1280 return rv;
1281 }
1282 };
1283
1284 class ImportKeyTask : public WebCryptoTask {
1285 public:
Init(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1286 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1287 const ObjectOrString& aAlgorithm, bool aExtractable,
1288 const Sequence<nsString>& aKeyUsages) {
1289 mFormat = aFormat;
1290 mDataIsSet = false;
1291 mDataIsJwk = false;
1292
1293 // This stuff pretty much always happens, so we'll do it here
1294 mKey = new CryptoKey(aGlobal);
1295 mKey->SetExtractable(aExtractable);
1296 mKey->ClearUsages();
1297 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
1298 mEarlyRv = mKey->AddUsage(aKeyUsages[i]);
1299 if (NS_FAILED(mEarlyRv)) {
1300 return;
1301 }
1302 }
1303
1304 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
1305 if (NS_FAILED(mEarlyRv)) {
1306 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1307 return;
1308 }
1309 }
1310
JwkCompatible(const JsonWebKey & aJwk,const CryptoKey * aKey)1311 static bool JwkCompatible(const JsonWebKey& aJwk, const CryptoKey* aKey) {
1312 // Check 'ext'
1313 if (aKey->Extractable() && aJwk.mExt.WasPassed() && !aJwk.mExt.Value()) {
1314 return false;
1315 }
1316
1317 // Check 'alg'
1318 if (aJwk.mAlg.WasPassed() &&
1319 aJwk.mAlg.Value() != aKey->Algorithm().JwkAlg()) {
1320 return false;
1321 }
1322
1323 // Check 'key_ops'
1324 if (aJwk.mKey_ops.WasPassed()) {
1325 nsTArray<nsString> usages;
1326 aKey->GetUsages(usages);
1327 for (size_t i = 0; i < usages.Length(); ++i) {
1328 if (!aJwk.mKey_ops.Value().Contains(usages[i])) {
1329 return false;
1330 }
1331 }
1332 }
1333
1334 // Individual algorithms may still have to check 'use'
1335 return true;
1336 }
1337
SetKeyData(JSContext * aCx,JS::Handle<JSObject * > aKeyData)1338 void SetKeyData(JSContext* aCx, JS::Handle<JSObject*> aKeyData) {
1339 mDataIsJwk = false;
1340
1341 // Try ArrayBuffer
1342 RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
1343 if (ab.Init(aKeyData)) {
1344 if (!mKeyData.Assign(ab)) {
1345 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1346 }
1347 return;
1348 }
1349
1350 // Try ArrayBufferView
1351 RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
1352 if (abv.Init(aKeyData)) {
1353 if (!mKeyData.Assign(abv)) {
1354 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1355 }
1356 return;
1357 }
1358
1359 // Try JWK
1360 ClearException ce(aCx);
1361 JS::RootedValue value(aCx, JS::ObjectValue(*aKeyData));
1362 if (!mJwk.Init(aCx, value)) {
1363 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1364 return;
1365 }
1366
1367 mDataIsJwk = true;
1368 }
1369
SetKeyDataMaybeParseJWK(const CryptoBuffer & aKeyData)1370 void SetKeyDataMaybeParseJWK(const CryptoBuffer& aKeyData) {
1371 if (!mKeyData.Assign(aKeyData)) {
1372 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1373 return;
1374 }
1375
1376 mDataIsJwk = false;
1377
1378 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1379 nsDependentCSubstring utf8(
1380 (const char*)mKeyData.Elements(),
1381 (const char*)(mKeyData.Elements() + mKeyData.Length()));
1382 if (!IsUtf8(utf8)) {
1383 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1384 return;
1385 }
1386
1387 nsString json = NS_ConvertUTF8toUTF16(utf8);
1388 if (!mJwk.Init(json)) {
1389 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1390 return;
1391 }
1392
1393 mDataIsJwk = true;
1394 }
1395 }
1396
SetRawKeyData(const CryptoBuffer & aKeyData)1397 void SetRawKeyData(const CryptoBuffer& aKeyData) {
1398 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1399 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1400 return;
1401 }
1402
1403 if (!mKeyData.Assign(aKeyData)) {
1404 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
1405 return;
1406 }
1407
1408 mDataIsJwk = false;
1409 }
1410
1411 protected:
1412 nsString mFormat;
1413 RefPtr<CryptoKey> mKey;
1414 CryptoBuffer mKeyData;
1415 bool mDataIsSet;
1416 bool mDataIsJwk;
1417 JsonWebKey mJwk;
1418 nsString mAlgName;
1419
1420 private:
Resolve()1421 virtual void Resolve() override { mResultPromise->MaybeResolve(mKey); }
1422
Cleanup()1423 virtual void Cleanup() override { mKey = nullptr; }
1424 };
1425
1426 class ImportSymmetricKeyTask : public ImportKeyTask {
1427 public:
ImportSymmetricKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1428 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1429 const nsAString& aFormat,
1430 const ObjectOrString& aAlgorithm, bool aExtractable,
1431 const Sequence<nsString>& aKeyUsages) {
1432 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1433 }
1434
ImportSymmetricKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const JS::Handle<JSObject * > aKeyData,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1435 ImportSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1436 const nsAString& aFormat,
1437 const JS::Handle<JSObject*> aKeyData,
1438 const ObjectOrString& aAlgorithm, bool aExtractable,
1439 const Sequence<nsString>& aKeyUsages) {
1440 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1441 if (NS_FAILED(mEarlyRv)) {
1442 return;
1443 }
1444
1445 SetKeyData(aCx, aKeyData);
1446 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1447 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1448 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1449 return;
1450 }
1451 }
1452
Init(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1453 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1454 const ObjectOrString& aAlgorithm, bool aExtractable,
1455 const Sequence<nsString>& aKeyUsages) {
1456 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1457 aKeyUsages);
1458 if (NS_FAILED(mEarlyRv)) {
1459 return;
1460 }
1461
1462 // This task only supports raw and JWK format.
1463 if (!mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1464 !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1465 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1466 return;
1467 }
1468
1469 // If this is an HMAC key, import the hash name
1470 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1471 RootedDictionary<HmacImportParams> params(aCx);
1472 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1473 if (NS_FAILED(mEarlyRv)) {
1474 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1475 return;
1476 }
1477 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1478 if (NS_FAILED(mEarlyRv)) {
1479 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1480 return;
1481 }
1482 }
1483 }
1484
BeforeCrypto()1485 virtual nsresult BeforeCrypto() override {
1486 nsresult rv;
1487
1488 // If we're doing a JWK import, import the key data
1489 if (mDataIsJwk) {
1490 if (!mJwk.mK.WasPassed()) {
1491 return NS_ERROR_DOM_DATA_ERR;
1492 }
1493
1494 // Import the key material
1495 rv = mKeyData.FromJwkBase64(mJwk.mK.Value());
1496 if (NS_FAILED(rv)) {
1497 return NS_ERROR_DOM_DATA_ERR;
1498 }
1499 }
1500 // Check that we have valid key data.
1501 if (mKeyData.Length() == 0 &&
1502 !mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1503 return NS_ERROR_DOM_DATA_ERR;
1504 }
1505
1506 // Construct an appropriate KeyAlorithm,
1507 // and verify that usages are appropriate
1508 uint32_t length = 8 * mKeyData.Length(); // bytes to bits
1509 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
1510 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
1511 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
1512 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
1513 if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
1514 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1515 return NS_ERROR_DOM_DATA_ERR;
1516 }
1517
1518 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) &&
1519 mKey->HasUsageOtherThan(CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY)) {
1520 return NS_ERROR_DOM_DATA_ERR;
1521 }
1522
1523 if ((length != 128) && (length != 192) && (length != 256)) {
1524 return NS_ERROR_DOM_DATA_ERR;
1525 }
1526 mKey->Algorithm().MakeAes(mAlgName, length);
1527
1528 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1529 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_ENC)) {
1530 return NS_ERROR_DOM_DATA_ERR;
1531 }
1532 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
1533 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
1534 if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY |
1535 CryptoKey::DERIVEBITS)) {
1536 return NS_ERROR_DOM_DATA_ERR;
1537 }
1538 mKey->Algorithm().MakeAes(mAlgName, length);
1539
1540 if (mDataIsJwk && mJwk.mUse.WasPassed()) {
1541 // There is not a 'use' value consistent with PBKDF or HKDF
1542 return NS_ERROR_DOM_DATA_ERR;
1543 };
1544 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
1545 if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
1546 return NS_ERROR_DOM_DATA_ERR;
1547 }
1548
1549 mKey->Algorithm().MakeHmac(length, mHashName);
1550
1551 if (mKey->Algorithm().Mechanism() == UNKNOWN_CK_MECHANISM) {
1552 return NS_ERROR_DOM_SYNTAX_ERR;
1553 }
1554
1555 if (mDataIsJwk && mJwk.mUse.WasPassed() &&
1556 !mJwk.mUse.Value().EqualsLiteral(JWK_USE_SIG)) {
1557 return NS_ERROR_DOM_DATA_ERR;
1558 }
1559 } else {
1560 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1561 }
1562
1563 if (NS_FAILED(mKey->SetSymKey(mKeyData))) {
1564 return NS_ERROR_DOM_OPERATION_ERR;
1565 }
1566
1567 mKey->SetType(CryptoKey::SECRET);
1568
1569 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1570 return NS_ERROR_DOM_DATA_ERR;
1571 }
1572
1573 mEarlyComplete = true;
1574 return NS_OK;
1575 }
1576
1577 private:
1578 nsString mHashName;
1579 };
1580
1581 class ImportRsaKeyTask : public ImportKeyTask {
1582 public:
ImportRsaKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1583 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1584 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1585 bool aExtractable, const Sequence<nsString>& aKeyUsages)
1586 : mModulusLength(0) {
1587 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1588 }
1589
ImportRsaKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,JS::Handle<JSObject * > aKeyData,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1590 ImportRsaKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1591 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1592 const ObjectOrString& aAlgorithm, bool aExtractable,
1593 const Sequence<nsString>& aKeyUsages)
1594 : mModulusLength(0) {
1595 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1596 if (NS_FAILED(mEarlyRv)) {
1597 return;
1598 }
1599
1600 SetKeyData(aCx, aKeyData);
1601 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1602 if (mDataIsJwk && !mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1603 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1604 return;
1605 }
1606 }
1607
Init(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1608 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1609 const ObjectOrString& aAlgorithm, bool aExtractable,
1610 const Sequence<nsString>& aKeyUsages) {
1611 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1612 aKeyUsages);
1613 if (NS_FAILED(mEarlyRv)) {
1614 return;
1615 }
1616
1617 // If this is RSA with a hash, cache the hash name
1618 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1619 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
1620 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1621 RootedDictionary<RsaHashedImportParams> params(aCx);
1622 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1623 if (NS_FAILED(mEarlyRv)) {
1624 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1625 return;
1626 }
1627
1628 mEarlyRv = GetAlgorithmName(aCx, params.mHash, mHashName);
1629 if (NS_FAILED(mEarlyRv)) {
1630 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
1631 return;
1632 }
1633 }
1634
1635 // Check support for the algorithm and hash names
1636 CK_MECHANISM_TYPE mech1 = MapAlgorithmNameToMechanism(mAlgName);
1637 CK_MECHANISM_TYPE mech2 = MapAlgorithmNameToMechanism(mHashName);
1638 if ((mech1 == UNKNOWN_CK_MECHANISM) || (mech2 == UNKNOWN_CK_MECHANISM)) {
1639 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1640 return;
1641 }
1642 }
1643
1644 private:
1645 nsString mHashName;
1646 uint32_t mModulusLength;
1647 CryptoBuffer mPublicExponent;
1648
DoCrypto()1649 virtual nsresult DoCrypto() override {
1650 // Import the key data itself
1651 UniqueSECKEYPublicKey pubKey;
1652 UniqueSECKEYPrivateKey privKey;
1653 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1654 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1655 !mJwk.mD.WasPassed())) {
1656 // Public key import
1657 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1658 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1659 } else {
1660 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1661 }
1662
1663 if (!pubKey) {
1664 return NS_ERROR_DOM_DATA_ERR;
1665 }
1666
1667 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1668 return NS_ERROR_DOM_OPERATION_ERR;
1669 }
1670
1671 mKey->SetType(CryptoKey::PUBLIC);
1672 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) ||
1673 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1674 mJwk.mD.WasPassed())) {
1675 // Private key import
1676 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1677 privKey = CryptoKey::PrivateKeyFromPkcs8(mKeyData);
1678 } else {
1679 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1680 }
1681
1682 if (!privKey) {
1683 return NS_ERROR_DOM_DATA_ERR;
1684 }
1685
1686 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1687 return NS_ERROR_DOM_OPERATION_ERR;
1688 }
1689
1690 mKey->SetType(CryptoKey::PRIVATE);
1691 pubKey = UniqueSECKEYPublicKey(SECKEY_ConvertToPublicKey(privKey.get()));
1692 if (!pubKey) {
1693 return NS_ERROR_DOM_UNKNOWN_ERR;
1694 }
1695 } else {
1696 // Invalid key format
1697 return NS_ERROR_DOM_SYNTAX_ERR;
1698 }
1699
1700 // Extract relevant information from the public key
1701 mModulusLength = 8 * pubKey->u.rsa.modulus.len;
1702 if (!mPublicExponent.Assign(&pubKey->u.rsa.publicExponent)) {
1703 return NS_ERROR_DOM_OPERATION_ERR;
1704 }
1705
1706 return NS_OK;
1707 }
1708
AfterCrypto()1709 virtual nsresult AfterCrypto() override {
1710 // Check permissions for the requested operation
1711 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
1712 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1713 mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::WRAPKEY)) ||
1714 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1715 mKey->HasUsageOtherThan(CryptoKey::DECRYPT |
1716 CryptoKey::UNWRAPKEY))) {
1717 return NS_ERROR_DOM_DATA_ERR;
1718 }
1719 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
1720 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
1721 if ((mKey->GetKeyType() == CryptoKey::PUBLIC &&
1722 mKey->HasUsageOtherThan(CryptoKey::VERIFY)) ||
1723 (mKey->GetKeyType() == CryptoKey::PRIVATE &&
1724 mKey->HasUsageOtherThan(CryptoKey::SIGN))) {
1725 return NS_ERROR_DOM_DATA_ERR;
1726 }
1727 }
1728
1729 // Set an appropriate KeyAlgorithm
1730 if (!mKey->Algorithm().MakeRsa(mAlgName, mModulusLength, mPublicExponent,
1731 mHashName)) {
1732 return NS_ERROR_DOM_OPERATION_ERR;
1733 }
1734
1735 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1736 return NS_ERROR_DOM_DATA_ERR;
1737 }
1738
1739 return NS_OK;
1740 }
1741 };
1742
1743 class ImportEcKeyTask : public ImportKeyTask {
1744 public:
ImportEcKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1745 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1746 const nsAString& aFormat, const ObjectOrString& aAlgorithm,
1747 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
1748 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1749 }
1750
ImportEcKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,JS::Handle<JSObject * > aKeyData,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1751 ImportEcKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
1752 const nsAString& aFormat, JS::Handle<JSObject*> aKeyData,
1753 const ObjectOrString& aAlgorithm, bool aExtractable,
1754 const Sequence<nsString>& aKeyUsages) {
1755 Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable, aKeyUsages);
1756 if (NS_FAILED(mEarlyRv)) {
1757 return;
1758 }
1759
1760 SetKeyData(aCx, aKeyData);
1761 NS_ENSURE_SUCCESS_VOID(mEarlyRv);
1762 }
1763
Init(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)1764 void Init(nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
1765 const ObjectOrString& aAlgorithm, bool aExtractable,
1766 const Sequence<nsString>& aKeyUsages) {
1767 ImportKeyTask::Init(aGlobal, aCx, aFormat, aAlgorithm, aExtractable,
1768 aKeyUsages);
1769 if (NS_FAILED(mEarlyRv)) {
1770 return;
1771 }
1772
1773 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1774 RootedDictionary<EcKeyImportParams> params(aCx);
1775 mEarlyRv = Coerce(aCx, params, aAlgorithm);
1776 if (NS_FAILED(mEarlyRv) || !params.mNamedCurve.WasPassed()) {
1777 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
1778 return;
1779 }
1780
1781 if (!NormalizeToken(params.mNamedCurve.Value(), mNamedCurve)) {
1782 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1783 return;
1784 }
1785 }
1786 }
1787
1788 private:
1789 nsString mNamedCurve;
1790
DoCrypto()1791 virtual nsresult DoCrypto() override {
1792 // Import the key data itself
1793 UniqueSECKEYPublicKey pubKey;
1794 UniqueSECKEYPrivateKey privKey;
1795
1796 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1797 mJwk.mD.WasPassed()) {
1798 // Private key import
1799 privKey = CryptoKey::PrivateKeyFromJwk(mJwk);
1800 if (!privKey) {
1801 return NS_ERROR_DOM_DATA_ERR;
1802 }
1803
1804 if (NS_FAILED(mKey->SetPrivateKey(privKey.get()))) {
1805 return NS_ERROR_DOM_OPERATION_ERR;
1806 }
1807
1808 mKey->SetType(CryptoKey::PRIVATE);
1809 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) ||
1810 mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) ||
1811 (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK) &&
1812 !mJwk.mD.WasPassed())) {
1813 // Public key import
1814 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1815 pubKey = CryptoKey::PublicECKeyFromRaw(mKeyData, mNamedCurve);
1816 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1817 pubKey = CryptoKey::PublicKeyFromSpki(mKeyData);
1818 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1819 pubKey = CryptoKey::PublicKeyFromJwk(mJwk);
1820 } else {
1821 MOZ_ASSERT(false);
1822 }
1823
1824 if (!pubKey) {
1825 return NS_ERROR_DOM_DATA_ERR;
1826 }
1827
1828 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1829 if (!CheckEncodedECParameters(&pubKey->u.ec.DEREncodedParams)) {
1830 return NS_ERROR_DOM_OPERATION_ERR;
1831 }
1832
1833 // Construct the OID tag.
1834 SECItem oid = {siBuffer, nullptr, 0};
1835 oid.len = pubKey->u.ec.DEREncodedParams.data[1];
1836 oid.data = pubKey->u.ec.DEREncodedParams.data + 2;
1837
1838 // Find a matching and supported named curve.
1839 if (!MapOIDTagToNamedCurve(SECOID_FindOIDTag(&oid), mNamedCurve)) {
1840 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1841 }
1842 }
1843
1844 if (NS_FAILED(mKey->SetPublicKey(pubKey.get()))) {
1845 return NS_ERROR_DOM_OPERATION_ERR;
1846 }
1847
1848 mKey->SetType(CryptoKey::PUBLIC);
1849 } else {
1850 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1851 }
1852
1853 // Extract 'crv' parameter from JWKs.
1854 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1855 if (!NormalizeToken(mJwk.mCrv.Value(), mNamedCurve)) {
1856 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1857 }
1858 }
1859
1860 return NS_OK;
1861 }
1862
AfterCrypto()1863 virtual nsresult AfterCrypto() override {
1864 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
1865 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
1866 privateAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1867 publicAllowedUsages = CryptoKey::DERIVEBITS | CryptoKey::DERIVEKEY;
1868 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
1869 privateAllowedUsages = CryptoKey::SIGN;
1870 publicAllowedUsages = CryptoKey::VERIFY;
1871 }
1872
1873 // Check permissions for the requested operation
1874 if ((mKey->GetKeyType() == CryptoKey::PRIVATE &&
1875 mKey->HasUsageOtherThan(privateAllowedUsages)) ||
1876 (mKey->GetKeyType() == CryptoKey::PUBLIC &&
1877 mKey->HasUsageOtherThan(publicAllowedUsages))) {
1878 return NS_ERROR_DOM_DATA_ERR;
1879 }
1880
1881 mKey->Algorithm().MakeEc(mAlgName, mNamedCurve);
1882
1883 if (mDataIsJwk && !JwkCompatible(mJwk, mKey)) {
1884 return NS_ERROR_DOM_DATA_ERR;
1885 }
1886
1887 return NS_OK;
1888 }
1889 };
1890
1891 class ExportKeyTask : public WebCryptoTask {
1892 public:
ExportKeyTask(const nsAString & aFormat,CryptoKey & aKey)1893 ExportKeyTask(const nsAString& aFormat, CryptoKey& aKey)
1894 : mFormat(aFormat),
1895 mPrivateKey(aKey.GetPrivateKey()),
1896 mPublicKey(aKey.GetPublicKey()),
1897 mKeyType(aKey.GetKeyType()),
1898 mExtractable(aKey.Extractable()),
1899 mAlg(aKey.Algorithm().JwkAlg()) {
1900 aKey.GetUsages(mKeyUsages);
1901
1902 if (!mSymKey.Assign(aKey.GetSymKey())) {
1903 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
1904 return;
1905 }
1906 }
1907
1908 protected:
1909 nsString mFormat;
1910 CryptoBuffer mSymKey;
1911 UniqueSECKEYPrivateKey mPrivateKey;
1912 UniqueSECKEYPublicKey mPublicKey;
1913 CryptoKey::KeyType mKeyType;
1914 bool mExtractable;
1915 nsString mAlg;
1916 nsTArray<nsString> mKeyUsages;
1917 CryptoBuffer mResult;
1918 JsonWebKey mJwk;
1919
1920 private:
DoCrypto()1921 virtual nsresult DoCrypto() override {
1922 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
1923 if (mPublicKey && mPublicKey->keyType == dhKey) {
1924 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1925 }
1926
1927 if (mPublicKey && mPublicKey->keyType == ecKey) {
1928 nsresult rv = CryptoKey::PublicECKeyToRaw(mPublicKey.get(), mResult);
1929 if (NS_FAILED(rv)) {
1930 return NS_ERROR_DOM_OPERATION_ERR;
1931 }
1932 return NS_OK;
1933 }
1934
1935 if (!mResult.Assign(mSymKey)) {
1936 return NS_ERROR_OUT_OF_MEMORY;
1937 }
1938 if (mResult.Length() == 0) {
1939 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1940 }
1941
1942 return NS_OK;
1943 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8)) {
1944 if (!mPrivateKey) {
1945 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1946 }
1947
1948 switch (mPrivateKey->keyType) {
1949 case rsaKey: {
1950 nsresult rv =
1951 CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), mResult);
1952 if (NS_FAILED(rv)) {
1953 return NS_ERROR_DOM_OPERATION_ERR;
1954 }
1955 return NS_OK;
1956 }
1957 default:
1958 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1959 }
1960 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI)) {
1961 if (!mPublicKey) {
1962 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1963 }
1964
1965 return CryptoKey::PublicKeyToSpki(mPublicKey.get(), mResult);
1966 } else if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
1967 if (mKeyType == CryptoKey::SECRET) {
1968 nsString k;
1969 nsresult rv = mSymKey.ToJwkBase64(k);
1970 if (NS_FAILED(rv)) {
1971 return NS_ERROR_DOM_OPERATION_ERR;
1972 }
1973 mJwk.mK.Construct(k);
1974 mJwk.mKty = NS_LITERAL_STRING(JWK_TYPE_SYMMETRIC);
1975 } else if (mKeyType == CryptoKey::PUBLIC) {
1976 if (!mPublicKey) {
1977 return NS_ERROR_DOM_UNKNOWN_ERR;
1978 }
1979
1980 nsresult rv = CryptoKey::PublicKeyToJwk(mPublicKey.get(), mJwk);
1981 if (NS_FAILED(rv)) {
1982 return NS_ERROR_DOM_OPERATION_ERR;
1983 }
1984 } else if (mKeyType == CryptoKey::PRIVATE) {
1985 if (!mPrivateKey) {
1986 return NS_ERROR_DOM_UNKNOWN_ERR;
1987 }
1988
1989 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), mJwk);
1990 if (NS_FAILED(rv)) {
1991 return NS_ERROR_DOM_OPERATION_ERR;
1992 }
1993 }
1994
1995 if (!mAlg.IsEmpty()) {
1996 mJwk.mAlg.Construct(mAlg);
1997 }
1998
1999 mJwk.mExt.Construct(mExtractable);
2000
2001 mJwk.mKey_ops.Construct();
2002 if (!mJwk.mKey_ops.Value().AppendElements(mKeyUsages, fallible)) {
2003 return NS_ERROR_OUT_OF_MEMORY;
2004 }
2005
2006 return NS_OK;
2007 }
2008
2009 return NS_ERROR_DOM_SYNTAX_ERR;
2010 }
2011
2012 // Returns mResult as an ArrayBufferView or JWK, as appropriate
Resolve()2013 virtual void Resolve() override {
2014 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2015 mResultPromise->MaybeResolve(mJwk);
2016 return;
2017 }
2018
2019 TypedArrayCreator<ArrayBuffer> ret(mResult);
2020 mResultPromise->MaybeResolve(ret);
2021 }
2022 };
2023
2024 class GenerateSymmetricKeyTask : public WebCryptoTask {
2025 public:
GenerateSymmetricKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)2026 GenerateSymmetricKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2027 const ObjectOrString& aAlgorithm, bool aExtractable,
2028 const Sequence<nsString>& aKeyUsages) {
2029 // Create an empty key and set easy attributes
2030 mKey = new CryptoKey(aGlobal);
2031 mKey->SetExtractable(aExtractable);
2032 mKey->SetType(CryptoKey::SECRET);
2033
2034 // Extract algorithm name
2035 nsString algName;
2036 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, algName);
2037 if (NS_FAILED(mEarlyRv)) {
2038 return;
2039 }
2040
2041 // Construct an appropriate KeyAlorithm
2042 uint32_t allowedUsages = 0;
2043 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2044 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2045 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2046 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
2047 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aAlgorithm, mLength);
2048 if (NS_FAILED(mEarlyRv)) {
2049 return;
2050 }
2051 mKey->Algorithm().MakeAes(algName, mLength);
2052
2053 allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT |
2054 CryptoKey::WRAPKEY | CryptoKey::UNWRAPKEY;
2055 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2056 RootedDictionary<HmacKeyGenParams> params(aCx);
2057 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2058 if (NS_FAILED(mEarlyRv)) {
2059 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2060 return;
2061 }
2062
2063 nsString hashName;
2064 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2065 if (NS_FAILED(mEarlyRv)) {
2066 return;
2067 }
2068
2069 if (params.mLength.WasPassed()) {
2070 mLength = params.mLength.Value();
2071 } else {
2072 mLength = MapHashAlgorithmNameToBlockSize(hashName);
2073 }
2074
2075 if (mLength == 0) {
2076 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2077 return;
2078 }
2079
2080 mKey->Algorithm().MakeHmac(mLength, hashName);
2081 allowedUsages = CryptoKey::SIGN | CryptoKey::VERIFY;
2082 } else {
2083 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2084 return;
2085 }
2086
2087 // Add key usages
2088 mKey->ClearUsages();
2089 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2090 mEarlyRv = mKey->AddUsageIntersecting(aKeyUsages[i], allowedUsages);
2091 if (NS_FAILED(mEarlyRv)) {
2092 return;
2093 }
2094 }
2095
2096 mLength = mLength >> 3; // bits to bytes
2097 mMechanism = mKey->Algorithm().Mechanism();
2098 // SetSymKey done in Resolve, after we've done the keygen
2099 }
2100
2101 private:
2102 RefPtr<CryptoKey> mKey;
2103 size_t mLength;
2104 CK_MECHANISM_TYPE mMechanism;
2105 CryptoBuffer mKeyData;
2106
DoCrypto()2107 virtual nsresult DoCrypto() override {
2108 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2109 MOZ_ASSERT(slot.get());
2110
2111 UniquePK11SymKey symKey(
2112 PK11_KeyGen(slot.get(), mMechanism, nullptr, mLength, nullptr));
2113 if (!symKey) {
2114 return NS_ERROR_DOM_UNKNOWN_ERR;
2115 }
2116
2117 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2118 if (NS_FAILED(rv)) {
2119 return NS_ERROR_DOM_UNKNOWN_ERR;
2120 }
2121
2122 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2123 // just refers to a buffer managed by symKey. The assignment copies the
2124 // data, so mKeyData manages one copy, while symKey manages another.
2125 ATTEMPT_BUFFER_ASSIGN(mKeyData, PK11_GetKeyData(symKey.get()));
2126 return NS_OK;
2127 }
2128
Resolve()2129 virtual void Resolve() override {
2130 if (NS_SUCCEEDED(mKey->SetSymKey(mKeyData))) {
2131 mResultPromise->MaybeResolve(mKey);
2132 } else {
2133 mResultPromise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
2134 }
2135 }
2136
Cleanup()2137 virtual void Cleanup() override { mKey = nullptr; }
2138 };
2139
GenerateAsymmetricKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)2140 GenerateAsymmetricKeyTask::GenerateAsymmetricKeyTask(
2141 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
2142 bool aExtractable, const Sequence<nsString>& aKeyUsages)
2143 : mKeyPair(new CryptoKeyPair()),
2144 mMechanism(CKM_INVALID_MECHANISM),
2145 mRsaParams(),
2146 mDhParams() {
2147 mArena = UniquePLArenaPool(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2148 if (!mArena) {
2149 mEarlyRv = NS_ERROR_DOM_UNKNOWN_ERR;
2150 return;
2151 }
2152
2153 // Create an empty key pair and set easy attributes
2154 mKeyPair->mPrivateKey = new CryptoKey(aGlobal);
2155 mKeyPair->mPublicKey = new CryptoKey(aGlobal);
2156
2157 // Extract algorithm name
2158 mEarlyRv = GetAlgorithmName(aCx, aAlgorithm, mAlgName);
2159 if (NS_FAILED(mEarlyRv)) {
2160 return;
2161 }
2162
2163 // Construct an appropriate KeyAlorithm
2164 uint32_t privateAllowedUsages = 0, publicAllowedUsages = 0;
2165 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2166 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2167 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2168 RootedDictionary<RsaHashedKeyGenParams> params(aCx);
2169 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2170 if (NS_FAILED(mEarlyRv)) {
2171 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2172 return;
2173 }
2174
2175 // Pull relevant info
2176 uint32_t modulusLength = params.mModulusLength;
2177 CryptoBuffer publicExponent;
2178 ATTEMPT_BUFFER_INIT(publicExponent, params.mPublicExponent);
2179 nsString hashName;
2180 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2181 if (NS_FAILED(mEarlyRv)) {
2182 return;
2183 }
2184
2185 // Create algorithm
2186 if (!mKeyPair->mPublicKey.get()->Algorithm().MakeRsa(
2187 mAlgName, modulusLength, publicExponent, hashName)) {
2188 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2189 return;
2190 }
2191 if (!mKeyPair->mPrivateKey.get()->Algorithm().MakeRsa(
2192 mAlgName, modulusLength, publicExponent, hashName)) {
2193 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2194 return;
2195 }
2196 mMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
2197
2198 // Set up params struct
2199 mRsaParams.keySizeInBits = modulusLength;
2200 bool converted = publicExponent.GetBigIntValue(mRsaParams.pe);
2201 if (!converted) {
2202 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2203 return;
2204 }
2205 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2206 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2207 RootedDictionary<EcKeyGenParams> params(aCx);
2208 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2209 if (NS_FAILED(mEarlyRv)) {
2210 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2211 return;
2212 }
2213
2214 if (!NormalizeToken(params.mNamedCurve, mNamedCurve)) {
2215 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2216 return;
2217 }
2218
2219 // Create algorithm.
2220 mKeyPair->mPublicKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
2221 mKeyPair->mPrivateKey.get()->Algorithm().MakeEc(mAlgName, mNamedCurve);
2222 mMechanism = CKM_EC_KEY_PAIR_GEN;
2223 } else {
2224 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2225 return;
2226 }
2227
2228 // Set key usages.
2229 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2230 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2231 mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2232 privateAllowedUsages = CryptoKey::SIGN;
2233 publicAllowedUsages = CryptoKey::VERIFY;
2234 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2235 privateAllowedUsages = CryptoKey::DECRYPT | CryptoKey::UNWRAPKEY;
2236 publicAllowedUsages = CryptoKey::ENCRYPT | CryptoKey::WRAPKEY;
2237 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
2238 privateAllowedUsages = CryptoKey::DERIVEKEY | CryptoKey::DERIVEBITS;
2239 publicAllowedUsages = 0;
2240 } else {
2241 MOZ_ASSERT(false); // This shouldn't happen.
2242 }
2243
2244 mKeyPair->mPrivateKey.get()->SetExtractable(aExtractable);
2245 mKeyPair->mPrivateKey.get()->SetType(CryptoKey::PRIVATE);
2246
2247 mKeyPair->mPublicKey.get()->SetExtractable(true);
2248 mKeyPair->mPublicKey.get()->SetType(CryptoKey::PUBLIC);
2249
2250 mKeyPair->mPrivateKey.get()->ClearUsages();
2251 mKeyPair->mPublicKey.get()->ClearUsages();
2252 for (uint32_t i = 0; i < aKeyUsages.Length(); ++i) {
2253 mEarlyRv = mKeyPair->mPrivateKey.get()->AddUsageIntersecting(
2254 aKeyUsages[i], privateAllowedUsages);
2255 if (NS_FAILED(mEarlyRv)) {
2256 return;
2257 }
2258
2259 mEarlyRv = mKeyPair->mPublicKey.get()->AddUsageIntersecting(
2260 aKeyUsages[i], publicAllowedUsages);
2261 if (NS_FAILED(mEarlyRv)) {
2262 return;
2263 }
2264 }
2265
2266 // If no usages ended up being allowed, DataError
2267 if (!mKeyPair->mPublicKey.get()->HasAnyUsage() &&
2268 !mKeyPair->mPrivateKey.get()->HasAnyUsage()) {
2269 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2270 return;
2271 }
2272 }
2273
DoCrypto()2274 nsresult GenerateAsymmetricKeyTask::DoCrypto() {
2275 MOZ_ASSERT(mKeyPair);
2276
2277 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2278 MOZ_ASSERT(slot.get());
2279
2280 void* param;
2281 switch (mMechanism) {
2282 case CKM_RSA_PKCS_KEY_PAIR_GEN:
2283 param = &mRsaParams;
2284 break;
2285 case CKM_DH_PKCS_KEY_PAIR_GEN:
2286 param = &mDhParams;
2287 break;
2288 case CKM_EC_KEY_PAIR_GEN: {
2289 param = CreateECParamsForCurve(mNamedCurve, mArena.get());
2290 if (!param) {
2291 return NS_ERROR_DOM_UNKNOWN_ERR;
2292 }
2293 break;
2294 }
2295 default:
2296 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2297 }
2298
2299 SECKEYPublicKey* pubKey = nullptr;
2300 mPrivateKey = UniqueSECKEYPrivateKey(PK11_GenerateKeyPair(
2301 slot.get(), mMechanism, param, &pubKey, PR_FALSE, PR_FALSE, nullptr));
2302 mPublicKey = UniqueSECKEYPublicKey(pubKey);
2303 pubKey = nullptr;
2304 if (!mPrivateKey.get() || !mPublicKey.get()) {
2305 return NS_ERROR_DOM_UNKNOWN_ERR;
2306 }
2307
2308 nsresult rv = mKeyPair->mPrivateKey.get()->SetPrivateKey(mPrivateKey.get());
2309 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2310 rv = mKeyPair->mPublicKey.get()->SetPublicKey(mPublicKey.get());
2311 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2312
2313 // PK11_GenerateKeyPair() does not set a CKA_EC_POINT attribute on the
2314 // private key, we need this later when exporting to PKCS8 and JWK though.
2315 if (mMechanism == CKM_EC_KEY_PAIR_GEN) {
2316 rv = mKeyPair->mPrivateKey->AddPublicKeyData(mPublicKey.get());
2317 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_OPERATION_ERR);
2318 }
2319
2320 return NS_OK;
2321 }
2322
Resolve()2323 void GenerateAsymmetricKeyTask::Resolve() {
2324 mResultPromise->MaybeResolve(*mKeyPair);
2325 }
2326
Cleanup()2327 void GenerateAsymmetricKeyTask::Cleanup() { mKeyPair = nullptr; }
2328
2329 class DeriveHkdfBitsTask : public ReturnArrayBufferViewTask {
2330 public:
DeriveHkdfBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)2331 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2332 CryptoKey& aKey, uint32_t aLength)
2333 : mMechanism(CKM_INVALID_MECHANISM) {
2334 Init(aCx, aAlgorithm, aKey, aLength);
2335 }
2336
DeriveHkdfBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const ObjectOrString & aTargetAlgorithm)2337 DeriveHkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2338 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2339 : mLengthInBits(0), mLengthInBytes(0), mMechanism(CKM_INVALID_MECHANISM) {
2340 size_t length;
2341 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2342
2343 if (NS_SUCCEEDED(mEarlyRv)) {
2344 Init(aCx, aAlgorithm, aKey, length);
2345 }
2346 }
2347
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)2348 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2349 uint32_t aLength) {
2350 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_HKDF);
2351 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_HKDF);
2352
2353 if (!mSymKey.Assign(aKey.GetSymKey())) {
2354 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2355 return;
2356 }
2357
2358 // Check that we have a key.
2359 if (mSymKey.Length() == 0) {
2360 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2361 return;
2362 }
2363
2364 RootedDictionary<HkdfParams> params(aCx);
2365 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2366 if (NS_FAILED(mEarlyRv)) {
2367 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2368 return;
2369 }
2370
2371 // length must be greater than zero.
2372 if (aLength == 0) {
2373 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2374 return;
2375 }
2376
2377 // Extract the hash algorithm.
2378 nsString hashName;
2379 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2380 if (NS_FAILED(mEarlyRv)) {
2381 return;
2382 }
2383
2384 // Check the given hash algorithm.
2385 switch (MapAlgorithmNameToMechanism(hashName)) {
2386 case CKM_SHA_1:
2387 mMechanism = CKM_NSS_HKDF_SHA1;
2388 break;
2389 case CKM_SHA256:
2390 mMechanism = CKM_NSS_HKDF_SHA256;
2391 break;
2392 case CKM_SHA384:
2393 mMechanism = CKM_NSS_HKDF_SHA384;
2394 break;
2395 case CKM_SHA512:
2396 mMechanism = CKM_NSS_HKDF_SHA512;
2397 break;
2398 default:
2399 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2400 return;
2401 }
2402
2403 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2404 ATTEMPT_BUFFER_INIT(mInfo, params.mInfo)
2405 mLengthInBytes = ceil((double)aLength / 8);
2406 mLengthInBits = aLength;
2407 }
2408
2409 private:
2410 size_t mLengthInBits;
2411 size_t mLengthInBytes;
2412 CryptoBuffer mSalt;
2413 CryptoBuffer mInfo;
2414 CryptoBuffer mSymKey;
2415 CK_MECHANISM_TYPE mMechanism;
2416
DoCrypto()2417 virtual nsresult DoCrypto() override {
2418 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2419 if (!arena) {
2420 return NS_ERROR_DOM_OPERATION_ERR;
2421 }
2422
2423 // Import the key
2424 SECItem keyItem = {siBuffer, nullptr, 0};
2425 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2426
2427 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2428 if (!slot.get()) {
2429 return NS_ERROR_DOM_OPERATION_ERR;
2430 }
2431
2432 UniquePK11SymKey baseKey(PK11_ImportSymKey(slot.get(), mMechanism,
2433 PK11_OriginUnwrap, CKA_WRAP,
2434 &keyItem, nullptr));
2435 if (!baseKey) {
2436 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
2437 }
2438
2439 SECItem salt = {siBuffer, nullptr, 0};
2440 SECItem info = {siBuffer, nullptr, 0};
2441 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2442 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &info, mInfo);
2443
2444 CK_NSS_HKDFParams hkdfParams = {true, salt.data, salt.len,
2445 true, info.data, info.len};
2446 SECItem params = {siBuffer, (unsigned char*)&hkdfParams,
2447 sizeof(hkdfParams)};
2448
2449 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2450 // derived symmetric key and don't matter because we ignore them anyway.
2451 UniquePK11SymKey symKey(PK11_Derive(baseKey.get(), mMechanism, ¶ms,
2452 CKM_SHA512_HMAC, CKA_SIGN,
2453 mLengthInBytes));
2454
2455 if (!symKey.get()) {
2456 return NS_ERROR_DOM_OPERATION_ERR;
2457 }
2458
2459 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2460 if (NS_FAILED(rv)) {
2461 return NS_ERROR_DOM_OPERATION_ERR;
2462 }
2463
2464 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2465 // just refers to a buffer managed by symKey. The assignment copies the
2466 // data, so mResult manages one copy, while symKey manages another.
2467 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2468
2469 if (mLengthInBytes > mResult.Length()) {
2470 return NS_ERROR_DOM_DATA_ERR;
2471 }
2472
2473 if (!mResult.SetLength(mLengthInBytes, fallible)) {
2474 return NS_ERROR_DOM_UNKNOWN_ERR;
2475 }
2476
2477 // If the number of bits to derive is not a multiple of 8 we need to
2478 // zero out the remaining bits that were derived but not requested.
2479 if (mLengthInBits % 8) {
2480 mResult[mResult.Length() - 1] &= 0xff << (mLengthInBits % 8);
2481 }
2482
2483 return NS_OK;
2484 }
2485 };
2486
2487 class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask {
2488 public:
DerivePbkdfBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)2489 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2490 CryptoKey& aKey, uint32_t aLength)
2491 : mHashOidTag(SEC_OID_UNKNOWN) {
2492 Init(aCx, aAlgorithm, aKey, aLength);
2493 }
2494
DerivePbkdfBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const ObjectOrString & aTargetAlgorithm)2495 DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2496 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2497 : mLength(0), mIterations(0), mHashOidTag(SEC_OID_UNKNOWN) {
2498 size_t length;
2499 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, length);
2500
2501 if (NS_SUCCEEDED(mEarlyRv)) {
2502 Init(aCx, aAlgorithm, aKey, length);
2503 }
2504 }
2505
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)2506 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2507 uint32_t aLength) {
2508 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_PBKDF2);
2509 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_PBKDF2);
2510
2511 if (!mSymKey.Assign(aKey.GetSymKey())) {
2512 mEarlyRv = NS_ERROR_OUT_OF_MEMORY;
2513 return;
2514 }
2515
2516 RootedDictionary<Pbkdf2Params> params(aCx);
2517 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2518 if (NS_FAILED(mEarlyRv)) {
2519 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2520 return;
2521 }
2522
2523 // length must be a multiple of 8 bigger than zero.
2524 if (aLength == 0 || aLength % 8) {
2525 mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
2526 return;
2527 }
2528
2529 // Extract the hash algorithm.
2530 nsString hashName;
2531 mEarlyRv = GetAlgorithmName(aCx, params.mHash, hashName);
2532 if (NS_FAILED(mEarlyRv)) {
2533 return;
2534 }
2535
2536 // Check the given hash algorithm.
2537 switch (MapAlgorithmNameToMechanism(hashName)) {
2538 case CKM_SHA_1:
2539 mHashOidTag = SEC_OID_HMAC_SHA1;
2540 break;
2541 case CKM_SHA256:
2542 mHashOidTag = SEC_OID_HMAC_SHA256;
2543 break;
2544 case CKM_SHA384:
2545 mHashOidTag = SEC_OID_HMAC_SHA384;
2546 break;
2547 case CKM_SHA512:
2548 mHashOidTag = SEC_OID_HMAC_SHA512;
2549 break;
2550 default:
2551 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2552 return;
2553 }
2554
2555 ATTEMPT_BUFFER_INIT(mSalt, params.mSalt)
2556 mLength = aLength >> 3; // bits to bytes
2557 mIterations = params.mIterations;
2558 }
2559
2560 private:
2561 size_t mLength;
2562 size_t mIterations;
2563 CryptoBuffer mSalt;
2564 CryptoBuffer mSymKey;
2565 SECOidTag mHashOidTag;
2566
DoCrypto()2567 virtual nsresult DoCrypto() override {
2568 UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
2569 if (!arena) {
2570 return NS_ERROR_DOM_OPERATION_ERR;
2571 }
2572
2573 SECItem salt = {siBuffer, nullptr, 0};
2574 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &salt, mSalt);
2575 // PK11_CreatePBEV2AlgorithmID will "helpfully" create PBKDF2 parameters
2576 // with a random salt if given a SECItem* that is either null or has a null
2577 // data pointer. This obviously isn't what we want, so we have to fake it
2578 // out by passing in a SECItem* with a non-null data pointer but with zero
2579 // length.
2580 if (!salt.data) {
2581 MOZ_ASSERT(salt.len == 0);
2582 salt.data =
2583 reinterpret_cast<unsigned char*>(PORT_ArenaAlloc(arena.get(), 1));
2584 if (!salt.data) {
2585 return NS_ERROR_DOM_UNKNOWN_ERR;
2586 }
2587 }
2588
2589 // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
2590 // parameter is unused for key generation. It is currently only used
2591 // for PBKDF2 authentication or key (un)wrapping when specifying an
2592 // encryption algorithm (PBES2).
2593 UniqueSECAlgorithmID algID(
2594 PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1,
2595 mHashOidTag, mLength, mIterations, &salt));
2596
2597 if (!algID) {
2598 return NS_ERROR_DOM_OPERATION_ERR;
2599 }
2600
2601 UniquePK11SlotInfo slot(PK11_GetInternalSlot());
2602 if (!slot.get()) {
2603 return NS_ERROR_DOM_OPERATION_ERR;
2604 }
2605
2606 SECItem keyItem = {siBuffer, nullptr, 0};
2607 ATTEMPT_BUFFER_TO_SECITEM(arena.get(), &keyItem, mSymKey);
2608
2609 UniquePK11SymKey symKey(
2610 PK11_PBEKeyGen(slot.get(), algID.get(), &keyItem, false, nullptr));
2611 if (!symKey.get()) {
2612 return NS_ERROR_DOM_OPERATION_ERR;
2613 }
2614
2615 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2616 if (NS_FAILED(rv)) {
2617 return NS_ERROR_DOM_OPERATION_ERR;
2618 }
2619
2620 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2621 // just refers to a buffer managed by symKey. The assignment copies the
2622 // data, so mResult manages one copy, while symKey manages another.
2623 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2624 return NS_OK;
2625 }
2626 };
2627
2628 template <class DeriveBitsTask>
2629 class DeriveKeyTask : public DeriveBitsTask {
2630 public:
DeriveKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aBaseKey,const ObjectOrString & aDerivedKeyType,bool aExtractable,const Sequence<nsString> & aKeyUsages)2631 DeriveKeyTask(nsIGlobalObject* aGlobal, JSContext* aCx,
2632 const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
2633 const ObjectOrString& aDerivedKeyType, bool aExtractable,
2634 const Sequence<nsString>& aKeyUsages)
2635 : DeriveBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType) {
2636 if (NS_FAILED(this->mEarlyRv)) {
2637 return;
2638 }
2639
2640 NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW);
2641 mTask = new ImportSymmetricKeyTask(aGlobal, aCx, format, aDerivedKeyType,
2642 aExtractable, aKeyUsages);
2643 }
2644
2645 protected:
2646 RefPtr<ImportSymmetricKeyTask> mTask;
2647
2648 private:
Resolve()2649 virtual void Resolve() override {
2650 mTask->SetRawKeyData(this->mResult);
2651 mTask->DispatchWithPromise(this->mResultPromise);
2652 }
2653
Cleanup()2654 virtual void Cleanup() override { mTask = nullptr; }
2655 };
2656
2657 class DeriveEcdhBitsTask : public ReturnArrayBufferViewTask {
2658 public:
DeriveEcdhBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)2659 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2660 CryptoKey& aKey, uint32_t aLength)
2661 : mLength(aLength), mPrivKey(aKey.GetPrivateKey()) {
2662 Init(aCx, aAlgorithm, aKey);
2663 }
2664
DeriveEcdhBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const ObjectOrString & aTargetAlgorithm)2665 DeriveEcdhBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
2666 CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
2667 : mPrivKey(aKey.GetPrivateKey()) {
2668 mEarlyRv = GetKeyLengthForAlgorithm(aCx, aTargetAlgorithm, mLength);
2669 if (NS_SUCCEEDED(mEarlyRv)) {
2670 Init(aCx, aAlgorithm, aKey);
2671 }
2672 }
2673
Init(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey)2674 void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey) {
2675 Telemetry::Accumulate(Telemetry::WEBCRYPTO_ALG, TA_ECDH);
2676 CHECK_KEY_ALGORITHM(aKey.Algorithm(), WEBCRYPTO_ALG_ECDH);
2677
2678 // Check that we have a private key.
2679 if (!mPrivKey) {
2680 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2681 return;
2682 }
2683
2684 // Length must be a multiple of 8 bigger than zero.
2685 if (mLength == 0 || mLength % 8) {
2686 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2687 return;
2688 }
2689
2690 mLength = mLength >> 3; // bits to bytes
2691
2692 // Retrieve the peer's public key.
2693 RootedDictionary<EcdhKeyDeriveParams> params(aCx);
2694 mEarlyRv = Coerce(aCx, params, aAlgorithm);
2695 if (NS_FAILED(mEarlyRv)) {
2696 mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
2697 return;
2698 }
2699
2700 CryptoKey* publicKey = params.mPublic;
2701 mPubKey = publicKey->GetPublicKey();
2702 if (!mPubKey) {
2703 mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
2704 return;
2705 }
2706
2707 CHECK_KEY_ALGORITHM(publicKey->Algorithm(), WEBCRYPTO_ALG_ECDH);
2708
2709 // Both keys must use the same named curve.
2710 nsString curve1 = aKey.Algorithm().mEc.mNamedCurve;
2711 nsString curve2 = publicKey->Algorithm().mEc.mNamedCurve;
2712
2713 if (!curve1.Equals(curve2)) {
2714 mEarlyRv = NS_ERROR_DOM_DATA_ERR;
2715 return;
2716 }
2717 }
2718
2719 private:
2720 size_t mLength;
2721 UniqueSECKEYPrivateKey mPrivKey;
2722 UniqueSECKEYPublicKey mPubKey;
2723
DoCrypto()2724 virtual nsresult DoCrypto() override {
2725 // CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
2726 // derived symmetric key and don't matter because we ignore them anyway.
2727 UniquePK11SymKey symKey(
2728 PK11_PubDeriveWithKDF(mPrivKey.get(), mPubKey.get(), PR_FALSE, nullptr,
2729 nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
2730 CKA_SIGN, 0, CKD_NULL, nullptr, nullptr));
2731
2732 if (!symKey.get()) {
2733 return NS_ERROR_DOM_OPERATION_ERR;
2734 }
2735
2736 nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey.get()));
2737 if (NS_FAILED(rv)) {
2738 return NS_ERROR_DOM_OPERATION_ERR;
2739 }
2740
2741 // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
2742 // just refers to a buffer managed by symKey. The assignment copies the
2743 // data, so mResult manages one copy, while symKey manages another.
2744 ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey.get()));
2745
2746 if (mLength > mResult.Length()) {
2747 return NS_ERROR_DOM_DATA_ERR;
2748 }
2749
2750 if (!mResult.SetLength(mLength, fallible)) {
2751 return NS_ERROR_DOM_UNKNOWN_ERR;
2752 }
2753
2754 return NS_OK;
2755 }
2756 };
2757
2758 template <class KeyEncryptTask>
2759 class WrapKeyTask : public ExportKeyTask {
2760 public:
WrapKeyTask(JSContext * aCx,const nsAString & aFormat,CryptoKey & aKey,CryptoKey & aWrappingKey,const ObjectOrString & aWrapAlgorithm)2761 WrapKeyTask(JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
2762 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm)
2763 : ExportKeyTask(aFormat, aKey) {
2764 if (NS_FAILED(mEarlyRv)) {
2765 return;
2766 }
2767
2768 mTask = new KeyEncryptTask(aCx, aWrapAlgorithm, aWrappingKey, true);
2769 }
2770
2771 private:
2772 RefPtr<KeyEncryptTask> mTask;
2773
AfterCrypto()2774 virtual nsresult AfterCrypto() override {
2775 // If wrapping JWK, stringify the JSON
2776 if (mFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2777 nsAutoString json;
2778 if (!mJwk.ToJSON(json)) {
2779 return NS_ERROR_DOM_OPERATION_ERR;
2780 }
2781
2782 NS_ConvertUTF16toUTF8 utf8(json);
2783 if (!mResult.Assign((const uint8_t*)utf8.BeginReading(), utf8.Length())) {
2784 return NS_ERROR_DOM_OPERATION_ERR;
2785 }
2786 }
2787
2788 return NS_OK;
2789 }
2790
Resolve()2791 virtual void Resolve() override {
2792 mTask->SetData(mResult);
2793 mTask->DispatchWithPromise(mResultPromise);
2794 }
2795
Cleanup()2796 virtual void Cleanup() override { mTask = nullptr; }
2797 };
2798
2799 template <class KeyEncryptTask>
2800 class UnwrapKeyTask : public KeyEncryptTask {
2801 public:
UnwrapKeyTask(JSContext * aCx,const ArrayBufferViewOrArrayBuffer & aWrappedKey,CryptoKey & aUnwrappingKey,const ObjectOrString & aUnwrapAlgorithm,ImportKeyTask * aTask)2802 UnwrapKeyTask(JSContext* aCx, const ArrayBufferViewOrArrayBuffer& aWrappedKey,
2803 CryptoKey& aUnwrappingKey,
2804 const ObjectOrString& aUnwrapAlgorithm, ImportKeyTask* aTask)
2805 : KeyEncryptTask(aCx, aUnwrapAlgorithm, aUnwrappingKey, aWrappedKey,
2806 false),
2807 mTask(aTask) {}
2808
2809 private:
2810 RefPtr<ImportKeyTask> mTask;
2811
Resolve()2812 virtual void Resolve() override {
2813 mTask->SetKeyDataMaybeParseJWK(KeyEncryptTask::mResult);
2814 mTask->DispatchWithPromise(KeyEncryptTask::mResultPromise);
2815 }
2816
Cleanup()2817 virtual void Cleanup() override { mTask = nullptr; }
2818 };
2819
2820 // Task creation methods for WebCryptoTask
2821
2822 // Note: We do not perform algorithm normalization as a monolithic process,
2823 // as described in the spec. Instead:
2824 // * Each method handles its slice of the supportedAlgorithms structure
2825 // * Task constructors take care of:
2826 // * Coercing the algorithm to the proper concrete type
2827 // * Cloning subordinate data items
2828 // * Cloning input data as needed
2829 //
2830 // Thus, support for different algorithms is determined by the if-statements
2831 // below, rather than a data structure.
2832 //
2833 // This results in algorithm normalization coming after some other checks,
2834 // and thus slightly more steps being done synchronously than the spec calls
2835 // for. But none of these steps is especially time-consuming.
2836
CreateEncryptDecryptTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aData,bool aEncrypt)2837 WebCryptoTask* WebCryptoTask::CreateEncryptDecryptTask(
2838 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2839 const CryptoOperationData& aData, bool aEncrypt) {
2840 TelemetryMethod method = (aEncrypt) ? TM_ENCRYPT : TM_DECRYPT;
2841 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2842 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_ENC,
2843 aKey.Extractable());
2844
2845 // Ensure key is usable for this operation
2846 if ((aEncrypt && !aKey.HasUsage(CryptoKey::ENCRYPT)) ||
2847 (!aEncrypt && !aKey.HasUsage(CryptoKey::DECRYPT))) {
2848 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2849 }
2850
2851 nsString algName;
2852 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2853 if (NS_FAILED(rv)) {
2854 return new FailureTask(rv);
2855 }
2856
2857 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2858 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2859 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
2860 return new AesTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2861 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
2862 return new RsaOaepTask(aCx, aAlgorithm, aKey, aData, aEncrypt);
2863 }
2864
2865 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2866 }
2867
CreateSignVerifyTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,const CryptoOperationData & aSignature,const CryptoOperationData & aData,bool aSign)2868 WebCryptoTask* WebCryptoTask::CreateSignVerifyTask(
2869 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
2870 const CryptoOperationData& aSignature, const CryptoOperationData& aData,
2871 bool aSign) {
2872 TelemetryMethod method = (aSign) ? TM_SIGN : TM_VERIFY;
2873 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, method);
2874 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_SIG,
2875 aKey.Extractable());
2876
2877 // Ensure key is usable for this operation
2878 if ((aSign && !aKey.HasUsage(CryptoKey::SIGN)) ||
2879 (!aSign && !aKey.HasUsage(CryptoKey::VERIFY))) {
2880 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2881 }
2882
2883 nsString algName;
2884 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2885 if (NS_FAILED(rv)) {
2886 return new FailureTask(rv);
2887 }
2888
2889 if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2890 return new HmacTask(aCx, aAlgorithm, aKey, aSignature, aData, aSign);
2891 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2892 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
2893 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2894 return new AsymmetricSignVerifyTask(aCx, aAlgorithm, aKey, aSignature,
2895 aData, aSign);
2896 }
2897
2898 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2899 }
2900
CreateDigestTask(JSContext * aCx,const ObjectOrString & aAlgorithm,const CryptoOperationData & aData)2901 WebCryptoTask* WebCryptoTask::CreateDigestTask(
2902 JSContext* aCx, const ObjectOrString& aAlgorithm,
2903 const CryptoOperationData& aData) {
2904 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DIGEST);
2905
2906 nsString algName;
2907 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2908 if (NS_FAILED(rv)) {
2909 return new FailureTask(rv);
2910 }
2911
2912 if (algName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
2913 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA256) ||
2914 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
2915 algName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
2916 return new DigestTask(aCx, aAlgorithm, aData);
2917 }
2918
2919 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2920 }
2921
CreateImportKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,JS::Handle<JSObject * > aKeyData,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)2922 WebCryptoTask* WebCryptoTask::CreateImportKeyTask(
2923 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
2924 JS::Handle<JSObject*> aKeyData, const ObjectOrString& aAlgorithm,
2925 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
2926 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_IMPORTKEY);
2927 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_IMPORT, aExtractable);
2928
2929 // Verify that the format is recognized
2930 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2931 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2932 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2933 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2934 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2935 }
2936
2937 // Verify that aKeyUsages does not contain an unrecognized value
2938 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
2939 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2940 }
2941
2942 nsString algName;
2943 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
2944 if (NS_FAILED(rv)) {
2945 return new FailureTask(rv);
2946 }
2947
2948 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
2949 // However, the spec should be updated to allow it.
2950 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2951 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2952 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2953 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
2954 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
2955 algName.EqualsLiteral(WEBCRYPTO_ALG_HKDF) ||
2956 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
2957 return new ImportSymmetricKeyTask(aGlobal, aCx, aFormat, aKeyData,
2958 aAlgorithm, aExtractable, aKeyUsages);
2959 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
2960 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
2961 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) {
2962 return new ImportRsaKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2963 aExtractable, aKeyUsages);
2964 } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH) ||
2965 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) {
2966 return new ImportEcKeyTask(aGlobal, aCx, aFormat, aKeyData, aAlgorithm,
2967 aExtractable, aKeyUsages);
2968 } else {
2969 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
2970 }
2971 }
2972
CreateExportKeyTask(const nsAString & aFormat,CryptoKey & aKey)2973 WebCryptoTask* WebCryptoTask::CreateExportKeyTask(const nsAString& aFormat,
2974 CryptoKey& aKey) {
2975 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_EXPORTKEY);
2976
2977 // Verify that the format is recognized
2978 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
2979 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
2980 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
2981 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
2982 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
2983 }
2984
2985 // Verify that the key is extractable
2986 if (!aKey.Extractable()) {
2987 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
2988 }
2989
2990 // Verify that the algorithm supports export
2991 // SPEC-BUG: PBKDF2 is not supposed to be supported for this operation.
2992 // However, the spec should be updated to allow it.
2993 nsString algName = aKey.Algorithm().mName;
2994 if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
2995 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
2996 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
2997 algName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW) ||
2998 algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
2999 algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC) ||
3000 algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3001 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP) ||
3002 algName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS) ||
3003 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA) ||
3004 algName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) {
3005 return new ExportKeyTask(aFormat, aKey);
3006 }
3007
3008 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3009 }
3010
CreateGenerateKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const ObjectOrString & aAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)3011 WebCryptoTask* WebCryptoTask::CreateGenerateKeyTask(
3012 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3013 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3014 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_GENERATEKEY);
3015 Telemetry::Accumulate(Telemetry::WEBCRYPTO_EXTRACTABLE_GENERATE,
3016 aExtractable);
3017
3018 // Verify that aKeyUsages does not contain an unrecognized value
3019 // SPEC-BUG: Spec says that this should be InvalidAccessError, but that
3020 // is inconsistent with other analogous points in the spec
3021 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3022 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3023 }
3024
3025 nsString algName;
3026 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3027 if (NS_FAILED(rv)) {
3028 return new FailureTask(rv);
3029 }
3030
3031 if (algName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3032 algName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3033 algName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3034 algName.EqualsASCII(WEBCRYPTO_ALG_AES_KW) ||
3035 algName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3036 return new GenerateSymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3037 aKeyUsages);
3038 } else if (algName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3039 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3040 algName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS) ||
3041 algName.EqualsASCII(WEBCRYPTO_ALG_ECDH) ||
3042 algName.EqualsASCII(WEBCRYPTO_ALG_ECDSA)) {
3043 return new GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, aExtractable,
3044 aKeyUsages);
3045 } else {
3046 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3047 }
3048 }
3049
CreateDeriveKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aBaseKey,const ObjectOrString & aDerivedKeyType,bool aExtractable,const Sequence<nsString> & aKeyUsages)3050 WebCryptoTask* WebCryptoTask::CreateDeriveKeyTask(
3051 nsIGlobalObject* aGlobal, JSContext* aCx, const ObjectOrString& aAlgorithm,
3052 CryptoKey& aBaseKey, const ObjectOrString& aDerivedKeyType,
3053 bool aExtractable, const Sequence<nsString>& aKeyUsages) {
3054 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
3055
3056 // Ensure baseKey is usable for this operation
3057 if (!aBaseKey.HasUsage(CryptoKey::DERIVEKEY)) {
3058 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3059 }
3060
3061 // Verify that aKeyUsages does not contain an unrecognized value
3062 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3063 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3064 }
3065
3066 nsString algName;
3067 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3068 if (NS_FAILED(rv)) {
3069 return new FailureTask(rv);
3070 }
3071
3072 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3073 return new DeriveKeyTask<DeriveHkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3074 aBaseKey, aDerivedKeyType,
3075 aExtractable, aKeyUsages);
3076 }
3077
3078 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3079 return new DeriveKeyTask<DerivePbkdfBitsTask>(aGlobal, aCx, aAlgorithm,
3080 aBaseKey, aDerivedKeyType,
3081 aExtractable, aKeyUsages);
3082 }
3083
3084 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3085 return new DeriveKeyTask<DeriveEcdhBitsTask>(aGlobal, aCx, aAlgorithm,
3086 aBaseKey, aDerivedKeyType,
3087 aExtractable, aKeyUsages);
3088 }
3089
3090 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3091 }
3092
CreateDeriveBitsTask(JSContext * aCx,const ObjectOrString & aAlgorithm,CryptoKey & aKey,uint32_t aLength)3093 WebCryptoTask* WebCryptoTask::CreateDeriveBitsTask(
3094 JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
3095 uint32_t aLength) {
3096 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
3097
3098 // Ensure baseKey is usable for this operation
3099 if (!aKey.HasUsage(CryptoKey::DERIVEBITS)) {
3100 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3101 }
3102
3103 nsString algName;
3104 nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
3105 if (NS_FAILED(rv)) {
3106 return new FailureTask(rv);
3107 }
3108
3109 if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
3110 return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3111 }
3112
3113 if (algName.EqualsASCII(WEBCRYPTO_ALG_ECDH)) {
3114 return new DeriveEcdhBitsTask(aCx, aAlgorithm, aKey, aLength);
3115 }
3116
3117 if (algName.EqualsASCII(WEBCRYPTO_ALG_HKDF)) {
3118 return new DeriveHkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
3119 }
3120
3121 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3122 }
3123
CreateWrapKeyTask(JSContext * aCx,const nsAString & aFormat,CryptoKey & aKey,CryptoKey & aWrappingKey,const ObjectOrString & aWrapAlgorithm)3124 WebCryptoTask* WebCryptoTask::CreateWrapKeyTask(
3125 JSContext* aCx, const nsAString& aFormat, CryptoKey& aKey,
3126 CryptoKey& aWrappingKey, const ObjectOrString& aWrapAlgorithm) {
3127 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_WRAPKEY);
3128
3129 // Verify that the format is recognized
3130 if (!aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW) &&
3131 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_SPKI) &&
3132 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_PKCS8) &&
3133 !aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
3134 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3135 }
3136
3137 // Ensure wrappingKey is usable for this operation
3138 if (!aWrappingKey.HasUsage(CryptoKey::WRAPKEY)) {
3139 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3140 }
3141
3142 // Ensure key is extractable
3143 if (!aKey.Extractable()) {
3144 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3145 }
3146
3147 nsString wrapAlgName;
3148 nsresult rv = GetAlgorithmName(aCx, aWrapAlgorithm, wrapAlgName);
3149 if (NS_FAILED(rv)) {
3150 return new FailureTask(rv);
3151 }
3152
3153 if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3154 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3155 wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3156 return new WrapKeyTask<AesTask>(aCx, aFormat, aKey, aWrappingKey,
3157 aWrapAlgorithm);
3158 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3159 return new WrapKeyTask<AesKwTask>(aCx, aFormat, aKey, aWrappingKey,
3160 aWrapAlgorithm);
3161 } else if (wrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3162 return new WrapKeyTask<RsaOaepTask>(aCx, aFormat, aKey, aWrappingKey,
3163 aWrapAlgorithm);
3164 }
3165
3166 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3167 }
3168
CreateUnwrapKeyTask(nsIGlobalObject * aGlobal,JSContext * aCx,const nsAString & aFormat,const ArrayBufferViewOrArrayBuffer & aWrappedKey,CryptoKey & aUnwrappingKey,const ObjectOrString & aUnwrapAlgorithm,const ObjectOrString & aUnwrappedKeyAlgorithm,bool aExtractable,const Sequence<nsString> & aKeyUsages)3169 WebCryptoTask* WebCryptoTask::CreateUnwrapKeyTask(
3170 nsIGlobalObject* aGlobal, JSContext* aCx, const nsAString& aFormat,
3171 const ArrayBufferViewOrArrayBuffer& aWrappedKey, CryptoKey& aUnwrappingKey,
3172 const ObjectOrString& aUnwrapAlgorithm,
3173 const ObjectOrString& aUnwrappedKeyAlgorithm, bool aExtractable,
3174 const Sequence<nsString>& aKeyUsages) {
3175 Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_UNWRAPKEY);
3176
3177 // Ensure key is usable for this operation
3178 if (!aUnwrappingKey.HasUsage(CryptoKey::UNWRAPKEY)) {
3179 return new FailureTask(NS_ERROR_DOM_INVALID_ACCESS_ERR);
3180 }
3181
3182 // Verify that aKeyUsages does not contain an unrecognized value
3183 if (!CryptoKey::AllUsagesRecognized(aKeyUsages)) {
3184 return new FailureTask(NS_ERROR_DOM_SYNTAX_ERR);
3185 }
3186
3187 nsString keyAlgName;
3188 nsresult rv = GetAlgorithmName(aCx, aUnwrappedKeyAlgorithm, keyAlgName);
3189 if (NS_FAILED(rv)) {
3190 return new FailureTask(rv);
3191 }
3192
3193 CryptoOperationData dummy;
3194 RefPtr<ImportKeyTask> importTask;
3195 if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CBC) ||
3196 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_CTR) ||
3197 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_AES_GCM) ||
3198 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HKDF) ||
3199 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_HMAC)) {
3200 importTask = new ImportSymmetricKeyTask(aGlobal, aCx, aFormat,
3201 aUnwrappedKeyAlgorithm,
3202 aExtractable, aKeyUsages);
3203 } else if (keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSASSA_PKCS1) ||
3204 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_OAEP) ||
3205 keyAlgName.EqualsASCII(WEBCRYPTO_ALG_RSA_PSS)) {
3206 importTask =
3207 new ImportRsaKeyTask(aGlobal, aCx, aFormat, aUnwrappedKeyAlgorithm,
3208 aExtractable, aKeyUsages);
3209 } else {
3210 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3211 }
3212
3213 nsString unwrapAlgName;
3214 rv = GetAlgorithmName(aCx, aUnwrapAlgorithm, unwrapAlgName);
3215 if (NS_FAILED(rv)) {
3216 return new FailureTask(rv);
3217 }
3218 if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
3219 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
3220 unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
3221 return new UnwrapKeyTask<AesTask>(aCx, aWrappedKey, aUnwrappingKey,
3222 aUnwrapAlgorithm, importTask);
3223 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) {
3224 return new UnwrapKeyTask<AesKwTask>(aCx, aWrappedKey, aUnwrappingKey,
3225 aUnwrapAlgorithm, importTask);
3226 } else if (unwrapAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) {
3227 return new UnwrapKeyTask<RsaOaepTask>(aCx, aWrappedKey, aUnwrappingKey,
3228 aUnwrapAlgorithm, importTask);
3229 }
3230
3231 return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3232 }
3233
WebCryptoTask()3234 WebCryptoTask::WebCryptoTask()
3235 : CancelableRunnable("WebCryptoTask"),
3236 mEarlyRv(NS_OK),
3237 mEarlyComplete(false),
3238 mOriginalEventTarget(nullptr),
3239 mRv(NS_ERROR_NOT_INITIALIZED) {}
3240
3241 WebCryptoTask::~WebCryptoTask() = default;
3242
3243 } // namespace dom
3244 } // namespace mozilla
3245