Line data Source code
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 "CryptoKey.h"
8 :
9 : #include "cryptohi.h"
10 : #include "mozilla/ArrayUtils.h"
11 : #include "mozilla/dom/SubtleCryptoBinding.h"
12 : #include "mozilla/dom/ToJSValue.h"
13 : #include "nsNSSComponent.h"
14 : #include "pk11pub.h"
15 :
16 : // Templates taken from security/nss/lib/cryptohi/seckey.c
17 : // These would ideally be exported by NSS and until that
18 : // happens we have to keep our own copies.
19 : const SEC_ASN1Template SECKEY_DHPublicKeyTemplate[] = {
20 : { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.publicValue), },
21 : { 0, }
22 : };
23 : const SEC_ASN1Template SECKEY_DHParamKeyTemplate[] = {
24 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SECKEYPublicKey) },
25 : { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.prime), },
26 : { SEC_ASN1_INTEGER, offsetof(SECKEYPublicKey,u.dh.base), },
27 : { SEC_ASN1_SKIP_REST },
28 : { 0, }
29 : };
30 :
31 : namespace mozilla {
32 : namespace dom {
33 :
34 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CryptoKey, mGlobal)
35 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(CryptoKey)
36 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(CryptoKey)
37 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CryptoKey)
38 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
39 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
40 0 : NS_INTERFACE_MAP_END
41 :
42 : nsresult
43 0 : StringToUsage(const nsString& aUsage, CryptoKey::KeyUsage& aUsageOut)
44 : {
45 0 : if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_ENCRYPT)) {
46 0 : aUsageOut = CryptoKey::ENCRYPT;
47 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DECRYPT)) {
48 0 : aUsageOut = CryptoKey::DECRYPT;
49 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_SIGN)) {
50 0 : aUsageOut = CryptoKey::SIGN;
51 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_VERIFY)) {
52 0 : aUsageOut = CryptoKey::VERIFY;
53 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEKEY)) {
54 0 : aUsageOut = CryptoKey::DERIVEKEY;
55 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_DERIVEBITS)) {
56 0 : aUsageOut = CryptoKey::DERIVEBITS;
57 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_WRAPKEY)) {
58 0 : aUsageOut = CryptoKey::WRAPKEY;
59 0 : } else if (aUsage.EqualsLiteral(WEBCRYPTO_KEY_USAGE_UNWRAPKEY)) {
60 0 : aUsageOut = CryptoKey::UNWRAPKEY;
61 : } else {
62 0 : return NS_ERROR_DOM_SYNTAX_ERR;
63 : }
64 0 : return NS_OK;
65 : }
66 :
67 : // This helper function will release the memory backing a SECKEYPrivateKey and
68 : // any resources acquired in its creation. It will leave the backing PKCS#11
69 : // object untouched, however. This should only be called from
70 : // PrivateKeyFromPrivateKeyTemplate.
71 : static void
72 0 : DestroyPrivateKeyWithoutDestroyingPKCS11Object(SECKEYPrivateKey* key)
73 : {
74 0 : PK11_FreeSlot(key->pkcs11Slot);
75 0 : PORT_FreeArena(key->arena, PR_TRUE);
76 0 : }
77 :
78 : // To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
79 : // generates a random ID for each key. The given template must contain an
80 : // attribute slot for a key ID, but it must consist of a null pointer and have a
81 : // length of 0.
82 : UniqueSECKEYPrivateKey
83 0 : PrivateKeyFromPrivateKeyTemplate(CK_ATTRIBUTE* aTemplate,
84 : CK_ULONG aTemplateSize)
85 : {
86 : // Create a generic object with the contents of the key
87 0 : UniquePK11SlotInfo slot(PK11_GetInternalSlot());
88 0 : if (!slot) {
89 0 : return nullptr;
90 : }
91 :
92 : // Generate a random 160-bit object ID. This ID must be unique.
93 0 : UniqueSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20));
94 0 : SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
95 0 : if (rv != SECSuccess) {
96 0 : return nullptr;
97 : }
98 : // Check if something is already using this ID.
99 0 : SECKEYPrivateKey* preexistingKey = PK11_FindKeyByKeyID(slot.get(),
100 : objID.get(),
101 0 : nullptr);
102 0 : if (preexistingKey) {
103 : // Note that we can't just call SECKEY_DestroyPrivateKey here because that
104 : // will destroy the PKCS#11 object that is backing a preexisting key (that
105 : // we still have a handle on somewhere else in memory). If that object were
106 : // destroyed, cryptographic operations performed by that other key would
107 : // fail.
108 0 : DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
109 : // Try again with a new ID (but only once - collisions are very unlikely).
110 0 : rv = PK11_GenerateRandomOnSlot(slot.get(), objID->data, objID->len);
111 0 : if (rv != SECSuccess) {
112 0 : return nullptr;
113 : }
114 0 : preexistingKey = PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr);
115 0 : if (preexistingKey) {
116 0 : DestroyPrivateKeyWithoutDestroyingPKCS11Object(preexistingKey);
117 0 : return nullptr;
118 : }
119 : }
120 :
121 0 : CK_ATTRIBUTE* idAttributeSlot = nullptr;
122 0 : for (CK_ULONG i = 0; i < aTemplateSize; i++) {
123 0 : if (aTemplate[i].type == CKA_ID) {
124 0 : if (aTemplate[i].pValue != nullptr || aTemplate[i].ulValueLen != 0) {
125 0 : return nullptr;
126 : }
127 0 : idAttributeSlot = aTemplate + i;
128 0 : break;
129 : }
130 : }
131 0 : if (!idAttributeSlot) {
132 0 : return nullptr;
133 : }
134 :
135 0 : idAttributeSlot->pValue = objID->data;
136 0 : idAttributeSlot->ulValueLen = objID->len;
137 : UniquePK11GenericObject obj(PK11_CreateGenericObject(slot.get(),
138 : aTemplate,
139 : aTemplateSize,
140 0 : PR_FALSE));
141 : // Unset the ID attribute slot's pointer and length so that data that only
142 : // lives for the scope of this function doesn't escape.
143 0 : idAttributeSlot->pValue = nullptr;
144 0 : idAttributeSlot->ulValueLen = 0;
145 0 : if (!obj) {
146 0 : return nullptr;
147 : }
148 :
149 : // Have NSS translate the object to a private key.
150 : return UniqueSECKEYPrivateKey(
151 0 : PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr));
152 : }
153 :
154 0 : CryptoKey::CryptoKey(nsIGlobalObject* aGlobal)
155 : : mGlobal(aGlobal)
156 : , mAttributes(0)
157 : , mSymKey()
158 : , mPrivateKey(nullptr)
159 0 : , mPublicKey(nullptr)
160 : {
161 0 : }
162 :
163 0 : CryptoKey::~CryptoKey()
164 : {
165 0 : nsNSSShutDownPreventionLock locker;
166 0 : if (isAlreadyShutDown()) {
167 0 : return;
168 : }
169 0 : destructorSafeDestroyNSSReference();
170 0 : shutdown(ShutdownCalledFrom::Object);
171 0 : }
172 :
173 : JSObject*
174 0 : CryptoKey::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
175 : {
176 0 : return CryptoKeyBinding::Wrap(aCx, this, aGivenProto);
177 : }
178 :
179 : void
180 0 : CryptoKey::GetType(nsString& aRetVal) const
181 : {
182 0 : uint32_t type = mAttributes & TYPE_MASK;
183 0 : switch (type) {
184 0 : case PUBLIC: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC); break;
185 0 : case PRIVATE: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE); break;
186 0 : case SECRET: aRetVal.AssignLiteral(WEBCRYPTO_KEY_TYPE_SECRET); break;
187 : }
188 0 : }
189 :
190 : bool
191 0 : CryptoKey::Extractable() const
192 : {
193 0 : return (mAttributes & EXTRACTABLE);
194 : }
195 :
196 : void
197 0 : CryptoKey::GetAlgorithm(JSContext* cx, JS::MutableHandle<JSObject*> aRetVal,
198 : ErrorResult& aRv) const
199 : {
200 0 : bool converted = false;
201 0 : JS::RootedValue val(cx);
202 0 : switch (mAlgorithm.mType) {
203 : case KeyAlgorithmProxy::AES:
204 0 : converted = ToJSValue(cx, mAlgorithm.mAes, &val);
205 0 : break;
206 : case KeyAlgorithmProxy::HMAC:
207 0 : converted = ToJSValue(cx, mAlgorithm.mHmac, &val);
208 0 : break;
209 : case KeyAlgorithmProxy::RSA: {
210 0 : RootedDictionary<RsaHashedKeyAlgorithm> rsa(cx);
211 0 : converted = mAlgorithm.mRsa.ToKeyAlgorithm(cx, rsa);
212 0 : if (converted) {
213 0 : converted = ToJSValue(cx, rsa, &val);
214 : }
215 0 : break;
216 : }
217 : case KeyAlgorithmProxy::EC:
218 0 : converted = ToJSValue(cx, mAlgorithm.mEc, &val);
219 0 : break;
220 : case KeyAlgorithmProxy::DH: {
221 0 : RootedDictionary<DhKeyAlgorithm> dh(cx);
222 0 : converted = mAlgorithm.mDh.ToKeyAlgorithm(cx, dh);
223 0 : if (converted) {
224 0 : converted = ToJSValue(cx, dh, &val);
225 : }
226 0 : break;
227 : }
228 : }
229 0 : if (!converted) {
230 0 : aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
231 0 : return;
232 : }
233 :
234 0 : aRetVal.set(&val.toObject());
235 : }
236 :
237 : void
238 0 : CryptoKey::GetUsages(nsTArray<nsString>& aRetVal) const
239 : {
240 0 : if (mAttributes & ENCRYPT) {
241 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_ENCRYPT));
242 : }
243 0 : if (mAttributes & DECRYPT) {
244 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DECRYPT));
245 : }
246 0 : if (mAttributes & SIGN) {
247 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_SIGN));
248 : }
249 0 : if (mAttributes & VERIFY) {
250 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_VERIFY));
251 : }
252 0 : if (mAttributes & DERIVEKEY) {
253 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEKEY));
254 : }
255 0 : if (mAttributes & DERIVEBITS) {
256 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_DERIVEBITS));
257 : }
258 0 : if (mAttributes & WRAPKEY) {
259 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_WRAPKEY));
260 : }
261 0 : if (mAttributes & UNWRAPKEY) {
262 0 : aRetVal.AppendElement(NS_LITERAL_STRING(WEBCRYPTO_KEY_USAGE_UNWRAPKEY));
263 : }
264 0 : }
265 :
266 : KeyAlgorithmProxy&
267 0 : CryptoKey::Algorithm()
268 : {
269 0 : return mAlgorithm;
270 : }
271 :
272 : const KeyAlgorithmProxy&
273 0 : CryptoKey::Algorithm() const
274 : {
275 0 : return mAlgorithm;
276 : }
277 :
278 : CryptoKey::KeyType
279 0 : CryptoKey::GetKeyType() const
280 : {
281 0 : return static_cast<CryptoKey::KeyType>(mAttributes & TYPE_MASK);
282 : }
283 :
284 : nsresult
285 0 : CryptoKey::SetType(const nsString& aType)
286 : {
287 0 : mAttributes &= CLEAR_TYPE;
288 0 : if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_SECRET)) {
289 0 : mAttributes |= SECRET;
290 0 : } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PUBLIC)) {
291 0 : mAttributes |= PUBLIC;
292 0 : } else if (aType.EqualsLiteral(WEBCRYPTO_KEY_TYPE_PRIVATE)) {
293 0 : mAttributes |= PRIVATE;
294 : } else {
295 0 : mAttributes |= UNKNOWN;
296 0 : return NS_ERROR_DOM_SYNTAX_ERR;
297 : }
298 :
299 0 : return NS_OK;
300 : }
301 :
302 : void
303 0 : CryptoKey::SetType(CryptoKey::KeyType aType)
304 : {
305 0 : mAttributes &= CLEAR_TYPE;
306 0 : mAttributes |= aType;
307 0 : }
308 :
309 : void
310 0 : CryptoKey::SetExtractable(bool aExtractable)
311 : {
312 0 : mAttributes &= CLEAR_EXTRACTABLE;
313 0 : if (aExtractable) {
314 0 : mAttributes |= EXTRACTABLE;
315 : }
316 0 : }
317 :
318 : // NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the
319 : // public value. To properly export the private key to JWK or PKCS #8 we need
320 : // the public key data though and so we use this method to augment a private
321 : // key with data from the given public key.
322 : nsresult
323 0 : CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey)
324 : {
325 : // This should be a private key.
326 0 : MOZ_ASSERT(GetKeyType() == PRIVATE);
327 : // There should be a private NSS key with type 'EC'.
328 0 : MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey);
329 : // The given public key should have the same key type.
330 0 : MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType);
331 :
332 0 : nsNSSShutDownPreventionLock locker;
333 :
334 : // Read EC params.
335 0 : ScopedAutoSECItem params;
336 0 : SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(),
337 0 : CKA_EC_PARAMS, ¶ms);
338 0 : if (rv != SECSuccess) {
339 0 : return NS_ERROR_DOM_OPERATION_ERR;
340 : }
341 :
342 : // Read private value.
343 0 : ScopedAutoSECItem value;
344 0 : rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey.get(), CKA_VALUE,
345 0 : &value);
346 0 : if (rv != SECSuccess) {
347 0 : return NS_ERROR_DOM_OPERATION_ERR;
348 : }
349 :
350 0 : SECItem* point = &aPublicKey->u.ec.publicValue;
351 0 : CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
352 0 : CK_BBOOL falseValue = CK_FALSE;
353 0 : CK_KEY_TYPE ecValue = CKK_EC;
354 :
355 : CK_ATTRIBUTE keyTemplate[9] = {
356 : { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
357 : { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) },
358 : { CKA_TOKEN, &falseValue, sizeof(falseValue) },
359 : { CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
360 : { CKA_PRIVATE, &falseValue, sizeof(falseValue) },
361 : // PrivateKeyFromPrivateKeyTemplate sets the ID.
362 : { CKA_ID, nullptr, 0 },
363 0 : { CKA_EC_PARAMS, params.data, params.len },
364 0 : { CKA_EC_POINT, point->data, point->len },
365 0 : { CKA_VALUE, value.data, value.len },
366 0 : };
367 :
368 0 : mPrivateKey = PrivateKeyFromPrivateKeyTemplate(keyTemplate,
369 0 : ArrayLength(keyTemplate));
370 0 : NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR);
371 :
372 0 : return NS_OK;
373 : }
374 :
375 : void
376 0 : CryptoKey::ClearUsages()
377 : {
378 0 : mAttributes &= CLEAR_USAGES;
379 0 : }
380 :
381 : nsresult
382 0 : CryptoKey::AddUsage(const nsString& aUsage)
383 : {
384 0 : return AddUsageIntersecting(aUsage, USAGES_MASK);
385 : }
386 :
387 : nsresult
388 0 : CryptoKey::AddUsageIntersecting(const nsString& aUsage, uint32_t aUsageMask)
389 : {
390 : KeyUsage usage;
391 0 : if (NS_FAILED(StringToUsage(aUsage, usage))) {
392 0 : return NS_ERROR_DOM_SYNTAX_ERR;
393 : }
394 :
395 0 : if (usage & aUsageMask) {
396 0 : AddUsage(usage);
397 0 : return NS_OK;
398 : }
399 :
400 0 : return NS_OK;
401 : }
402 :
403 : void
404 0 : CryptoKey::AddUsage(CryptoKey::KeyUsage aUsage)
405 : {
406 0 : mAttributes |= aUsage;
407 0 : }
408 :
409 : bool
410 0 : CryptoKey::HasAnyUsage()
411 : {
412 0 : return !!(mAttributes & USAGES_MASK);
413 : }
414 :
415 : bool
416 0 : CryptoKey::HasUsage(CryptoKey::KeyUsage aUsage)
417 : {
418 0 : return !!(mAttributes & aUsage);
419 : }
420 :
421 : bool
422 0 : CryptoKey::HasUsageOtherThan(uint32_t aUsages)
423 : {
424 0 : return !!(mAttributes & USAGES_MASK & ~aUsages);
425 : }
426 :
427 : bool
428 0 : CryptoKey::IsRecognizedUsage(const nsString& aUsage)
429 : {
430 : KeyUsage dummy;
431 0 : nsresult rv = StringToUsage(aUsage, dummy);
432 0 : return NS_SUCCEEDED(rv);
433 : }
434 :
435 : bool
436 0 : CryptoKey::AllUsagesRecognized(const Sequence<nsString>& aUsages)
437 : {
438 0 : for (uint32_t i = 0; i < aUsages.Length(); ++i) {
439 0 : if (!IsRecognizedUsage(aUsages[i])) {
440 0 : return false;
441 : }
442 : }
443 0 : return true;
444 : }
445 :
446 0 : nsresult CryptoKey::SetSymKey(const CryptoBuffer& aSymKey)
447 : {
448 0 : if (!mSymKey.Assign(aSymKey)) {
449 0 : return NS_ERROR_OUT_OF_MEMORY;
450 : }
451 :
452 0 : return NS_OK;
453 : }
454 :
455 : nsresult
456 0 : CryptoKey::SetPrivateKey(SECKEYPrivateKey* aPrivateKey)
457 : {
458 0 : nsNSSShutDownPreventionLock locker;
459 :
460 0 : if (!aPrivateKey || isAlreadyShutDown()) {
461 0 : mPrivateKey = nullptr;
462 0 : return NS_OK;
463 : }
464 :
465 0 : mPrivateKey = UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(aPrivateKey));
466 0 : return mPrivateKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
467 : }
468 :
469 : nsresult
470 0 : CryptoKey::SetPublicKey(SECKEYPublicKey* aPublicKey)
471 : {
472 0 : nsNSSShutDownPreventionLock locker;
473 :
474 0 : if (!aPublicKey || isAlreadyShutDown()) {
475 0 : mPublicKey = nullptr;
476 0 : return NS_OK;
477 : }
478 :
479 0 : mPublicKey = UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(aPublicKey));
480 0 : return mPublicKey ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
481 : }
482 :
483 : const CryptoBuffer&
484 0 : CryptoKey::GetSymKey() const
485 : {
486 0 : return mSymKey;
487 : }
488 :
489 : UniqueSECKEYPrivateKey
490 0 : CryptoKey::GetPrivateKey() const
491 : {
492 0 : nsNSSShutDownPreventionLock locker;
493 0 : if (!mPrivateKey || isAlreadyShutDown()) {
494 0 : return nullptr;
495 : }
496 0 : return UniqueSECKEYPrivateKey(SECKEY_CopyPrivateKey(mPrivateKey.get()));
497 : }
498 :
499 : UniqueSECKEYPublicKey
500 0 : CryptoKey::GetPublicKey() const
501 : {
502 0 : nsNSSShutDownPreventionLock locker;
503 0 : if (!mPublicKey || isAlreadyShutDown()) {
504 0 : return nullptr;
505 : }
506 0 : return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(mPublicKey.get()));
507 : }
508 :
509 0 : void CryptoKey::virtualDestroyNSSReference()
510 : {
511 0 : destructorSafeDestroyNSSReference();
512 0 : }
513 :
514 0 : void CryptoKey::destructorSafeDestroyNSSReference()
515 : {
516 0 : mPrivateKey = nullptr;
517 0 : mPublicKey = nullptr;
518 0 : }
519 :
520 :
521 : // Serialization and deserialization convenience methods
522 :
523 : UniqueSECKEYPrivateKey
524 0 : CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData,
525 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
526 : {
527 0 : UniquePK11SlotInfo slot(PK11_GetInternalSlot());
528 0 : if (!slot) {
529 0 : return nullptr;
530 : }
531 :
532 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
533 0 : if (!arena) {
534 0 : return nullptr;
535 : }
536 :
537 0 : SECItem pkcs8Item = { siBuffer, nullptr, 0 };
538 0 : if (!aKeyData.ToSECItem(arena.get(), &pkcs8Item)) {
539 0 : return nullptr;
540 : }
541 :
542 : // Allow everything, we enforce usage ourselves
543 0 : unsigned int usage = KU_ALL;
544 :
545 : SECKEYPrivateKey* privKey;
546 0 : SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
547 : slot.get(), &pkcs8Item, nullptr, nullptr, false, false,
548 0 : usage, &privKey, nullptr);
549 :
550 0 : if (rv == SECFailure) {
551 0 : return nullptr;
552 : }
553 :
554 0 : return UniqueSECKEYPrivateKey(privKey);
555 : }
556 :
557 : UniqueSECKEYPublicKey
558 0 : CryptoKey::PublicKeyFromSpki(CryptoBuffer& aKeyData,
559 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
560 : {
561 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
562 0 : if (!arena) {
563 0 : return nullptr;
564 : }
565 :
566 0 : SECItem spkiItem = { siBuffer, nullptr, 0 };
567 0 : if (!aKeyData.ToSECItem(arena.get(), &spkiItem)) {
568 0 : return nullptr;
569 : }
570 :
571 : UniqueCERTSubjectPublicKeyInfo spki(
572 0 : SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
573 0 : if (!spki) {
574 0 : return nullptr;
575 : }
576 :
577 0 : bool isECDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_EC_DH,
578 0 : &spki->algorithm.algorithm);
579 0 : bool isDHAlgorithm = SECITEM_ItemsAreEqual(&SEC_OID_DATA_DH_KEY_AGREEMENT,
580 0 : &spki->algorithm.algorithm);
581 :
582 : // Check for |id-ecDH| and |dhKeyAgreement|. Per the WebCrypto spec we must
583 : // support these OIDs but NSS does unfortunately not know about them. Let's
584 : // change the algorithm to |id-ecPublicKey| or |dhPublicKey| to make NSS happy.
585 0 : if (isECDHAlgorithm || isDHAlgorithm) {
586 0 : SECOidTag oid = SEC_OID_UNKNOWN;
587 0 : if (isECDHAlgorithm) {
588 0 : oid = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
589 0 : } else if (isDHAlgorithm) {
590 0 : oid = SEC_OID_X942_DIFFIE_HELMAN_KEY;
591 : } else {
592 0 : MOZ_ASSERT(false);
593 : }
594 :
595 0 : SECOidData* oidData = SECOID_FindOIDByTag(oid);
596 0 : if (!oidData) {
597 0 : return nullptr;
598 : }
599 :
600 0 : SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
601 0 : &oidData->oid);
602 0 : if (rv != SECSuccess) {
603 0 : return nullptr;
604 : }
605 : }
606 :
607 0 : UniqueSECKEYPublicKey tmp(SECKEY_ExtractPublicKey(spki.get()));
608 0 : if (!tmp.get() || !PublicKeyValid(tmp.get())) {
609 0 : return nullptr;
610 : }
611 :
612 0 : return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(tmp.get()));
613 : }
614 :
615 : nsresult
616 0 : CryptoKey::PrivateKeyToPkcs8(SECKEYPrivateKey* aPrivKey,
617 : CryptoBuffer& aRetVal,
618 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
619 : {
620 0 : UniqueSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(aPrivKey, nullptr));
621 0 : if (!pkcs8Item.get()) {
622 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
623 : }
624 0 : if (!aRetVal.Assign(pkcs8Item.get())) {
625 0 : return NS_ERROR_DOM_OPERATION_ERR;
626 : }
627 0 : return NS_OK;
628 : }
629 :
630 : nsresult
631 0 : PublicDhKeyToSpki(SECKEYPublicKey* aPubKey,
632 : CERTSubjectPublicKeyInfo* aSpki)
633 : {
634 0 : SECItem* params = ::SECITEM_AllocItem(aSpki->arena, nullptr, 0);
635 0 : if (!params) {
636 0 : return NS_ERROR_DOM_OPERATION_ERR;
637 : }
638 :
639 0 : SECItem* rvItem = SEC_ASN1EncodeItem(aSpki->arena, params, aPubKey,
640 0 : SECKEY_DHParamKeyTemplate);
641 0 : if (!rvItem) {
642 0 : return NS_ERROR_DOM_OPERATION_ERR;
643 : }
644 :
645 0 : SECStatus rv = SECOID_SetAlgorithmID(aSpki->arena, &aSpki->algorithm,
646 0 : SEC_OID_X942_DIFFIE_HELMAN_KEY, params);
647 0 : if (rv != SECSuccess) {
648 0 : return NS_ERROR_DOM_OPERATION_ERR;
649 : }
650 :
651 0 : rvItem = SEC_ASN1EncodeItem(aSpki->arena, &aSpki->subjectPublicKey, aPubKey,
652 0 : SECKEY_DHPublicKeyTemplate);
653 0 : if (!rvItem) {
654 0 : return NS_ERROR_DOM_OPERATION_ERR;
655 : }
656 :
657 : // The public value is a BIT_STRING encoded as an INTEGER. After encoding
658 : // an INT we need to adjust the length to reflect the number of bits.
659 0 : aSpki->subjectPublicKey.len <<= 3;
660 :
661 0 : return NS_OK;
662 : }
663 :
664 : nsresult
665 0 : CryptoKey::PublicKeyToSpki(SECKEYPublicKey* aPubKey,
666 : CryptoBuffer& aRetVal,
667 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
668 : {
669 0 : UniqueCERTSubjectPublicKeyInfo spki;
670 :
671 : // NSS doesn't support exporting DH public keys.
672 0 : if (aPubKey->keyType == dhKey) {
673 : // Mimic the behavior of SECKEY_CreateSubjectPublicKeyInfo() and create
674 : // a new arena for the SPKI object.
675 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
676 0 : if (!arena) {
677 0 : return NS_ERROR_DOM_OPERATION_ERR;
678 : }
679 :
680 0 : spki.reset(PORT_ArenaZNew(arena.get(), CERTSubjectPublicKeyInfo));
681 0 : if (!spki) {
682 0 : return NS_ERROR_DOM_OPERATION_ERR;
683 : }
684 :
685 : // Assign |arena| to |spki| and null the variable afterwards so that the
686 : // arena created above that holds the SPKI object is free'd when |spki|
687 : // goes out of scope, not when |arena| does.
688 0 : spki->arena = arena.release();
689 :
690 0 : nsresult rv = PublicDhKeyToSpki(aPubKey, spki.get());
691 0 : NS_ENSURE_SUCCESS(rv, rv);
692 : } else {
693 0 : spki.reset(SECKEY_CreateSubjectPublicKeyInfo(aPubKey));
694 0 : if (!spki) {
695 0 : return NS_ERROR_DOM_OPERATION_ERR;
696 : }
697 : }
698 :
699 : // Per WebCrypto spec we must export ECDH SPKIs with the algorithm OID
700 : // id-ecDH (1.3.132.112) and DH SPKIs with OID dhKeyAgreement
701 : // (1.2.840.113549.1.3.1). NSS doesn't know about these OIDs and there is
702 : // no way to specify the algorithm to use when exporting a public key.
703 0 : if (aPubKey->keyType == ecKey || aPubKey->keyType == dhKey) {
704 0 : const SECItem* oidData = nullptr;
705 0 : if (aPubKey->keyType == ecKey) {
706 0 : oidData = &SEC_OID_DATA_EC_DH;
707 0 : } else if (aPubKey->keyType == dhKey) {
708 0 : oidData = &SEC_OID_DATA_DH_KEY_AGREEMENT;
709 : } else {
710 0 : MOZ_ASSERT(false);
711 : }
712 :
713 0 : SECStatus rv = SECITEM_CopyItem(spki->arena, &spki->algorithm.algorithm,
714 0 : oidData);
715 0 : if (rv != SECSuccess) {
716 0 : return NS_ERROR_DOM_OPERATION_ERR;
717 : }
718 : }
719 :
720 0 : const SEC_ASN1Template* tpl = SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate);
721 0 : UniqueSECItem spkiItem(SEC_ASN1EncodeItem(nullptr, nullptr, spki.get(), tpl));
722 :
723 0 : if (!aRetVal.Assign(spkiItem.get())) {
724 0 : return NS_ERROR_DOM_OPERATION_ERR;
725 : }
726 0 : return NS_OK;
727 : }
728 :
729 : SECItem*
730 0 : CreateECPointForCoordinates(const CryptoBuffer& aX,
731 : const CryptoBuffer& aY,
732 : PLArenaPool* aArena)
733 : {
734 : // Check that both points have the same length.
735 0 : if (aX.Length() != aY.Length()) {
736 0 : return nullptr;
737 : }
738 :
739 : // Create point.
740 0 : SECItem* point = ::SECITEM_AllocItem(aArena, nullptr, aX.Length() + aY.Length() + 1);
741 0 : if (!point) {
742 0 : return nullptr;
743 : }
744 :
745 : // Set point data.
746 0 : point->data[0] = EC_POINT_FORM_UNCOMPRESSED;
747 0 : memcpy(point->data + 1, aX.Elements(), aX.Length());
748 0 : memcpy(point->data + 1 + aX.Length(), aY.Elements(), aY.Length());
749 :
750 0 : return point;
751 : }
752 :
753 : UniqueSECKEYPrivateKey
754 0 : CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk,
755 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
756 : {
757 0 : CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY;
758 0 : CK_BBOOL falseValue = CK_FALSE;
759 :
760 0 : if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
761 : // Verify that all of the required parameters are present
762 0 : CryptoBuffer x, y, d;
763 0 : if (!aJwk.mCrv.WasPassed() ||
764 0 : !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
765 0 : !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value())) ||
766 0 : !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value()))) {
767 0 : return nullptr;
768 : }
769 :
770 0 : nsString namedCurve;
771 0 : if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
772 0 : return nullptr;
773 : }
774 :
775 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
776 0 : if (!arena) {
777 0 : return nullptr;
778 : }
779 :
780 : // Create parameters.
781 0 : SECItem* params = CreateECParamsForCurve(namedCurve, arena.get());
782 0 : if (!params) {
783 0 : return nullptr;
784 : }
785 :
786 0 : SECItem* ecPoint = CreateECPointForCoordinates(x, y, arena.get());
787 0 : if (!ecPoint) {
788 0 : return nullptr;
789 : }
790 :
791 : // Populate template from parameters
792 0 : CK_KEY_TYPE ecValue = CKK_EC;
793 : CK_ATTRIBUTE keyTemplate[9] = {
794 : { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
795 : { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) },
796 : { CKA_TOKEN, &falseValue, sizeof(falseValue) },
797 : { CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
798 : { CKA_PRIVATE, &falseValue, sizeof(falseValue) },
799 : // PrivateKeyFromPrivateKeyTemplate sets the ID.
800 : { CKA_ID, nullptr, 0 },
801 0 : { CKA_EC_PARAMS, params->data, params->len },
802 0 : { CKA_EC_POINT, ecPoint->data, ecPoint->len },
803 0 : { CKA_VALUE, (void*) d.Elements(), (CK_ULONG) d.Length() },
804 0 : };
805 :
806 : return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
807 0 : ArrayLength(keyTemplate));
808 : }
809 :
810 0 : if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
811 : // Verify that all of the required parameters are present
812 0 : CryptoBuffer n, e, d, p, q, dp, dq, qi;
813 0 : if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
814 0 : !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) ||
815 0 : !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) ||
816 0 : !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) ||
817 0 : !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) ||
818 0 : !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) ||
819 0 : !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) ||
820 0 : !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) {
821 0 : return nullptr;
822 : }
823 :
824 : // Populate template from parameters
825 0 : CK_KEY_TYPE rsaValue = CKK_RSA;
826 : CK_ATTRIBUTE keyTemplate[14] = {
827 : { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) },
828 : { CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) },
829 : { CKA_TOKEN, &falseValue, sizeof(falseValue) },
830 : { CKA_SENSITIVE, &falseValue, sizeof(falseValue) },
831 : { CKA_PRIVATE, &falseValue, sizeof(falseValue) },
832 : // PrivateKeyFromPrivateKeyTemplate sets the ID.
833 : { CKA_ID, nullptr, 0 },
834 0 : { CKA_MODULUS, (void*) n.Elements(), (CK_ULONG) n.Length() },
835 0 : { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), (CK_ULONG) e.Length() },
836 0 : { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), (CK_ULONG) d.Length() },
837 0 : { CKA_PRIME_1, (void*) p.Elements(), (CK_ULONG) p.Length() },
838 0 : { CKA_PRIME_2, (void*) q.Elements(), (CK_ULONG) q.Length() },
839 0 : { CKA_EXPONENT_1, (void*) dp.Elements(), (CK_ULONG) dp.Length() },
840 0 : { CKA_EXPONENT_2, (void*) dq.Elements(), (CK_ULONG) dq.Length() },
841 0 : { CKA_COEFFICIENT, (void*) qi.Elements(), (CK_ULONG) qi.Length() },
842 0 : };
843 :
844 : return PrivateKeyFromPrivateKeyTemplate(keyTemplate,
845 0 : ArrayLength(keyTemplate));
846 : }
847 :
848 0 : return nullptr;
849 : }
850 :
851 0 : bool ReadAndEncodeAttribute(SECKEYPrivateKey* aKey,
852 : CK_ATTRIBUTE_TYPE aAttribute,
853 : Optional<nsString>& aDst)
854 : {
855 0 : ScopedAutoSECItem item;
856 0 : if (PK11_ReadRawAttribute(PK11_TypePrivKey, aKey, aAttribute, &item)
857 : != SECSuccess) {
858 0 : return false;
859 : }
860 :
861 0 : CryptoBuffer buffer;
862 0 : if (!buffer.Assign(&item)) {
863 0 : return false;
864 : }
865 :
866 0 : if (NS_FAILED(buffer.ToJwkBase64(aDst.Value()))) {
867 0 : return false;
868 : }
869 :
870 0 : return true;
871 : }
872 :
873 : bool
874 0 : ECKeyToJwk(const PK11ObjectType aKeyType, void* aKey, const SECItem* aEcParams,
875 : const SECItem* aPublicValue, JsonWebKey& aRetVal)
876 : {
877 0 : aRetVal.mX.Construct();
878 0 : aRetVal.mY.Construct();
879 :
880 : // Check that the given EC parameters are valid.
881 0 : if (!CheckEncodedECParameters(aEcParams)) {
882 0 : return false;
883 : }
884 :
885 : // Construct the OID tag.
886 0 : SECItem oid = { siBuffer, nullptr, 0 };
887 0 : oid.len = aEcParams->data[1];
888 0 : oid.data = aEcParams->data + 2;
889 :
890 : uint32_t flen;
891 0 : switch (SECOID_FindOIDTag(&oid)) {
892 : case SEC_OID_SECG_EC_SECP256R1:
893 0 : flen = 32; // bytes
894 0 : aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P256));
895 0 : break;
896 : case SEC_OID_SECG_EC_SECP384R1:
897 0 : flen = 48; // bytes
898 0 : aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P384));
899 0 : break;
900 : case SEC_OID_SECG_EC_SECP521R1:
901 0 : flen = 66; // bytes
902 0 : aRetVal.mCrv.Construct(NS_LITERAL_STRING(WEBCRYPTO_NAMED_CURVE_P521));
903 0 : break;
904 : default:
905 0 : return false;
906 : }
907 :
908 : // No support for compressed points.
909 0 : if (aPublicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) {
910 0 : return false;
911 : }
912 :
913 : // Check length of uncompressed point coordinates.
914 0 : if (aPublicValue->len != (2 * flen + 1)) {
915 0 : return false;
916 : }
917 :
918 0 : UniqueSECItem ecPointX(::SECITEM_AllocItem(nullptr, nullptr, flen));
919 0 : UniqueSECItem ecPointY(::SECITEM_AllocItem(nullptr, nullptr, flen));
920 0 : if (!ecPointX || !ecPointY) {
921 0 : return false;
922 : }
923 :
924 : // Extract point data.
925 0 : memcpy(ecPointX->data, aPublicValue->data + 1, flen);
926 0 : memcpy(ecPointY->data, aPublicValue->data + 1 + flen, flen);
927 :
928 0 : CryptoBuffer x, y;
929 0 : if (!x.Assign(ecPointX.get()) ||
930 0 : NS_FAILED(x.ToJwkBase64(aRetVal.mX.Value())) ||
931 0 : !y.Assign(ecPointY.get()) ||
932 0 : NS_FAILED(y.ToJwkBase64(aRetVal.mY.Value()))) {
933 0 : return false;
934 : }
935 :
936 0 : aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_EC);
937 0 : return true;
938 : }
939 :
940 : nsresult
941 0 : CryptoKey::PrivateKeyToJwk(SECKEYPrivateKey* aPrivKey,
942 : JsonWebKey& aRetVal,
943 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
944 : {
945 0 : switch (aPrivKey->keyType) {
946 : case rsaKey: {
947 0 : aRetVal.mN.Construct();
948 0 : aRetVal.mE.Construct();
949 0 : aRetVal.mD.Construct();
950 0 : aRetVal.mP.Construct();
951 0 : aRetVal.mQ.Construct();
952 0 : aRetVal.mDp.Construct();
953 0 : aRetVal.mDq.Construct();
954 0 : aRetVal.mQi.Construct();
955 :
956 0 : if (!ReadAndEncodeAttribute(aPrivKey, CKA_MODULUS, aRetVal.mN) ||
957 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_PUBLIC_EXPONENT, aRetVal.mE) ||
958 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_PRIVATE_EXPONENT, aRetVal.mD) ||
959 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_1, aRetVal.mP) ||
960 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_PRIME_2, aRetVal.mQ) ||
961 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_1, aRetVal.mDp) ||
962 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_EXPONENT_2, aRetVal.mDq) ||
963 0 : !ReadAndEncodeAttribute(aPrivKey, CKA_COEFFICIENT, aRetVal.mQi)) {
964 0 : return NS_ERROR_DOM_OPERATION_ERR;
965 : }
966 :
967 0 : aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
968 0 : return NS_OK;
969 : }
970 : case ecKey: {
971 : // Read EC params.
972 0 : ScopedAutoSECItem params;
973 : SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey,
974 0 : CKA_EC_PARAMS, ¶ms);
975 0 : if (rv != SECSuccess) {
976 0 : return NS_ERROR_DOM_OPERATION_ERR;
977 : }
978 :
979 : // Read public point Q.
980 0 : ScopedAutoSECItem ecPoint;
981 : rv = PK11_ReadRawAttribute(PK11_TypePrivKey, aPrivKey, CKA_EC_POINT,
982 0 : &ecPoint);
983 0 : if (rv != SECSuccess) {
984 0 : return NS_ERROR_DOM_OPERATION_ERR;
985 : }
986 :
987 0 : if (!ECKeyToJwk(PK11_TypePrivKey, aPrivKey, ¶ms, &ecPoint, aRetVal)) {
988 0 : return NS_ERROR_DOM_OPERATION_ERR;
989 : }
990 :
991 0 : aRetVal.mD.Construct();
992 :
993 : // Read private value.
994 0 : if (!ReadAndEncodeAttribute(aPrivKey, CKA_VALUE, aRetVal.mD)) {
995 0 : return NS_ERROR_DOM_OPERATION_ERR;
996 : }
997 :
998 0 : return NS_OK;
999 : }
1000 : default:
1001 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1002 : }
1003 : }
1004 :
1005 : UniqueSECKEYPublicKey
1006 0 : CreateECPublicKey(const SECItem* aKeyData, const nsString& aNamedCurve)
1007 : {
1008 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1009 0 : if (!arena) {
1010 0 : return nullptr;
1011 : }
1012 :
1013 : // It's important that this be a UniqueSECKEYPublicKey, as this ensures that
1014 : // SECKEY_DestroyPublicKey will be called on it. If this doesn't happen, when
1015 : // CryptoKey::PublicKeyValid is called on it and it gets moved to the internal
1016 : // PKCS#11 slot, it will leak a reference to the slot.
1017 0 : UniqueSECKEYPublicKey key(PORT_ArenaZNew(arena.get(), SECKEYPublicKey));
1018 0 : if (!key) {
1019 0 : return nullptr;
1020 : }
1021 :
1022 0 : key->arena = nullptr; // key doesn't own the arena; it won't get double-freed
1023 0 : key->keyType = ecKey;
1024 0 : key->pkcs11Slot = nullptr;
1025 0 : key->pkcs11ID = CK_INVALID_HANDLE;
1026 :
1027 : // Create curve parameters.
1028 0 : SECItem* params = CreateECParamsForCurve(aNamedCurve, arena.get());
1029 0 : if (!params) {
1030 0 : return nullptr;
1031 : }
1032 0 : key->u.ec.DEREncodedParams = *params;
1033 :
1034 : // Set public point.
1035 0 : key->u.ec.publicValue = *aKeyData;
1036 :
1037 : // Ensure the given point is on the curve.
1038 0 : if (!CryptoKey::PublicKeyValid(key.get())) {
1039 0 : return nullptr;
1040 : }
1041 :
1042 0 : return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key.get()));
1043 : }
1044 :
1045 : UniqueSECKEYPublicKey
1046 0 : CryptoKey::PublicKeyFromJwk(const JsonWebKey& aJwk,
1047 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1048 : {
1049 0 : if (aJwk.mKty.EqualsLiteral(JWK_TYPE_RSA)) {
1050 : // Verify that all of the required parameters are present
1051 0 : CryptoBuffer n, e;
1052 0 : if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) ||
1053 0 : !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value()))) {
1054 0 : return nullptr;
1055 : }
1056 :
1057 : // Transcode to a DER RSAPublicKey structure
1058 : struct RSAPublicKeyData {
1059 : SECItem n;
1060 : SECItem e;
1061 : };
1062 : const RSAPublicKeyData input = {
1063 0 : { siUnsignedInteger, n.Elements(), (unsigned int) n.Length() },
1064 0 : { siUnsignedInteger, e.Elements(), (unsigned int) e.Length() }
1065 0 : };
1066 : const SEC_ASN1Template rsaPublicKeyTemplate[] = {
1067 : {SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(RSAPublicKeyData)},
1068 : {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, n),},
1069 : {SEC_ASN1_INTEGER, offsetof(RSAPublicKeyData, e),},
1070 : {0,}
1071 0 : };
1072 :
1073 : UniqueSECItem pkDer(SEC_ASN1EncodeItem(nullptr, nullptr, &input,
1074 0 : rsaPublicKeyTemplate));
1075 0 : if (!pkDer.get()) {
1076 0 : return nullptr;
1077 : }
1078 :
1079 0 : return UniqueSECKEYPublicKey(SECKEY_ImportDERPublicKey(pkDer.get(), CKK_RSA));
1080 : }
1081 :
1082 0 : if (aJwk.mKty.EqualsLiteral(JWK_TYPE_EC)) {
1083 : // Verify that all of the required parameters are present
1084 0 : CryptoBuffer x, y;
1085 0 : if (!aJwk.mCrv.WasPassed() ||
1086 0 : !aJwk.mX.WasPassed() || NS_FAILED(x.FromJwkBase64(aJwk.mX.Value())) ||
1087 0 : !aJwk.mY.WasPassed() || NS_FAILED(y.FromJwkBase64(aJwk.mY.Value()))) {
1088 0 : return nullptr;
1089 : }
1090 :
1091 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1092 0 : if (!arena) {
1093 0 : return nullptr;
1094 : }
1095 :
1096 : // Create point.
1097 0 : SECItem* point = CreateECPointForCoordinates(x, y, arena.get());
1098 0 : if (!point) {
1099 0 : return nullptr;
1100 : }
1101 :
1102 0 : nsString namedCurve;
1103 0 : if (!NormalizeToken(aJwk.mCrv.Value(), namedCurve)) {
1104 0 : return nullptr;
1105 : }
1106 :
1107 0 : return CreateECPublicKey(point, namedCurve);
1108 : }
1109 :
1110 0 : return nullptr;
1111 : }
1112 :
1113 : nsresult
1114 0 : CryptoKey::PublicKeyToJwk(SECKEYPublicKey* aPubKey,
1115 : JsonWebKey& aRetVal,
1116 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1117 : {
1118 0 : switch (aPubKey->keyType) {
1119 : case rsaKey: {
1120 0 : CryptoBuffer n, e;
1121 0 : aRetVal.mN.Construct();
1122 0 : aRetVal.mE.Construct();
1123 :
1124 0 : if (!n.Assign(&aPubKey->u.rsa.modulus) ||
1125 0 : !e.Assign(&aPubKey->u.rsa.publicExponent) ||
1126 0 : NS_FAILED(n.ToJwkBase64(aRetVal.mN.Value())) ||
1127 0 : NS_FAILED(e.ToJwkBase64(aRetVal.mE.Value()))) {
1128 0 : return NS_ERROR_DOM_OPERATION_ERR;
1129 : }
1130 :
1131 0 : aRetVal.mKty = NS_LITERAL_STRING(JWK_TYPE_RSA);
1132 0 : return NS_OK;
1133 : }
1134 : case ecKey:
1135 0 : if (!ECKeyToJwk(PK11_TypePubKey, aPubKey, &aPubKey->u.ec.DEREncodedParams,
1136 0 : &aPubKey->u.ec.publicValue, aRetVal)) {
1137 0 : return NS_ERROR_DOM_OPERATION_ERR;
1138 : }
1139 0 : return NS_OK;
1140 : default:
1141 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1142 : }
1143 : }
1144 :
1145 : UniqueSECKEYPublicKey
1146 0 : CryptoKey::PublicDhKeyFromRaw(CryptoBuffer& aKeyData,
1147 : const CryptoBuffer& aPrime,
1148 : const CryptoBuffer& aGenerator,
1149 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1150 : {
1151 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1152 0 : if (!arena) {
1153 0 : return nullptr;
1154 : }
1155 :
1156 0 : SECKEYPublicKey* key = PORT_ArenaZNew(arena.get(), SECKEYPublicKey);
1157 0 : if (!key) {
1158 0 : return nullptr;
1159 : }
1160 :
1161 0 : key->keyType = dhKey;
1162 0 : key->pkcs11Slot = nullptr;
1163 0 : key->pkcs11ID = CK_INVALID_HANDLE;
1164 :
1165 : // Set DH public key params.
1166 0 : if (!aPrime.ToSECItem(arena.get(), &key->u.dh.prime) ||
1167 0 : !aGenerator.ToSECItem(arena.get(), &key->u.dh.base) ||
1168 0 : !aKeyData.ToSECItem(arena.get(), &key->u.dh.publicValue)) {
1169 0 : return nullptr;
1170 : }
1171 :
1172 0 : key->u.dh.prime.type = siUnsignedInteger;
1173 0 : key->u.dh.base.type = siUnsignedInteger;
1174 0 : key->u.dh.publicValue.type = siUnsignedInteger;
1175 :
1176 0 : return UniqueSECKEYPublicKey(SECKEY_CopyPublicKey(key));
1177 : }
1178 :
1179 : nsresult
1180 0 : CryptoKey::PublicDhKeyToRaw(SECKEYPublicKey* aPubKey,
1181 : CryptoBuffer& aRetVal,
1182 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1183 : {
1184 0 : if (!aRetVal.Assign(&aPubKey->u.dh.publicValue)) {
1185 0 : return NS_ERROR_DOM_OPERATION_ERR;
1186 : }
1187 0 : return NS_OK;
1188 : }
1189 :
1190 : UniqueSECKEYPublicKey
1191 0 : CryptoKey::PublicECKeyFromRaw(CryptoBuffer& aKeyData,
1192 : const nsString& aNamedCurve,
1193 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1194 : {
1195 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
1196 0 : if (!arena) {
1197 0 : return nullptr;
1198 : }
1199 :
1200 0 : SECItem rawItem = { siBuffer, nullptr, 0 };
1201 0 : if (!aKeyData.ToSECItem(arena.get(), &rawItem)) {
1202 0 : return nullptr;
1203 : }
1204 :
1205 : uint32_t flen;
1206 0 : if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) {
1207 0 : flen = 32; // bytes
1208 0 : } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) {
1209 0 : flen = 48; // bytes
1210 0 : } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) {
1211 0 : flen = 66; // bytes
1212 : } else {
1213 0 : return nullptr;
1214 : }
1215 :
1216 : // Check length of uncompressed point coordinates. There are 2 field elements
1217 : // and a leading point form octet (which must EC_POINT_FORM_UNCOMPRESSED).
1218 0 : if (rawItem.len != (2 * flen + 1)) {
1219 0 : return nullptr;
1220 : }
1221 :
1222 : // No support for compressed points.
1223 0 : if (rawItem.data[0] != EC_POINT_FORM_UNCOMPRESSED) {
1224 0 : return nullptr;
1225 : }
1226 :
1227 0 : return CreateECPublicKey(&rawItem, aNamedCurve);
1228 : }
1229 :
1230 : nsresult
1231 0 : CryptoKey::PublicECKeyToRaw(SECKEYPublicKey* aPubKey,
1232 : CryptoBuffer& aRetVal,
1233 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
1234 : {
1235 0 : if (!aRetVal.Assign(&aPubKey->u.ec.publicValue)) {
1236 0 : return NS_ERROR_DOM_OPERATION_ERR;
1237 : }
1238 0 : return NS_OK;
1239 : }
1240 :
1241 : bool
1242 0 : CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey)
1243 : {
1244 0 : UniquePK11SlotInfo slot(PK11_GetInternalSlot());
1245 0 : if (!slot.get()) {
1246 0 : return false;
1247 : }
1248 :
1249 : // This assumes that NSS checks the validity of a public key when
1250 : // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE
1251 : // if it is invalid.
1252 0 : CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot.get(), aPubKey, PR_FALSE);
1253 0 : if (id == CK_INVALID_HANDLE) {
1254 0 : return false;
1255 : }
1256 :
1257 0 : SECStatus rv = PK11_DestroyObject(slot.get(), id);
1258 0 : return (rv == SECSuccess);
1259 : }
1260 :
1261 : bool
1262 0 : CryptoKey::WriteStructuredClone(JSStructuredCloneWriter* aWriter) const
1263 : {
1264 0 : nsNSSShutDownPreventionLock locker;
1265 0 : if (isAlreadyShutDown()) {
1266 0 : return false;
1267 : }
1268 :
1269 : // Write in five pieces
1270 : // 1. Attributes
1271 : // 2. Symmetric key as raw (if present)
1272 : // 3. Private key as pkcs8 (if present)
1273 : // 4. Public key as spki (if present)
1274 : // 5. Algorithm in whatever form it chooses
1275 0 : CryptoBuffer priv, pub;
1276 :
1277 0 : if (mPrivateKey) {
1278 0 : if (NS_FAILED(CryptoKey::PrivateKeyToPkcs8(mPrivateKey.get(), priv,
1279 : locker))) {
1280 0 : return false;
1281 : }
1282 : }
1283 :
1284 0 : if (mPublicKey) {
1285 0 : if (NS_FAILED(CryptoKey::PublicKeyToSpki(mPublicKey.get(), pub, locker))) {
1286 0 : return false;
1287 : }
1288 : }
1289 :
1290 0 : return JS_WriteUint32Pair(aWriter, mAttributes, CRYPTOKEY_SC_VERSION) &&
1291 0 : WriteBuffer(aWriter, mSymKey) &&
1292 0 : WriteBuffer(aWriter, priv) &&
1293 0 : WriteBuffer(aWriter, pub) &&
1294 0 : mAlgorithm.WriteStructuredClone(aWriter);
1295 : }
1296 :
1297 : bool
1298 0 : CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader)
1299 : {
1300 0 : nsNSSShutDownPreventionLock locker;
1301 0 : if (isAlreadyShutDown()) {
1302 0 : return false;
1303 : }
1304 :
1305 : // Ensure that NSS is initialized.
1306 0 : if (!EnsureNSSInitializedChromeOrContent()) {
1307 0 : return false;
1308 : }
1309 :
1310 : uint32_t version;
1311 0 : CryptoBuffer sym, priv, pub;
1312 :
1313 0 : bool read = JS_ReadUint32Pair(aReader, &mAttributes, &version) &&
1314 0 : (version == CRYPTOKEY_SC_VERSION) &&
1315 0 : ReadBuffer(aReader, sym) &&
1316 0 : ReadBuffer(aReader, priv) &&
1317 0 : ReadBuffer(aReader, pub) &&
1318 0 : mAlgorithm.ReadStructuredClone(aReader);
1319 0 : if (!read) {
1320 0 : return false;
1321 : }
1322 :
1323 0 : if (sym.Length() > 0 && !mSymKey.Assign(sym)) {
1324 0 : return false;
1325 : }
1326 0 : if (priv.Length() > 0) {
1327 0 : mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker);
1328 : }
1329 0 : if (pub.Length() > 0) {
1330 0 : mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker);
1331 : }
1332 :
1333 : // Ensure that what we've read is consistent
1334 : // If the attributes indicate a key type, should have a key of that type
1335 0 : if (!((GetKeyType() == SECRET && mSymKey.Length() > 0) ||
1336 0 : (GetKeyType() == PRIVATE && mPrivateKey) ||
1337 0 : (GetKeyType() == PUBLIC && mPublicKey))) {
1338 0 : return false;
1339 : }
1340 :
1341 0 : return true;
1342 : }
1343 :
1344 : } // namespace dom
1345 : } // namespace mozilla
|