nsNSSU2FToken.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsNSSU2FToken.h"
  6. #include "CryptoBuffer.h"
  7. #include "mozilla/Casting.h"
  8. #include "nsNSSComponent.h"
  9. #include "pk11pub.h"
  10. #include "prerror.h"
  11. #include "secerr.h"
  12. #include "WebCryptoCommon.h"
  13. using namespace mozilla;
  14. using mozilla::dom::CreateECParamsForCurve;
  15. NS_IMPL_ISUPPORTS(nsNSSU2FToken, nsIU2FToken, nsINSSU2FToken)
  16. // Not named "security.webauth.u2f_softtoken_counter" because setting that
  17. // name causes the window.u2f object to disappear until preferences get
  18. // reloaded, as its' pref is a substring!
  19. #define PREF_U2F_NSSTOKEN_COUNTER "security.webauth.softtoken_counter"
  20. const nsCString nsNSSU2FToken::mSecretNickname =
  21. NS_LITERAL_CSTRING("U2F_NSSTOKEN");
  22. const nsString nsNSSU2FToken::mVersion =
  23. NS_LITERAL_STRING("U2F_V2");
  24. NS_NAMED_LITERAL_CSTRING(kAttestCertSubjectName, "CN=Firefox U2F Soft Token");
  25. // This U2F-compatible soft token uses FIDO U2F-compatible ECDSA keypairs
  26. // on the SEC_OID_SECG_EC_SECP256R1 curve. When asked to Register, it will
  27. // generate and return a new keypair KP, where the private component is wrapped
  28. // using AES-KW with the 128-bit mWrappingKey to make an opaque "key handle".
  29. // In other words, Register yields { KP_pub, AES-KW(KP_priv, key=mWrappingKey) }
  30. //
  31. // The value mWrappingKey is long-lived; it is persisted as part of the NSS DB
  32. // for the current profile. The attestation certificates that are produced are
  33. // ephemeral to counteract profiling. They have little use for a soft-token
  34. // at any rate, but are required by the specification.
  35. const uint32_t kParamLen = 32;
  36. const uint32_t kPublicKeyLen = 65;
  37. const uint32_t kWrappedKeyBufLen = 256;
  38. const uint32_t kWrappingKeyByteLen = 128/8;
  39. NS_NAMED_LITERAL_STRING(kEcAlgorithm, WEBCRYPTO_NAMED_CURVE_P256);
  40. const PRTime kOneDay = PRTime(PR_USEC_PER_SEC)
  41. * PRTime(60) // sec
  42. * PRTime(60) // min
  43. * PRTime(24); // hours
  44. const PRTime kExpirationSlack = kOneDay; // Pre-date for clock skew
  45. const PRTime kExpirationLife = kOneDay;
  46. static mozilla::LazyLogModule gNSSTokenLog("webauth_u2f");
  47. nsNSSU2FToken::nsNSSU2FToken()
  48. : mInitialized(false)
  49. {}
  50. nsNSSU2FToken::~nsNSSU2FToken()
  51. {
  52. nsNSSShutDownPreventionLock locker;
  53. if (isAlreadyShutDown()) {
  54. return;
  55. }
  56. destructorSafeDestroyNSSReference();
  57. shutdown(ShutdownCalledFrom::Object);
  58. }
  59. void
  60. nsNSSU2FToken::virtualDestroyNSSReference()
  61. {
  62. destructorSafeDestroyNSSReference();
  63. }
  64. void
  65. nsNSSU2FToken::destructorSafeDestroyNSSReference()
  66. {
  67. mWrappingKey = nullptr;
  68. }
  69. /**
  70. * Gets the first key with the given nickname from the given slot. Any other
  71. * keys found are not returned.
  72. * PK11_GetNextSymKey() should not be called on the returned key.
  73. *
  74. * @param aSlot Slot to search.
  75. * @param aNickname Nickname the key should have.
  76. * @return The first key found. nullptr if no key could be found.
  77. */
  78. static UniquePK11SymKey
  79. GetSymKeyByNickname(const UniquePK11SlotInfo& aSlot,
  80. const nsCString& aNickname,
  81. const nsNSSShutDownPreventionLock&)
  82. {
  83. MOZ_ASSERT(aSlot);
  84. if (!aSlot) {
  85. return nullptr;
  86. }
  87. MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
  88. ("Searching for a symmetric key named %s", aNickname.get()));
  89. UniquePK11SymKey keyListHead(
  90. PK11_ListFixedKeysInSlot(aSlot.get(), const_cast<char*>(aNickname.get()),
  91. /* wincx */ nullptr));
  92. if (!keyListHead) {
  93. MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key not found."));
  94. return nullptr;
  95. }
  96. // Sanity check PK11_ListFixedKeysInSlot() only returns keys with the correct
  97. // nickname.
  98. MOZ_ASSERT(aNickname ==
  99. UniquePORTString(PK11_GetSymKeyNickname(keyListHead.get())).get());
  100. MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Symmetric key found!"));
  101. // Free any remaining keys in the key list.
  102. UniquePK11SymKey freeKey(PK11_GetNextSymKey(keyListHead.get()));
  103. while (freeKey) {
  104. freeKey = UniquePK11SymKey(PK11_GetNextSymKey(freeKey.get()));
  105. }
  106. return keyListHead;
  107. }
  108. static nsresult
  109. GenEcKeypair(const UniquePK11SlotInfo& aSlot,
  110. /*out*/ UniqueSECKEYPrivateKey& aPrivKey,
  111. /*out*/ UniqueSECKEYPublicKey& aPubKey,
  112. const nsNSSShutDownPreventionLock&)
  113. {
  114. MOZ_ASSERT(aSlot);
  115. if (!aSlot) {
  116. return NS_ERROR_INVALID_ARG;
  117. }
  118. UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
  119. if (!arena) {
  120. return NS_ERROR_OUT_OF_MEMORY;
  121. }
  122. // Set the curve parameters; keyParams belongs to the arena memory space
  123. SECItem* keyParams = CreateECParamsForCurve(kEcAlgorithm, arena.get());
  124. if (!keyParams) {
  125. return NS_ERROR_OUT_OF_MEMORY;
  126. }
  127. // Generate a key pair
  128. CK_MECHANISM_TYPE mechanism = CKM_EC_KEY_PAIR_GEN;
  129. SECKEYPublicKey* pubKeyRaw;
  130. aPrivKey = UniqueSECKEYPrivateKey(
  131. PK11_GenerateKeyPair(aSlot.get(), mechanism, keyParams, &pubKeyRaw,
  132. /* ephemeral */ false, false,
  133. /* wincx */ nullptr));
  134. aPubKey = UniqueSECKEYPublicKey(pubKeyRaw);
  135. pubKeyRaw = nullptr;
  136. if (!aPrivKey.get() || !aPubKey.get()) {
  137. return NS_ERROR_FAILURE;
  138. }
  139. // Check that the public key has the correct length
  140. if (aPubKey->u.ec.publicValue.len != kPublicKeyLen) {
  141. return NS_ERROR_FAILURE;
  142. }
  143. return NS_OK;
  144. }
  145. nsresult
  146. nsNSSU2FToken::GetOrCreateWrappingKey(const UniquePK11SlotInfo& aSlot,
  147. const nsNSSShutDownPreventionLock& locker)
  148. {
  149. MOZ_ASSERT(aSlot);
  150. if (!aSlot) {
  151. return NS_ERROR_INVALID_ARG;
  152. }
  153. // Search for an existing wrapping key. If we find it,
  154. // store it for later and mark ourselves initialized.
  155. mWrappingKey = GetSymKeyByNickname(aSlot, mSecretNickname, locker);
  156. if (mWrappingKey) {
  157. MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token Key found."));
  158. mInitialized = true;
  159. return NS_OK;
  160. }
  161. MOZ_LOG(gNSSTokenLog, LogLevel::Info,
  162. ("No keys found. Generating new U2F Soft Token wrapping key."));
  163. // We did not find an existing wrapping key, so we generate one in the
  164. // persistent database (e.g, Token).
  165. mWrappingKey = UniquePK11SymKey(
  166. PK11_TokenKeyGenWithFlags(aSlot.get(), CKM_AES_KEY_GEN,
  167. /* default params */ nullptr,
  168. kWrappingKeyByteLen,
  169. /* empty keyid */ nullptr,
  170. /* flags */ CKF_WRAP | CKF_UNWRAP,
  171. /* attributes */ PK11_ATTR_TOKEN |
  172. PK11_ATTR_PRIVATE,
  173. /* wincx */ nullptr));
  174. if (!mWrappingKey) {
  175. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  176. ("Failed to store wrapping key, NSS error #%d", PORT_GetError()));
  177. return NS_ERROR_FAILURE;
  178. }
  179. SECStatus srv = PK11_SetSymKeyNickname(mWrappingKey.get(),
  180. mSecretNickname.get());
  181. if (srv != SECSuccess) {
  182. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  183. ("Failed to set nickname, NSS error #%d", PORT_GetError()));
  184. return NS_ERROR_FAILURE;
  185. }
  186. MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
  187. ("Key stored, nickname set to %s.", mSecretNickname.get()));
  188. Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, 0);
  189. return NS_OK;
  190. }
  191. static nsresult
  192. GetAttestationCertificate(const UniquePK11SlotInfo& aSlot,
  193. /*out*/ UniqueSECKEYPrivateKey& aAttestPrivKey,
  194. /*out*/ UniqueCERTCertificate& aAttestCert,
  195. const nsNSSShutDownPreventionLock& locker)
  196. {
  197. MOZ_ASSERT(aSlot);
  198. if (!aSlot) {
  199. return NS_ERROR_INVALID_ARG;
  200. }
  201. UniqueSECKEYPublicKey pubKey;
  202. // Construct an ephemeral keypair for this Attestation Certificate
  203. nsresult rv = GenEcKeypair(aSlot, aAttestPrivKey, pubKey, locker);
  204. if (NS_FAILED(rv) || !aAttestPrivKey || !pubKey) {
  205. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  206. ("Failed to gen keypair, NSS error #%d", PORT_GetError()));
  207. return NS_ERROR_FAILURE;
  208. }
  209. // Construct the Attestation Certificate itself
  210. UniqueCERTName subjectName(CERT_AsciiToName(kAttestCertSubjectName.get()));
  211. if (!subjectName) {
  212. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  213. ("Failed to set subject name, NSS error #%d", PORT_GetError()));
  214. return NS_ERROR_FAILURE;
  215. }
  216. UniqueCERTSubjectPublicKeyInfo spki(
  217. SECKEY_CreateSubjectPublicKeyInfo(pubKey.get()));
  218. if (!spki) {
  219. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  220. ("Failed to set SPKI, NSS error #%d", PORT_GetError()));
  221. return NS_ERROR_FAILURE;
  222. }
  223. UniqueCERTCertificateRequest certreq(
  224. CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
  225. if (!certreq) {
  226. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  227. ("Failed to gen CSR, NSS error #%d", PORT_GetError()));
  228. return NS_ERROR_FAILURE;
  229. }
  230. PRTime now = PR_Now();
  231. PRTime notBefore = now - kExpirationSlack;
  232. PRTime notAfter = now + kExpirationLife;
  233. UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
  234. if (!validity) {
  235. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  236. ("Failed to gen validity, NSS error #%d", PORT_GetError()));
  237. return NS_ERROR_FAILURE;
  238. }
  239. unsigned long serial;
  240. unsigned char* serialBytes =
  241. mozilla::BitwiseCast<unsigned char*, unsigned long*>(&serial);
  242. SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), serialBytes,
  243. sizeof(serial));
  244. if (srv != SECSuccess) {
  245. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  246. ("Failed to gen serial, NSS error #%d", PORT_GetError()));
  247. return NS_ERROR_FAILURE;
  248. }
  249. // Ensure that the most significant bit isn't set (which would
  250. // indicate a negative number, which isn't valid for serial
  251. // numbers).
  252. serialBytes[0] &= 0x7f;
  253. // Also ensure that the least significant bit on the most
  254. // significant byte is set (to prevent a leading zero byte,
  255. // which also wouldn't be valid).
  256. serialBytes[0] |= 0x01;
  257. aAttestCert = UniqueCERTCertificate(
  258. CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
  259. certreq.get()));
  260. if (!aAttestCert) {
  261. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  262. ("Failed to gen certificate, NSS error #%d", PORT_GetError()));
  263. return NS_ERROR_FAILURE;
  264. }
  265. PLArenaPool* arena = aAttestCert->arena;
  266. srv = SECOID_SetAlgorithmID(arena, &aAttestCert->signature,
  267. SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE,
  268. /* wincx */ nullptr);
  269. if (srv != SECSuccess) {
  270. return NS_ERROR_FAILURE;
  271. }
  272. // Set version to X509v3.
  273. *(aAttestCert->version.data) = SEC_CERTIFICATE_VERSION_3;
  274. aAttestCert->version.len = 1;
  275. SECItem innerDER = { siBuffer, nullptr, 0 };
  276. if (!SEC_ASN1EncodeItem(arena, &innerDER, aAttestCert.get(),
  277. SEC_ASN1_GET(CERT_CertificateTemplate))) {
  278. return NS_ERROR_FAILURE;
  279. }
  280. SECItem* signedCert = PORT_ArenaZNew(arena, SECItem);
  281. if (!signedCert) {
  282. return NS_ERROR_FAILURE;
  283. }
  284. srv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
  285. aAttestPrivKey.get(),
  286. SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
  287. if (srv != SECSuccess) {
  288. return NS_ERROR_FAILURE;
  289. }
  290. aAttestCert->derCert = *signedCert;
  291. MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
  292. ("U2F Soft Token attestation certificate generated."));
  293. return NS_OK;
  294. }
  295. // Set up the context for the soft U2F Token. This is called by NSS
  296. // initialization.
  297. NS_IMETHODIMP
  298. nsNSSU2FToken::Init()
  299. {
  300. MOZ_ASSERT(NS_IsMainThread());
  301. MOZ_ASSERT(!mInitialized);
  302. if (mInitialized) {
  303. return NS_ERROR_FAILURE;
  304. }
  305. nsNSSShutDownPreventionLock locker;
  306. if (isAlreadyShutDown()) {
  307. return NS_ERROR_NOT_AVAILABLE;
  308. }
  309. UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
  310. MOZ_ASSERT(slot.get());
  311. // Search for an existing wrapping key, or create one.
  312. nsresult rv = GetOrCreateWrappingKey(slot, locker);
  313. if (NS_WARN_IF(NS_FAILED(rv))) {
  314. return rv;
  315. }
  316. mInitialized = true;
  317. MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Soft Token initialized."));
  318. return NS_OK;
  319. }
  320. // Convert a Private Key object into an opaque key handle, using AES Key Wrap
  321. // and aWrappingKey to convert aPrivKey.
  322. static UniqueSECItem
  323. KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot,
  324. const UniquePK11SymKey& aWrappingKey,
  325. const UniqueSECKEYPrivateKey& aPrivKey,
  326. const nsNSSShutDownPreventionLock&)
  327. {
  328. MOZ_ASSERT(aSlot);
  329. MOZ_ASSERT(aWrappingKey);
  330. MOZ_ASSERT(aPrivKey);
  331. if (!aSlot || !aWrappingKey || !aPrivKey) {
  332. return nullptr;
  333. }
  334. UniqueSECItem wrappedKey(SECITEM_AllocItem(/* default arena */ nullptr,
  335. /* no buffer */ nullptr,
  336. kWrappedKeyBufLen));
  337. if (!wrappedKey) {
  338. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  339. ("Failed to allocate memory, NSS error #%d", PORT_GetError()));
  340. return nullptr;
  341. }
  342. UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
  343. /* default IV */ nullptr ));
  344. SECStatus srv = PK11_WrapPrivKey(aSlot.get(), aWrappingKey.get(),
  345. aPrivKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
  346. param.get(), wrappedKey.get(),
  347. /* wincx */ nullptr);
  348. if (srv != SECSuccess) {
  349. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  350. ("Failed to wrap U2F key, NSS error #%d", PORT_GetError()));
  351. return nullptr;
  352. }
  353. return wrappedKey;
  354. }
  355. // Convert an opaque key handle aKeyHandle back into a Private Key object, using
  356. // aWrappingKey and the AES Key Wrap algorithm.
  357. static UniqueSECKEYPrivateKey
  358. PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot,
  359. const UniquePK11SymKey& aWrappingKey,
  360. uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
  361. const nsNSSShutDownPreventionLock&)
  362. {
  363. MOZ_ASSERT(aSlot);
  364. MOZ_ASSERT(aWrappingKey);
  365. MOZ_ASSERT(aKeyHandle);
  366. if (!aSlot || !aWrappingKey || !aKeyHandle) {
  367. return nullptr;
  368. }
  369. ScopedAutoSECItem pubKey(kPublicKeyLen);
  370. ScopedAutoSECItem keyHandleItem(aKeyHandleLen);
  371. memcpy(keyHandleItem.data, aKeyHandle, keyHandleItem.len);
  372. UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD,
  373. /* default IV */ nullptr ));
  374. CK_ATTRIBUTE_TYPE usages[] = { CKA_SIGN };
  375. int usageCount = 1;
  376. UniqueSECKEYPrivateKey unwrappedKey(
  377. PK11_UnwrapPrivKey(aSlot.get(), aWrappingKey.get(), CKM_NSS_AES_KEY_WRAP_PAD,
  378. param.get(), &keyHandleItem,
  379. /* no nickname */ nullptr,
  380. /* discard pubkey */ &pubKey,
  381. /* not permanent */ false,
  382. /* non-exportable */ true,
  383. CKK_EC, usages, usageCount,
  384. /* wincx */ nullptr));
  385. if (!unwrappedKey) {
  386. // Not our key.
  387. MOZ_LOG(gNSSTokenLog, LogLevel::Debug,
  388. ("Could not unwrap key handle, NSS Error #%d", PORT_GetError()));
  389. return nullptr;
  390. }
  391. return unwrappedKey;
  392. }
  393. // Return whether the provided version is supported by this token.
  394. NS_IMETHODIMP
  395. nsNSSU2FToken::IsCompatibleVersion(const nsAString& aVersion, bool* aResult)
  396. {
  397. NS_ENSURE_ARG_POINTER(aResult);
  398. MOZ_ASSERT(mInitialized);
  399. *aResult = (mVersion == aVersion);
  400. return NS_OK;
  401. }
  402. // IsRegistered determines if the provided key handle is usable by this token.
  403. NS_IMETHODIMP
  404. nsNSSU2FToken::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
  405. bool* aResult)
  406. {
  407. NS_ENSURE_ARG_POINTER(aKeyHandle);
  408. NS_ENSURE_ARG_POINTER(aResult);
  409. if (!NS_IsMainThread()) {
  410. NS_ERROR("nsNSSU2FToken::IsRegistered called off the main thread");
  411. return NS_ERROR_NOT_SAME_THREAD;
  412. }
  413. nsNSSShutDownPreventionLock locker;
  414. if (isAlreadyShutDown()) {
  415. return NS_ERROR_FAILURE;
  416. }
  417. MOZ_ASSERT(mInitialized);
  418. if (!mInitialized) {
  419. return NS_ERROR_FAILURE;
  420. }
  421. UniquePK11SlotInfo slot(PK11_GetInternalSlot());
  422. MOZ_ASSERT(slot.get());
  423. // Decode the key handle
  424. UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
  425. aKeyHandle,
  426. aKeyHandleLen,
  427. locker);
  428. *aResult = (privKey.get() != nullptr);
  429. return NS_OK;
  430. }
  431. // A U2F Register operation causes a new key pair to be generated by the token.
  432. // The token then returns the public key of the key pair, and a handle to the
  433. // private key, which is a fancy way of saying "key wrapped private key", as
  434. // well as the generated attestation certificate and a signature using that
  435. // certificate's private key.
  436. //
  437. // The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform
  438. // the actual key wrap/unwrap operations.
  439. //
  440. // The format of the return registration data is as follows:
  441. //
  442. // Bytes Value
  443. // 1 0x05
  444. // 65 public key
  445. // 1 key handle length
  446. // * key handle
  447. // ASN.1 attestation certificate
  448. // * attestation signature
  449. //
  450. NS_IMETHODIMP
  451. nsNSSU2FToken::Register(uint8_t* aApplication,
  452. uint32_t aApplicationLen,
  453. uint8_t* aChallenge,
  454. uint32_t aChallengeLen,
  455. uint8_t** aRegistration,
  456. uint32_t* aRegistrationLen)
  457. {
  458. NS_ENSURE_ARG_POINTER(aApplication);
  459. NS_ENSURE_ARG_POINTER(aChallenge);
  460. NS_ENSURE_ARG_POINTER(aRegistration);
  461. NS_ENSURE_ARG_POINTER(aRegistrationLen);
  462. if (!NS_IsMainThread()) {
  463. NS_ERROR("nsNSSU2FToken::Register called off the main thread");
  464. return NS_ERROR_NOT_SAME_THREAD;
  465. }
  466. nsNSSShutDownPreventionLock locker;
  467. if (isAlreadyShutDown()) {
  468. return NS_ERROR_NOT_AVAILABLE;
  469. }
  470. MOZ_ASSERT(mInitialized);
  471. if (!mInitialized) {
  472. return NS_ERROR_NOT_INITIALIZED;
  473. }
  474. // We should already have a wrapping key
  475. MOZ_ASSERT(mWrappingKey);
  476. UniquePK11SlotInfo slot(PK11_GetInternalSlot());
  477. MOZ_ASSERT(slot.get());
  478. // Construct a one-time-use Attestation Certificate
  479. UniqueSECKEYPrivateKey attestPrivKey;
  480. UniqueCERTCertificate attestCert;
  481. nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert,
  482. locker);
  483. if (NS_WARN_IF(NS_FAILED(rv))) {
  484. return NS_ERROR_FAILURE;
  485. }
  486. MOZ_ASSERT(attestCert);
  487. MOZ_ASSERT(attestPrivKey);
  488. // Generate a new keypair; the private will be wrapped into a Key Handle
  489. UniqueSECKEYPrivateKey privKey;
  490. UniqueSECKEYPublicKey pubKey;
  491. rv = GenEcKeypair(slot, privKey, pubKey, locker);
  492. if (NS_WARN_IF(NS_FAILED(rv))) {
  493. return NS_ERROR_FAILURE;
  494. }
  495. // The key handle will be the result of keywrap(privKey, key=mWrappingKey)
  496. UniqueSECItem keyHandleItem = KeyHandleFromPrivateKey(slot, mWrappingKey,
  497. privKey, locker);
  498. if (!keyHandleItem.get()) {
  499. return NS_ERROR_FAILURE;
  500. }
  501. // Sign the challenge using the Attestation privkey (from attestCert)
  502. mozilla::dom::CryptoBuffer signedDataBuf;
  503. if (!signedDataBuf.SetCapacity(1 + aApplicationLen + aChallengeLen +
  504. keyHandleItem->len + kPublicKeyLen,
  505. mozilla::fallible)) {
  506. return NS_ERROR_OUT_OF_MEMORY;
  507. }
  508. // It's OK to ignore the return values here because we're writing into
  509. // pre-allocated space
  510. signedDataBuf.AppendElement(0x00, mozilla::fallible);
  511. signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible);
  512. signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible);
  513. signedDataBuf.AppendSECItem(keyHandleItem.get());
  514. signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue);
  515. ScopedAutoSECItem signatureItem;
  516. SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
  517. signedDataBuf.Length(), attestPrivKey.get(),
  518. SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
  519. if (srv != SECSuccess) {
  520. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  521. ("Signature failure: %d", PORT_GetError()));
  522. return NS_ERROR_FAILURE;
  523. }
  524. // Serialize the registration data
  525. mozilla::dom::CryptoBuffer registrationBuf;
  526. if (!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len +
  527. attestCert.get()->derCert.len +
  528. signatureItem.len, mozilla::fallible)) {
  529. return NS_ERROR_OUT_OF_MEMORY;
  530. }
  531. registrationBuf.AppendElement(0x05, mozilla::fallible);
  532. registrationBuf.AppendSECItem(pubKey->u.ec.publicValue);
  533. registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible);
  534. registrationBuf.AppendSECItem(keyHandleItem.get());
  535. registrationBuf.AppendSECItem(attestCert.get()->derCert);
  536. registrationBuf.AppendSECItem(signatureItem);
  537. if (!registrationBuf.ToNewUnsignedBuffer(aRegistration, aRegistrationLen)) {
  538. return NS_ERROR_FAILURE;
  539. }
  540. return NS_OK;
  541. }
  542. // A U2F Sign operation creates a signature over the "param" arguments (plus
  543. // some other stuff) using the private key indicated in the key handle argument.
  544. //
  545. // The format of the signed data is as follows:
  546. //
  547. // 32 Application parameter
  548. // 1 User presence (0x01)
  549. // 4 Counter
  550. // 32 Challenge parameter
  551. //
  552. // The format of the signature data is as follows:
  553. //
  554. // 1 User presence
  555. // 4 Counter
  556. // * Signature
  557. //
  558. NS_IMETHODIMP
  559. nsNSSU2FToken::Sign(uint8_t* aApplication, uint32_t aApplicationLen,
  560. uint8_t* aChallenge, uint32_t aChallengeLen,
  561. uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
  562. uint8_t** aSignature, uint32_t* aSignatureLen)
  563. {
  564. NS_ENSURE_ARG_POINTER(aApplication);
  565. NS_ENSURE_ARG_POINTER(aChallenge);
  566. NS_ENSURE_ARG_POINTER(aKeyHandle);
  567. NS_ENSURE_ARG_POINTER(aKeyHandleLen);
  568. NS_ENSURE_ARG_POINTER(aSignature);
  569. NS_ENSURE_ARG_POINTER(aSignatureLen);
  570. if (!NS_IsMainThread()) {
  571. NS_ERROR("nsNSSU2FToken::Sign called off the main thread");
  572. return NS_ERROR_NOT_SAME_THREAD;
  573. }
  574. nsNSSShutDownPreventionLock locker;
  575. if (isAlreadyShutDown()) {
  576. return NS_ERROR_NOT_AVAILABLE;
  577. }
  578. MOZ_ASSERT(mInitialized);
  579. if (!mInitialized) {
  580. return NS_ERROR_NOT_INITIALIZED;
  581. }
  582. MOZ_ASSERT(mWrappingKey);
  583. UniquePK11SlotInfo slot(PK11_GetInternalSlot());
  584. MOZ_ASSERT(slot.get());
  585. if ((aChallengeLen != kParamLen) || (aApplicationLen != kParamLen)) {
  586. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  587. ("Parameter lengths are wrong! challenge=%d app=%d expected=%d",
  588. aChallengeLen, aApplicationLen, kParamLen));
  589. return NS_ERROR_ILLEGAL_VALUE;
  590. }
  591. // Decode the key handle
  592. UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey,
  593. aKeyHandle,
  594. aKeyHandleLen,
  595. locker);
  596. if (!privKey.get()) {
  597. MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!"));
  598. return NS_ERROR_FAILURE;
  599. }
  600. // Increment the counter and turn it into a SECItem
  601. uint32_t counter = Preferences::GetUint(PREF_U2F_NSSTOKEN_COUNTER) + 1;
  602. Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter);
  603. ScopedAutoSECItem counterItem(4);
  604. counterItem.data[0] = (counter >> 24) & 0xFF;
  605. counterItem.data[1] = (counter >> 16) & 0xFF;
  606. counterItem.data[2] = (counter >> 8) & 0xFF;
  607. counterItem.data[3] = (counter >> 0) & 0xFF;
  608. // Compute the signature
  609. mozilla::dom::CryptoBuffer signedDataBuf;
  610. if (!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible)) {
  611. return NS_ERROR_OUT_OF_MEMORY;
  612. }
  613. // It's OK to ignore the return values here because we're writing into
  614. // pre-allocated space
  615. signedDataBuf.AppendElements(aApplication, aApplicationLen, mozilla::fallible);
  616. signedDataBuf.AppendElement(0x01, mozilla::fallible);
  617. signedDataBuf.AppendSECItem(counterItem);
  618. signedDataBuf.AppendElements(aChallenge, aChallengeLen, mozilla::fallible);
  619. ScopedAutoSECItem signatureItem;
  620. SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(),
  621. signedDataBuf.Length(), privKey.get(),
  622. SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE);
  623. if (srv != SECSuccess) {
  624. MOZ_LOG(gNSSTokenLog, LogLevel::Warning,
  625. ("Signature failure: %d", PORT_GetError()));
  626. return NS_ERROR_FAILURE;
  627. }
  628. // Assemble the signature data into a buffer for return
  629. mozilla::dom::CryptoBuffer signatureBuf;
  630. if (!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len,
  631. mozilla::fallible)) {
  632. return NS_ERROR_OUT_OF_MEMORY;
  633. }
  634. // It's OK to ignore the return values here because we're writing into
  635. // pre-allocated space
  636. signatureBuf.AppendElement(0x01, mozilla::fallible);
  637. signatureBuf.AppendSECItem(counterItem);
  638. signatureBuf.AppendSECItem(signatureItem);
  639. if (!signatureBuf.ToNewUnsignedBuffer(aSignature, aSignatureLen)) {
  640. return NS_ERROR_FAILURE;
  641. }
  642. return NS_OK;
  643. }