Coverage Report

Created: 2018-09-25 14:53

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