Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/webauthn/U2FSoftTokenManager.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/dom/U2FSoftTokenManager.h"
8
#include "CryptoBuffer.h"
9
#include "mozilla/Base64.h"
10
#include "mozilla/Casting.h"
11
#include "nsNSSComponent.h"
12
#include "nsThreadUtils.h"
13
#include "pk11pub.h"
14
#include "prerror.h"
15
#include "secerr.h"
16
#include "WebCryptoCommon.h"
17
18
0
#define PREF_U2F_NSSTOKEN_COUNTER "security.webauth.softtoken_counter"
19
20
namespace mozilla {
21
namespace dom {
22
23
using namespace mozilla;
24
using mozilla::dom::CreateECParamsForCurve;
25
26
const nsCString U2FSoftTokenManager::mSecretNickname =
27
  NS_LITERAL_CSTRING("U2F_NSSTOKEN");
28
29
namespace {
30
NS_NAMED_LITERAL_CSTRING(kAttestCertSubjectName, "CN=Firefox U2F Soft Token");
31
32
// This U2F-compatible soft token uses FIDO U2F-compatible ECDSA keypairs
33
// on the SEC_OID_SECG_EC_SECP256R1 curve. When asked to Register, it will
34
// generate and return a new keypair KP, where the private component is wrapped
35
// using AES-KW with the 128-bit mWrappingKey to make an opaque "key handle".
36
// In other words, Register yields { KP_pub, AES-KW(KP_priv, key=mWrappingKey) }
37
//
38
// The value mWrappingKey is long-lived; it is persisted as part of the NSS DB
39
// for the current profile. The attestation certificates that are produced are
40
// ephemeral to counteract profiling. They have little use for a soft-token
41
// at any rate, but are required by the specification.
42
43
const uint32_t kParamLen = 32;
44
const uint32_t kPublicKeyLen = 65;
45
const uint32_t kWrappedKeyBufLen = 256;
46
const uint32_t kWrappingKeyByteLen = 128/8;
47
const uint32_t kSaltByteLen = 64/8;
48
const uint32_t kVersion1KeyHandleLen = 162;
49
NS_NAMED_LITERAL_STRING(kEcAlgorithm, WEBCRYPTO_NAMED_CURVE_P256);
50
51
const PRTime kOneDay = PRTime(PR_USEC_PER_SEC)
52
  * PRTime(60)  // sec
53
  * PRTime(60)  // min
54
  * PRTime(24); // hours
55
const PRTime kExpirationSlack = kOneDay; // Pre-date for clock skew
56
const PRTime kExpirationLife = kOneDay;
57
58
static mozilla::LazyLogModule gNSSTokenLog("webauth_u2f");
59
60
enum SoftTokenHandle {
61
  Version1 = 0,
62
};
63
64
}
65
66
U2FSoftTokenManager::U2FSoftTokenManager(uint32_t aCounter)
67
  : mInitialized(false),
68
    mCounter(aCounter)
69
0
{}
70
71
/**
72
 * Gets the first key with the given nickname from the given slot. Any other
73
 * keys found are not returned.
74
 * PK11_GetNextSymKey() should not be called on the returned key.
75
 *
76
 * @param aSlot Slot to search.
77
 * @param aNickname Nickname the key should have.
78
 * @return The first key found. nullptr if no key could be found.
79
 */
80
static UniquePK11SymKey
81
GetSymKeyByNickname(const UniquePK11SlotInfo& aSlot, const nsCString& aNickname)
82
0
{
83
0
  MOZ_ASSERT(aSlot);
84
0
  if (NS_WARN_IF(!aSlot)) {
85
0
    return nullptr;
86
0
  }
87
0
88
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
89
0
          ("Searching for a symmetric key named %s", aNickname.get()));
90
0
91
0
  UniquePK11SymKey keyListHead(
92
0
    PK11_ListFixedKeysInSlot(aSlot.get(), const_cast<char*>(aNickname.get()),
93
0
                             /* wincx */ nullptr));
94
0
  if (NS_WARN_IF(!keyListHead)) {
95
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key not found."));
96
0
    return nullptr;
97
0
  }
98
0
99
0
  // Sanity check PK11_ListFixedKeysInSlot() only returns keys with the correct
100
0
  // nickname.
101
0
  MOZ_ASSERT(aNickname ==
102
0
             UniquePORTString(PK11_GetSymKeyNickname(keyListHead.get())).get());
103
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key found!"));
104
0
105
0
  // Free any remaining keys in the key list.
106
0
  UniquePK11SymKey freeKey(PK11_GetNextSymKey(keyListHead.get()));
107
0
  while (freeKey) {
108
0
    freeKey = UniquePK11SymKey(PK11_GetNextSymKey(freeKey.get()));
109
0
  }
110
0
111
0
  return keyListHead;
112
0
}
113
114
static nsresult
115
GenEcKeypair(const UniquePK11SlotInfo& aSlot,
116
             /*out*/ UniqueSECKEYPrivateKey& aPrivKey,
117
             /*out*/ UniqueSECKEYPublicKey& aPubKey)
118
0
{
119
0
  MOZ_ASSERT(aSlot);
120
0
  if (NS_WARN_IF(!aSlot)) {
121
0
    return NS_ERROR_INVALID_ARG;
122
0
  }
123
0
124
0
  UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
125
0
  if (NS_WARN_IF(!arena)) {
126
0
    return NS_ERROR_OUT_OF_MEMORY;
127
0
  }
128
0
129
0
  // Set the curve parameters; keyParams belongs to the arena memory space
130
0
  SECItem* keyParams = CreateECParamsForCurve(kEcAlgorithm, arena.get());
131
0
  if (NS_WARN_IF(!keyParams)) {
132
0
    return NS_ERROR_OUT_OF_MEMORY;
133
0
  }
134
0
135
0
  // Generate a key pair
136
0
  CK_MECHANISM_TYPE mechanism = CKM_EC_KEY_PAIR_GEN;
137
0
138
0
  SECKEYPublicKey* pubKeyRaw;
139
0
  aPrivKey = UniqueSECKEYPrivateKey(
140
0
    PK11_GenerateKeyPair(aSlot.get(), mechanism, keyParams, &pubKeyRaw,
141
0
                         /* ephemeral */ false, false,
142
0
                         /* wincx */ nullptr));
143
0
  aPubKey = UniqueSECKEYPublicKey(pubKeyRaw);
144
0
  pubKeyRaw = nullptr;
145
0
  if (NS_WARN_IF(!aPrivKey.get() || !aPubKey.get())) {
146
0
    return NS_ERROR_FAILURE;
147
0
  }
148
0
149
0
  // Check that the public key has the correct length
150
0
  if (NS_WARN_IF(aPubKey->u.ec.publicValue.len != kPublicKeyLen)) {
151
0
    return NS_ERROR_FAILURE;
152
0
  }
153
0
154
0
  return NS_OK;
155
0
}
156
157
nsresult
158
U2FSoftTokenManager::GetOrCreateWrappingKey(const UniquePK11SlotInfo& aSlot)
159
0
{
160
0
  MOZ_ASSERT(aSlot);
161
0
  if (NS_WARN_IF(!aSlot)) {
162
0
    return NS_ERROR_INVALID_ARG;
163
0
  }
164
0
165
0
  // Search for an existing wrapping key. If we find it,
166
0
  // store it for later and mark ourselves initialized.
167
0
  mWrappingKey = GetSymKeyByNickname(aSlot, mSecretNickname);
168
0
  if (mWrappingKey) {
169
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token Key found."));
170
0
    mInitialized = true;
171
0
    return NS_OK;
172
0
  }
173
0
174
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Info,
175
0
          ("No keys found. Generating new U2F Soft Token wrapping key."));
176
0
177
0
  // We did not find an existing wrapping key, so we generate one in the
178
0
  // persistent database (e.g, Token).
179
0
  mWrappingKey = UniquePK11SymKey(
180
0
    PK11_TokenKeyGenWithFlags(aSlot.get(), CKM_AES_KEY_GEN,
181
0
                              /* default params */ nullptr,
182
0
                              kWrappingKeyByteLen,
183
0
                              /* empty keyid */ nullptr,
184
0
                              /* flags */ CKF_WRAP | CKF_UNWRAP,
185
0
                              /* attributes */ PK11_ATTR_TOKEN |
186
0
                              PK11_ATTR_PRIVATE,
187
0
                              /* wincx */ nullptr));
188
0
189
0
  if (NS_WARN_IF(!mWrappingKey)) {
190
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
191
0
            ("Failed to store wrapping key, NSS error #%d", PORT_GetError()));
192
0
    return NS_ERROR_FAILURE;
193
0
  }
194
0
195
0
  SECStatus srv = PK11_SetSymKeyNickname(mWrappingKey.get(),
196
0
                                         mSecretNickname.get());
197
0
  if (NS_WARN_IF(srv != SECSuccess)) {
198
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
199
0
            ("Failed to set nickname, NSS error #%d", PORT_GetError()));
200
0
    return NS_ERROR_FAILURE;
201
0
  }
202
0
203
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
204
0
          ("Key stored, nickname set to %s.", mSecretNickname.get()));
205
0
206
0
  GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
207
0
                                         "dom::U2FSoftTokenManager::GetOrCreateWrappingKey",
208
0
                                           [] () {
209
0
                                             MOZ_ASSERT(NS_IsMainThread());
210
0
                                             Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, 0);
211
0
                                           }));
212
0
213
0
  return NS_OK;
214
0
}
215
216
static nsresult
217
GetAttestationCertificate(const UniquePK11SlotInfo& aSlot,
218
                          /*out*/ UniqueSECKEYPrivateKey& aAttestPrivKey,
219
                          /*out*/ UniqueCERTCertificate& aAttestCert)
220
0
{
221
0
  MOZ_ASSERT(aSlot);
222
0
  if (NS_WARN_IF(!aSlot)) {
223
0
    return NS_ERROR_INVALID_ARG;
224
0
  }
225
0
226
0
  UniqueSECKEYPublicKey pubKey;
227
0
228
0
  // Construct an ephemeral keypair for this Attestation Certificate
229
0
  nsresult rv = GenEcKeypair(aSlot, aAttestPrivKey, pubKey);
230
0
  if (NS_WARN_IF(NS_FAILED(rv) || !aAttestPrivKey || !pubKey)) {
231
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
232
0
            ("Failed to gen keypair, NSS error #%d", PORT_GetError()));
233
0
    return NS_ERROR_FAILURE;
234
0
  }
235
0
236
0
  // Construct the Attestation Certificate itself
237
0
  UniqueCERTName subjectName(CERT_AsciiToName(kAttestCertSubjectName.get()));
238
0
  if (NS_WARN_IF(!subjectName)) {
239
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
240
0
            ("Failed to set subject name, NSS error #%d", PORT_GetError()));
241
0
    return NS_ERROR_FAILURE;
242
0
  }
243
0
244
0
  UniqueCERTSubjectPublicKeyInfo spki(
245
0
    SECKEY_CreateSubjectPublicKeyInfo(pubKey.get()));
246
0
  if (NS_WARN_IF(!spki)) {
247
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
248
0
            ("Failed to set SPKI, NSS error #%d", PORT_GetError()));
249
0
    return NS_ERROR_FAILURE;
250
0
  }
251
0
252
0
  UniqueCERTCertificateRequest certreq(
253
0
    CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
254
0
  if (NS_WARN_IF(!certreq)) {
255
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
256
0
            ("Failed to gen CSR, NSS error #%d", PORT_GetError()));
257
0
    return NS_ERROR_FAILURE;
258
0
  }
259
0
260
0
  PRTime now = PR_Now();
261
0
  PRTime notBefore = now - kExpirationSlack;
262
0
  PRTime notAfter = now + kExpirationLife;
263
0
264
0
  UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
265
0
  if (NS_WARN_IF(!validity)) {
266
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
267
0
            ("Failed to gen validity, NSS error #%d", PORT_GetError()));
268
0
    return NS_ERROR_FAILURE;
269
0
  }
270
0
271
0
  unsigned long serial;
272
0
  unsigned char* serialBytes =
273
0
    mozilla::BitwiseCast<unsigned char*, unsigned long*>(&serial);
274
0
  SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), serialBytes,
275
0
                                            sizeof(serial));
276
0
  if (NS_WARN_IF(srv != SECSuccess)) {
277
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
278
0
            ("Failed to gen serial, NSS error #%d", PORT_GetError()));
279
0
    return NS_ERROR_FAILURE;
280
0
  }
281
0
  // Ensure that the most significant bit isn't set (which would
282
0
  // indicate a negative number, which isn't valid for serial
283
0
  // numbers).
284
0
  serialBytes[0] &= 0x7f;
285
0
  // Also ensure that the least significant bit on the most
286
0
  // significant byte is set (to prevent a leading zero byte,
287
0
  // which also wouldn't be valid).
288
0
  serialBytes[0] |= 0x01;
289
0
290
0
  aAttestCert = UniqueCERTCertificate(
291
0
    CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
292
0
                           certreq.get()));
293
0
  if (NS_WARN_IF(!aAttestCert)) {
294
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
295
0
            ("Failed to gen certificate, NSS error #%d", PORT_GetError()));
296
0
    return NS_ERROR_FAILURE;
297
0
  }
298
0
299
0
  PLArenaPool* arena = aAttestCert->arena;
300
0
301
0
  srv = SECOID_SetAlgorithmID(arena, &aAttestCert->signature,
302
0
                              SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE,
303
0
                              /* wincx */ nullptr);
304
0
  if (NS_WARN_IF(srv != SECSuccess)) {
305
0
    return NS_ERROR_FAILURE;
306
0
  }
307
0
308
0
  // Set version to X509v3.
309
0
  *(aAttestCert->version.data) = SEC_CERTIFICATE_VERSION_3;
310
0
  aAttestCert->version.len = 1;
311
0
312
0
  SECItem innerDER = { siBuffer, nullptr, 0 };
313
0
  if (NS_WARN_IF(!SEC_ASN1EncodeItem(arena, &innerDER, aAttestCert.get(),
314
0
                                     SEC_ASN1_GET(CERT_CertificateTemplate)))) {
315
0
    return NS_ERROR_FAILURE;
316
0
  }
317
0
318
0
  SECItem* signedCert = PORT_ArenaZNew(arena, SECItem);
319
0
  if (NS_WARN_IF(!signedCert)) {
320
0
    return NS_ERROR_FAILURE;
321
0
  }
322
0
323
0
  srv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
324
0
                        aAttestPrivKey.get(),
325
0
                        SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
326
0
  if (NS_WARN_IF(srv != SECSuccess)) {
327
0
    return NS_ERROR_FAILURE;
328
0
  }
329
0
  aAttestCert->derCert = *signedCert;
330
0
331
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
332
0
          ("U2F Soft Token attestation certificate generated."));
333
0
  return NS_OK;
334
0
}
335
336
// Set up the context for the soft U2F Token. This is called by NSS
337
// initialization.
338
nsresult
339
U2FSoftTokenManager::Init()
340
0
{
341
0
  // If we've already initialized, just return.
342
0
  if (mInitialized) {
343
0
    return NS_OK;
344
0
  }
345
0
346
0
  UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
347
0
  MOZ_ASSERT(slot.get());
348
0
349
0
  // Search for an existing wrapping key, or create one.
350
0
  nsresult rv = GetOrCreateWrappingKey(slot);
351
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
352
0
    return rv;
353
0
  }
354
0
355
0
  mInitialized = true;
356
0
  MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token initialized."));
357
0
  return NS_OK;
358
0
}
359
360
// Convert a Private Key object into an opaque key handle, using AES Key Wrap
361
// with the long-lived aPersistentKey mixed with aAppParam to convert aPrivKey.
362
// The key handle's format is version || saltLen || salt || wrappedPrivateKey
363
static UniqueSECItem
364
KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot,
365
                        const UniquePK11SymKey& aPersistentKey,
366
                        uint8_t* aAppParam, uint32_t aAppParamLen,
367
                        const UniqueSECKEYPrivateKey& aPrivKey)
368
0
{
369
0
  MOZ_ASSERT(aSlot);
370
0
  MOZ_ASSERT(aPersistentKey);
371
0
  MOZ_ASSERT(aAppParam);
372
0
  MOZ_ASSERT(aPrivKey);
373
0
  if (NS_WARN_IF(!aSlot || !aPersistentKey || !aPrivKey || !aAppParam)) {
374
0
    return nullptr;
375
0
  }
376
0
377
0
  // Generate a random salt
378
0
  uint8_t saltParam[kSaltByteLen];
379
0
  SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), saltParam,
380
0
                                            sizeof(saltParam));
381
0
  if (NS_WARN_IF(srv != SECSuccess)) {
382
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
383
0
            ("Failed to generate a salt, NSS error #%d", PORT_GetError()));
384
0
    return nullptr;
385
0
  }
386
0
387
0
  // Prepare the HKDF (https://tools.ietf.org/html/rfc5869)
388
0
  CK_NSS_HKDFParams hkdfParams = { true, saltParam, sizeof(saltParam),
389
0
                                   true, aAppParam, aAppParamLen };
390
0
  SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams,
391
0
                        sizeof(hkdfParams) };
392
0
393
0
  // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam.
394
0
  // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the
395
0
  // derived symmetric key and don't matter because we ignore them anyway.
396
0
  UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256,
397
0
                                       &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP,
398
0
                                       kWrappingKeyByteLen));
399
0
  if (NS_WARN_IF(!wrapKey.get())) {
400
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
401
0
            ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError()));
402
0
    return nullptr;
403
0
  }
404
0
405
0
  UniqueSECItem wrappedKey(::SECITEM_AllocItem(/* default arena */ nullptr,
406
0
                                               /* no buffer */ nullptr,
407
0
                                               kWrappedKeyBufLen));
408
0
  if (NS_WARN_IF(!wrappedKey)) {
409
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
410
0
    return nullptr;
411
0
  }
412
0
413
0
  UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
414
0
                                       /* default IV */ nullptr ));
415
0
416
0
  srv = PK11_WrapPrivKey(aSlot.get(), wrapKey.get(), aPrivKey.get(),
417
0
                         CKM_NSS_AES_KEY_WRAP_PAD, param.get(), wrappedKey.get(),
418
0
                         /* wincx */ nullptr);
419
0
  if (NS_WARN_IF(srv != SECSuccess)) {
420
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
421
0
            ("Failed to wrap U2F key, NSS error #%d", PORT_GetError()));
422
0
    return nullptr;
423
0
  }
424
0
425
0
  // Concatenate the salt and the wrapped Private Key together
426
0
  mozilla::dom::CryptoBuffer keyHandleBuf;
427
0
  if (NS_WARN_IF(!keyHandleBuf.SetCapacity(wrappedKey.get()->len + sizeof(saltParam) + 2,
428
0
                                           mozilla::fallible))) {
429
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
430
0
    return nullptr;
431
0
  }
432
0
433
0
  // It's OK to ignore the return values here because we're writing into
434
0
  // pre-allocated space
435
0
  keyHandleBuf.AppendElement(SoftTokenHandle::Version1, mozilla::fallible);
436
0
  keyHandleBuf.AppendElement(sizeof(saltParam), mozilla::fallible);
437
0
  keyHandleBuf.AppendElements(saltParam, sizeof(saltParam), mozilla::fallible);
438
0
  keyHandleBuf.AppendSECItem(wrappedKey.get());
439
0
440
0
  UniqueSECItem keyHandle(::SECITEM_AllocItem(nullptr, nullptr, 0));
441
0
  if (NS_WARN_IF(!keyHandle)) {
442
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
443
0
    return nullptr;
444
0
  }
445
0
446
0
  if (NS_WARN_IF(!keyHandleBuf.ToSECItem(/* default arena */ nullptr, keyHandle.get()))) {
447
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory"));
448
0
    return nullptr;
449
0
  }
450
0
  return keyHandle;
451
0
}
452
453
// Convert an opaque key handle aKeyHandle back into a Private Key object, using
454
// the long-lived aPersistentKey mixed with aAppParam and the AES Key Wrap
455
// algorithm.
456
static UniqueSECKEYPrivateKey
457
PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot,
458
                        const UniquePK11SymKey& aPersistentKey,
459
                        uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
460
                        uint8_t* aAppParam, uint32_t aAppParamLen)
461
0
{
462
0
  MOZ_ASSERT(aSlot);
463
0
  MOZ_ASSERT(aPersistentKey);
464
0
  MOZ_ASSERT(aKeyHandle);
465
0
  MOZ_ASSERT(aAppParam);
466
0
  MOZ_ASSERT(aAppParamLen == SHA256_LENGTH);
467
0
  if (NS_WARN_IF(!aSlot || !aPersistentKey || !aKeyHandle || !aAppParam ||
468
0
                 aAppParamLen != SHA256_LENGTH)) {
469
0
    return nullptr;
470
0
  }
471
0
472
0
  // As we only support one key format ourselves (right now), fail early if
473
0
  // we aren't that length
474
0
  if (NS_WARN_IF(aKeyHandleLen != kVersion1KeyHandleLen)) {
475
0
    return nullptr;
476
0
  }
477
0
478
0
  if (NS_WARN_IF(aKeyHandle[0] != SoftTokenHandle::Version1)) {
479
0
    // Unrecognized version
480
0
    return nullptr;
481
0
  }
482
0
483
0
  uint8_t saltLen = aKeyHandle[1];
484
0
  uint8_t* saltPtr = aKeyHandle + 2;
485
0
  if (NS_WARN_IF(saltLen != kSaltByteLen)) {
486
0
    return nullptr;
487
0
  }
488
0
489
0
  // Prepare the HKDF (https://tools.ietf.org/html/rfc5869)
490
0
  CK_NSS_HKDFParams hkdfParams = { true, saltPtr, saltLen,
491
0
                                   true, aAppParam, aAppParamLen };
492
0
  SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams,
493
0
                        sizeof(hkdfParams) };
494
0
495
0
  // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam.
496
0
  // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the
497
0
  // derived symmetric key and don't matter because we ignore them anyway.
498
0
  UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256,
499
0
                                       &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP,
500
0
                                       kWrappingKeyByteLen));
501
0
  if (NS_WARN_IF(!wrapKey.get())) {
502
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
503
0
            ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError()));
504
0
    return nullptr;
505
0
  }
506
0
507
0
  uint8_t wrappedLen = aKeyHandleLen - saltLen - 2;
508
0
  uint8_t* wrappedPtr = aKeyHandle + saltLen + 2;
509
0
510
0
  ScopedAutoSECItem wrappedKeyItem(wrappedLen);
511
0
  memcpy(wrappedKeyItem.data, wrappedPtr, wrappedKeyItem.len);
512
0
513
0
  ScopedAutoSECItem pubKey(kPublicKeyLen);
514
0
515
0
  UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
516
0
                                       /* default IV */ nullptr ));
517
0
518
0
  CK_ATTRIBUTE_TYPE usages[] = { CKA_SIGN };
519
0
  int usageCount = 1;
520
0
521
0
  UniqueSECKEYPrivateKey unwrappedKey(
522
0
    PK11_UnwrapPrivKey(aSlot.get(), wrapKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
523
0
                       param.get(), &wrappedKeyItem,
524
0
                       /* no nickname */ nullptr,
525
0
                       /* discard pubkey */ &pubKey,
526
0
                       /* not permanent */ false,
527
0
                       /* non-exportable */ true,
528
0
                       CKK_EC, usages, usageCount,
529
0
                       /* wincx */ nullptr));
530
0
  if (NS_WARN_IF(!unwrappedKey)) {
531
0
    // Not our key.
532
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
533
0
            ("Could not unwrap key handle, NSS Error #%d", PORT_GetError()));
534
0
    return nullptr;
535
0
  }
536
0
537
0
  return unwrappedKey;
538
0
}
539
540
// IsRegistered determines if the provided key handle is usable by this token.
541
nsresult
542
U2FSoftTokenManager::IsRegistered(const nsTArray<uint8_t>& aKeyHandle,
543
                                  const nsTArray<uint8_t>& aAppParam,
544
                                  bool& aResult)
545
0
{
546
0
  if (!mInitialized) {
547
0
    nsresult rv = Init();
548
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
549
0
      return rv;
550
0
    }
551
0
  }
552
0
553
0
  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
554
0
  MOZ_ASSERT(slot.get());
555
0
556
0
  // Decode the key handle
557
0
  UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
558
0
                                                           const_cast<uint8_t*>(aKeyHandle.Elements()),
559
0
                                                           aKeyHandle.Length(),
560
0
                                                           const_cast<uint8_t*>(aAppParam.Elements()),
561
0
                                                           aAppParam.Length());
562
0
  aResult = privKey.get() != nullptr;
563
0
  return NS_OK;
564
0
}
565
566
// A U2F Register operation causes a new key pair to be generated by the token.
567
// The token then returns the public key of the key pair, and a handle to the
568
// private key, which is a fancy way of saying "key wrapped private key", as
569
// well as the generated attestation certificate and a signature using that
570
// certificate's private key.
571
//
572
// The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform
573
// the actual key wrap/unwrap operations.
574
//
575
// The format of the return registration data is as follows:
576
//
577
// Bytes  Value
578
// 1      0x05
579
// 65     public key
580
// 1      key handle length
581
// *      key handle
582
// ASN.1  attestation certificate
583
// *      attestation signature
584
//
585
RefPtr<U2FRegisterPromise>
586
U2FSoftTokenManager::Register(const WebAuthnMakeCredentialInfo& aInfo,
587
                              bool aForceNoneAttestation)
588
0
{
589
0
  if (!mInitialized) {
590
0
    nsresult rv = Init();
591
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
592
0
      return U2FRegisterPromise::CreateAndReject(rv, __func__);
593
0
    }
594
0
  }
595
0
596
0
  if (aInfo.Extra().type() != WebAuthnMaybeMakeCredentialExtraInfo::Tnull_t) {
597
0
    const auto& extra = aInfo.Extra().get_WebAuthnMakeCredentialExtraInfo();
598
0
    const WebAuthnAuthenticatorSelection& sel = extra.AuthenticatorSelection();
599
0
600
0
    // The U2F softtoken neither supports resident keys or
601
0
    // user verification, nor is it a platform authenticator.
602
0
    if (sel.requireResidentKey() ||
603
0
        sel.requireUserVerification() ||
604
0
        sel.requirePlatformAttachment()) {
605
0
      return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
606
0
    }
607
0
  }
608
0
609
0
  CryptoBuffer rpIdHash, clientDataHash;
610
0
  NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
611
0
  nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
612
0
                                       rpIdHash, clientDataHash);
613
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
614
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
615
0
  }
616
0
617
0
  // Optional exclusion list.
618
0
  for (const WebAuthnScopedCredential& cred: aInfo.ExcludeList()) {
619
0
    bool isRegistered = false;
620
0
    nsresult rv = IsRegistered(cred.id(), rpIdHash, isRegistered);
621
0
    if (NS_FAILED(rv)) {
622
0
      return U2FRegisterPromise::CreateAndReject(rv, __func__);
623
0
    }
624
0
    if (isRegistered) {
625
0
      return U2FRegisterPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
626
0
    }
627
0
  }
628
0
629
0
  // We should already have a wrapping key
630
0
  MOZ_ASSERT(mWrappingKey);
631
0
632
0
  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
633
0
  MOZ_ASSERT(slot.get());
634
0
635
0
  // Construct a one-time-use Attestation Certificate
636
0
  UniqueSECKEYPrivateKey attestPrivKey;
637
0
  UniqueCERTCertificate attestCert;
638
0
  rv = GetAttestationCertificate(slot, attestPrivKey, attestCert);
639
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
640
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
641
0
  }
642
0
  MOZ_ASSERT(attestCert);
643
0
  MOZ_ASSERT(attestPrivKey);
644
0
645
0
  // Generate a new keypair; the private will be wrapped into a Key Handle
646
0
  UniqueSECKEYPrivateKey privKey;
647
0
  UniqueSECKEYPublicKey pubKey;
648
0
  rv = GenEcKeypair(slot, privKey, pubKey);
649
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
650
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
651
0
  }
652
0
653
0
  // The key handle will be the result of keywrap(privKey, key=mWrappingKey)
654
0
  UniqueSECItem keyHandleItem =
655
0
    KeyHandleFromPrivateKey(slot, mWrappingKey,
656
0
                            const_cast<uint8_t*>(rpIdHash.Elements()),
657
0
                            rpIdHash.Length(), privKey);
658
0
  if (NS_WARN_IF(!keyHandleItem.get())) {
659
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
660
0
  }
661
0
662
0
  // Sign the challenge using the Attestation privkey (from attestCert)
663
0
  mozilla::dom::CryptoBuffer signedDataBuf;
664
0
  if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + rpIdHash.Length() +
665
0
                                            clientDataHash.Length() +
666
0
                                            keyHandleItem->len + kPublicKeyLen,
667
0
                                            mozilla::fallible))) {
668
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
669
0
  }
670
0
671
0
  // // It's OK to ignore the return values here because we're writing into
672
0
  // // pre-allocated space
673
0
  signedDataBuf.AppendElement(0x00, mozilla::fallible);
674
0
  signedDataBuf.AppendElements(rpIdHash, mozilla::fallible);
675
0
  signedDataBuf.AppendElements(clientDataHash, mozilla::fallible);
676
0
  signedDataBuf.AppendSECItem(keyHandleItem.get());
677
0
  signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
678
0
679
0
  ScopedAutoSECItem signatureItem;
680
0
  SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
681
0
                               signedDataBuf.Length(), attestPrivKey.get(),
682
0
                               SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
683
0
  if (NS_WARN_IF(srv != SECSuccess)) {
684
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
685
0
            ("Signature failure: %d", PORT_GetError()));
686
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
687
0
  }
688
0
689
0
  // Serialize the registration data
690
0
  mozilla::dom::CryptoBuffer registrationBuf;
691
0
  if (NS_WARN_IF(!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len +
692
0
                                              attestCert.get()->derCert.len +
693
0
                                              signatureItem.len, mozilla::fallible))) {
694
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
695
0
  }
696
0
  registrationBuf.AppendElement(0x05, mozilla::fallible);
697
0
  registrationBuf.AppendSECItem(pubKey->u.ec.publicValue);
698
0
  registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible);
699
0
  registrationBuf.AppendSECItem(keyHandleItem.get());
700
0
  registrationBuf.AppendSECItem(attestCert.get()->derCert);
701
0
  registrationBuf.AppendSECItem(signatureItem);
702
0
703
0
  CryptoBuffer keyHandleBuf;
704
0
  if (!keyHandleBuf.AppendSECItem(keyHandleItem.get())) {
705
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
706
0
  }
707
0
708
0
  CryptoBuffer attestCertBuf;
709
0
  if (!attestCertBuf.AppendSECItem(attestCert.get()->derCert)) {
710
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
711
0
  }
712
0
713
0
  CryptoBuffer signatureBuf;
714
0
  if (!signatureBuf.AppendSECItem(signatureItem)) {
715
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
716
0
  }
717
0
718
0
  CryptoBuffer pubKeyBuf;
719
0
  if (!pubKeyBuf.AppendSECItem(pubKey->u.ec.publicValue)) {
720
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
721
0
  }
722
0
723
0
  CryptoBuffer attObj;
724
0
  rv = AssembleAttestationObject(rpIdHash, pubKeyBuf, keyHandleBuf,
725
0
                                 attestCertBuf, signatureBuf,
726
0
                                 aForceNoneAttestation, attObj);
727
0
  if (NS_FAILED(rv)) {
728
0
    return U2FRegisterPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
729
0
  }
730
0
731
0
  WebAuthnMakeCredentialResult result(aInfo.ClientDataJSON(), attObj,
732
0
                                      keyHandleBuf, registrationBuf);
733
0
  return U2FRegisterPromise::CreateAndResolve(std::move(result), __func__);
734
0
}
735
736
bool
737
U2FSoftTokenManager::FindRegisteredKeyHandle(const nsTArray<nsTArray<uint8_t>>& aAppIds,
738
                                             const nsTArray<WebAuthnScopedCredential>& aCredentials,
739
                                             /*out*/ nsTArray<uint8_t>& aKeyHandle,
740
                                             /*out*/ nsTArray<uint8_t>& aAppId)
741
0
{
742
0
  for (const nsTArray<uint8_t>& app_id: aAppIds) {
743
0
    for (const WebAuthnScopedCredential& cred: aCredentials) {
744
0
      bool isRegistered = false;
745
0
      nsresult rv = IsRegistered(cred.id(), app_id, isRegistered);
746
0
      if (NS_SUCCEEDED(rv) && isRegistered) {
747
0
        aKeyHandle.Assign(cred.id());
748
0
        aAppId.Assign(app_id);
749
0
        return true;
750
0
      }
751
0
    }
752
0
  }
753
0
754
0
  return false;
755
0
}
756
757
// A U2F Sign operation creates a signature over the "param" arguments (plus
758
// some other stuff) using the private key indicated in the key handle argument.
759
//
760
// The format of the signed data is as follows:
761
//
762
//  32    Application parameter
763
//  1     User presence (0x01)
764
//  4     Counter
765
//  32    Challenge parameter
766
//
767
// The format of the signature data is as follows:
768
//
769
//  1     User presence
770
//  4     Counter
771
//  *     Signature
772
//
773
RefPtr<U2FSignPromise>
774
U2FSoftTokenManager::Sign(const WebAuthnGetAssertionInfo& aInfo)
775
0
{
776
0
  if (!mInitialized) {
777
0
    nsresult rv = Init();
778
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
779
0
      return U2FSignPromise::CreateAndReject(rv, __func__);
780
0
    }
781
0
  }
782
0
783
0
  CryptoBuffer rpIdHash, clientDataHash;
784
0
  NS_ConvertUTF16toUTF8 rpId(aInfo.RpId());
785
0
  nsresult rv = BuildTransactionHashes(rpId, aInfo.ClientDataJSON(),
786
0
                                       rpIdHash, clientDataHash);
787
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
788
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_UNKNOWN_ERR, __func__);
789
0
  }
790
0
791
0
  nsTArray<nsTArray<uint8_t>> appIds;
792
0
  appIds.AppendElement(rpIdHash);
793
0
794
0
  if (aInfo.Extra().type() != WebAuthnMaybeGetAssertionExtraInfo::Tnull_t) {
795
0
    const auto& extra = aInfo.Extra().get_WebAuthnGetAssertionExtraInfo();
796
0
797
0
    // The U2F softtoken doesn't support user verification.
798
0
    if (extra.RequireUserVerification()) {
799
0
      return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
800
0
    }
801
0
802
0
    // Process extensions.
803
0
    for (const WebAuthnExtension& ext: extra.Extensions()) {
804
0
      if (ext.type() == WebAuthnExtension::TWebAuthnExtensionAppId) {
805
0
        appIds.AppendElement(ext.get_WebAuthnExtensionAppId().AppId());
806
0
      }
807
0
    }
808
0
  }
809
0
810
0
  nsTArray<uint8_t> chosenAppId;
811
0
  nsTArray<uint8_t> keyHandle;
812
0
813
0
  // Fail if we can't find a valid key handle.
814
0
  if (!FindRegisteredKeyHandle(appIds, aInfo.AllowList(), keyHandle, chosenAppId)) {
815
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
816
0
  }
817
0
818
0
  MOZ_ASSERT(mWrappingKey);
819
0
820
0
  UniquePK11SlotInfo slot(PK11_GetInternalSlot());
821
0
  MOZ_ASSERT(slot.get());
822
0
823
0
  if (NS_WARN_IF((clientDataHash.Length() != kParamLen) ||
824
0
                 (chosenAppId.Length() != kParamLen))) {
825
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
826
0
            ("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
827
0
             (uint32_t)clientDataHash.Length(),
828
0
             (uint32_t)chosenAppId.Length(), kParamLen));
829
0
830
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_ILLEGAL_VALUE, __func__);
831
0
  }
832
0
833
0
  // Decode the key handle
834
0
  UniqueSECKEYPrivateKey privKey =
835
0
    PrivateKeyFromKeyHandle(slot, mWrappingKey,
836
0
                            const_cast<uint8_t*>(keyHandle.Elements()),
837
0
                            keyHandle.Length(),
838
0
                            const_cast<uint8_t*>(chosenAppId.Elements()),
839
0
                            chosenAppId.Length());
840
0
  if (NS_WARN_IF(!privKey.get())) {
841
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!"));
842
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
843
0
  }
844
0
845
0
  // Increment the counter and turn it into a SECItem
846
0
  mCounter += 1;
847
0
  ScopedAutoSECItem counterItem(4);
848
0
  counterItem.data[0] = (mCounter >> 24) & 0xFF;
849
0
  counterItem.data[1] = (mCounter >> 16) & 0xFF;
850
0
  counterItem.data[2] = (mCounter >>  8) & 0xFF;
851
0
  counterItem.data[3] = (mCounter >>  0) & 0xFF;
852
0
  uint32_t counter = mCounter;
853
0
  GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
854
0
                                         "dom::U2FSoftTokenManager::Sign",
855
0
                                           [counter] () {
856
0
                                             MOZ_ASSERT(NS_IsMainThread());
857
0
                                             Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter);
858
0
                                           }));
859
0
860
0
  // Compute the signature
861
0
  mozilla::dom::CryptoBuffer signedDataBuf;
862
0
  if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible))) {
863
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
864
0
  }
865
0
866
0
  // It's OK to ignore the return values here because we're writing into
867
0
  // pre-allocated space
868
0
  signedDataBuf.AppendElements(chosenAppId.Elements(),
869
0
                               chosenAppId.Length(),
870
0
                               mozilla::fallible);
871
0
  signedDataBuf.AppendElement(0x01, mozilla::fallible);
872
0
  signedDataBuf.AppendSECItem(counterItem);
873
0
  signedDataBuf.AppendElements(clientDataHash.Elements(),
874
0
                               clientDataHash.Length(),
875
0
                               mozilla::fallible);
876
0
877
0
  if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) {
878
0
    nsAutoCString base64;
879
0
    nsresult rv = Base64URLEncode(signedDataBuf.Length(), signedDataBuf.Elements(),
880
0
                                  Base64URLEncodePaddingPolicy::Omit, base64);
881
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
882
0
      return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
883
0
    }
884
0
885
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
886
0
            ("U2F Token signing bytes (base64): %s", base64.get()));
887
0
  }
888
0
889
0
  ScopedAutoSECItem signatureItem;
890
0
  SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
891
0
                               signedDataBuf.Length(), privKey.get(),
892
0
                               SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
893
0
  if (NS_WARN_IF(srv != SECSuccess)) {
894
0
    MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
895
0
            ("Signature failure: %d", PORT_GetError()));
896
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
897
0
  }
898
0
899
0
  // Assemble the signature data into a buffer for return
900
0
  mozilla::dom::CryptoBuffer signatureDataBuf;
901
0
  if (NS_WARN_IF(!signatureDataBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
902
0
                                           mozilla::fallible))) {
903
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
904
0
  }
905
0
906
0
  // It's OK to ignore the return values here because we're writing into
907
0
  // pre-allocated space
908
0
  signatureDataBuf.AppendElement(0x01, mozilla::fallible);
909
0
  signatureDataBuf.AppendSECItem(counterItem);
910
0
  signatureDataBuf.AppendSECItem(signatureItem);
911
0
912
0
  nsTArray<WebAuthnExtensionResult> extensions;
913
0
914
0
  if (chosenAppId != rpIdHash) {
915
0
    // Indicate to the RP that we used the FIDO appId.
916
0
    extensions.AppendElement(WebAuthnExtensionResultAppId(true));
917
0
  }
918
0
919
0
  CryptoBuffer counterBuf;
920
0
  if (!counterBuf.AppendSECItem(counterItem)) {
921
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
922
0
  }
923
0
924
0
  CryptoBuffer signatureBuf;
925
0
  if (!signatureBuf.AppendSECItem(signatureItem)) {
926
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
927
0
  }
928
0
929
0
  CryptoBuffer chosenAppIdBuf;
930
0
  if (!chosenAppIdBuf.Assign(chosenAppId)) {
931
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
932
0
  }
933
0
934
0
  CryptoBuffer authenticatorData;
935
0
  CryptoBuffer emptyAttestationData;
936
0
  rv = AssembleAuthenticatorData(chosenAppIdBuf, 0x01, counterBuf,
937
0
                                 emptyAttestationData, authenticatorData);
938
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
939
0
    return U2FSignPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
940
0
  }
941
0
942
0
  WebAuthnGetAssertionResult result(aInfo.ClientDataJSON(), keyHandle,
943
0
                                    signatureBuf, authenticatorData,
944
0
                                    extensions, signatureDataBuf);
945
0
  return U2FSignPromise::CreateAndResolve(std::move(result), __func__);
946
0
}
947
948
void
949
U2FSoftTokenManager::Cancel()
950
0
{
951
0
  // This implementation is sync, requests can't be aborted.
952
0
}
953
954
}
955
}