bug 1498909 - dynamically load libsecret at runtime if available r=franziskus,jcj
authorDana Keeler <dkeeler@mozilla.com>
Thu, 03 Jan 2019 00:39:45 +0000
changeset 509470 15af035c0cf4da68e737aee400ac96a182d5c1a1
parent 509469 7212af915b688bad63979b0e440b5d376b4b4ee6
child 509471 6a89226d8c15f0b8b9dbcb23f76f371fdefc289b
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfranziskus, jcj
bugs1498909
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1498909 - dynamically load libsecret at runtime if available r=franziskus,jcj Enough linux-based systems don't have libsecret that we can't make it a requirement on linux. For those that do, however, we can dynamically load the library at runtime. For those that don't, we can fall back to NSS. Differential Revision: https://phabricator.services.mozilla.com/D9969
config/system-headers.mozbuild
security/manager/ssl/LibSecret.cpp
security/manager/ssl/LibSecret.h
security/manager/ssl/OSKeyStore.cpp
security/manager/ssl/moz.build
security/manager/ssl/tests/unit/test_oskeystore.js
toolkit/library/moz.build
--- a/config/system-headers.mozbuild
+++ b/config/system-headers.mozbuild
@@ -1345,25 +1345,8 @@ if CONFIG['ENABLE_BIGINT']:
 
 if CONFIG['MOZ_WAYLAND']:
     system_headers += [
         'xkbcommon/xkbcommon.h',
         'wayland-client.h',
         'wayland-egl.h',
         'wayland-util.h',
     ]
-
-if CONFIG['MOZ_LIB_SECRET']:
-    system_headers += [
-        'libsecret/secret.h',
-        'libsecret/secret-attributes.h',
-        'libsecret/secret-collection.h',
-        'libsecret/secret-enum-types.h',
-        'libsecret/secret-item.h',
-        'libsecret/secret-password.h',
-        'libsecret/secret-paths.h',
-        'libsecret/secret-prompt.h',
-        'libsecret/secret-schema.h',
-        'libsecret/secret-schemas.h',
-        'libsecret/secret-types.h',
-        'libsecret/secret-value.h',
-        'libsecret/secret-service.h',
-    ]
--- a/security/manager/ssl/LibSecret.cpp
+++ b/security/manager/ssl/LibSecret.cpp
@@ -1,39 +1,217 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LibSecret.h"
 
+#include <gio/gio.h>
 #include <gmodule.h>
+#include <memory>
 
 #include "mozilla/Base64.h"
+#include "prlink.h"
 
 // This is the implementation of LibSecret, an instantiation of OSKeyStore for
 // Linux.
 
 using namespace mozilla;
 
 LazyLogModule gLibSecretLog("libsecret");
 
+static PRLibrary* libsecret = nullptr;
+
+typedef struct _SecretService SecretService;
+typedef struct _SecretCollection SecretCollection;
+
+typedef enum {
+  SECRET_SCHEMA_NONE = 0,
+  SECRET_SCHEMA_DONT_MATCH_NAME = 1 << 1
+} SecretSchemaFlags;
+
+typedef enum {
+  SECRET_SCHEMA_ATTRIBUTE_STRING = 0,
+  SECRET_SCHEMA_ATTRIBUTE_INTEGER = 1,
+  SECRET_SCHEMA_ATTRIBUTE_BOOLEAN = 2,
+} SecretSchemaAttributeType;
+
+typedef struct {
+  const gchar* name;
+  SecretSchemaAttributeType type;
+} SecretSchemaAttribute;
+
+typedef struct {
+  const gchar* name;
+  SecretSchemaFlags flags;
+  SecretSchemaAttribute attributes[32];
+
+  /* <private> */
+  gint reserved;
+  gpointer reserved1;
+  gpointer reserved2;
+  gpointer reserved3;
+  gpointer reserved4;
+  gpointer reserved5;
+  gpointer reserved6;
+  gpointer reserved7;
+} SecretSchema;
+
+typedef enum {
+  SECRET_COLLECTION_NONE = 0 << 0,
+  SECRET_COLLECTION_LOAD_ITEMS = 1 << 1,
+} SecretCollectionFlags;
+
+typedef enum {
+  SECRET_SERVICE_NONE = 0,
+  SECRET_SERVICE_OPEN_SESSION = 1 << 1,
+  SECRET_SERVICE_LOAD_COLLECTIONS = 1 << 2,
+} SecretServiceFlags;
+
+typedef enum {
+  SECRET_ERROR_PROTOCOL = 1,
+  SECRET_ERROR_IS_LOCKED = 2,
+  SECRET_ERROR_NO_SUCH_OBJECT = 3,
+  SECRET_ERROR_ALREADY_EXISTS = 4,
+} SecretError;
+
+#define SECRET_COLLECTION_DEFAULT "default"
+
+typedef SecretCollection* (*secret_collection_for_alias_sync_fn)(
+    SecretService*, const gchar*, SecretCollectionFlags, GCancellable*,
+    GError**);
+typedef SecretService* (*secret_service_get_sync_fn)(SecretServiceFlags,
+                                                     GCancellable*, GError**);
+typedef gint (*secret_service_lock_sync_fn)(SecretService*, GList*,
+                                            GCancellable*, GList**, GError**);
+typedef gboolean (*secret_password_clear_sync_fn)(const SecretSchema*,
+                                                  GCancellable*, GError**, ...);
+typedef gchar* (*secret_password_lookup_sync_fn)(const SecretSchema*,
+                                                 GCancellable*, GError**, ...);
+typedef gboolean (*secret_password_store_sync_fn)(const SecretSchema*,
+                                                  const gchar*, const gchar*,
+                                                  const gchar*, GCancellable*,
+                                                  GError**, ...);
+typedef void (*secret_password_free_fn)(const gchar*);
+typedef GQuark (*secret_error_get_quark_fn)();
+
+static secret_collection_for_alias_sync_fn secret_collection_for_alias_sync =
+    nullptr;
+static secret_service_get_sync_fn secret_service_get_sync = nullptr;
+static secret_service_lock_sync_fn secret_service_lock_sync = nullptr;
+static secret_password_clear_sync_fn secret_password_clear_sync = nullptr;
+static secret_password_lookup_sync_fn secret_password_lookup_sync = nullptr;
+static secret_password_store_sync_fn secret_password_store_sync = nullptr;
+static secret_password_free_fn secret_password_free = nullptr;
+static secret_error_get_quark_fn secret_error_get_quark = nullptr;
+
+nsresult MaybeLoadLibSecret() {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!NS_IsMainThread()) {
+    return NS_ERROR_NOT_SAME_THREAD;
+  }
+
+  if (!libsecret) {
+    libsecret = PR_LoadLibrary("libsecret-1.so.0");
+    if (!libsecret) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+
+#define FIND_FUNCTION_SYMBOL(function)                                   \
+  function = (function##_fn)PR_FindFunctionSymbol(libsecret, #function); \
+  if (!(function)) {                                                     \
+    PR_UnloadLibrary(libsecret);                                         \
+    libsecret = nullptr;                                                 \
+    return NS_ERROR_NOT_AVAILABLE;                                       \
+  }
+    FIND_FUNCTION_SYMBOL(secret_collection_for_alias_sync);
+    FIND_FUNCTION_SYMBOL(secret_service_get_sync);
+    FIND_FUNCTION_SYMBOL(secret_service_lock_sync);
+    FIND_FUNCTION_SYMBOL(secret_password_clear_sync);
+    FIND_FUNCTION_SYMBOL(secret_password_lookup_sync);
+    FIND_FUNCTION_SYMBOL(secret_password_store_sync);
+    FIND_FUNCTION_SYMBOL(secret_password_free);
+    FIND_FUNCTION_SYMBOL(secret_error_get_quark);
+#undef FIND_FUNCTION_SYMBOL
+  }
+
+  return NS_OK;
+}
+
+struct ScopedDelete {
+  void operator()(SecretService* ss) {
+    if (ss) g_object_unref(ss);
+  }
+  void operator()(SecretCollection* sc) {
+    if (sc) g_object_unref(sc);
+  }
+  void operator()(GError* error) {
+    if (error) g_error_free(error);
+  }
+  void operator()(GList* list) {
+    if (list) g_list_free(list);
+  }
+  void operator()(char* val) {
+    if (val) secret_password_free(val);
+  }
+};
+
+template <class T>
+struct ScopedMaybeDelete {
+  void operator()(T* ptr) {
+    if (ptr) {
+      ScopedDelete del;
+      del(ptr);
+    }
+  }
+};
+
+typedef std::unique_ptr<GError, ScopedMaybeDelete<GError>> ScopedGError;
+typedef std::unique_ptr<GList, ScopedMaybeDelete<GList>> ScopedGList;
+typedef std::unique_ptr<char, ScopedMaybeDelete<char>> ScopedPassword;
+typedef std::unique_ptr<SecretCollection, ScopedMaybeDelete<SecretCollection>>
+    ScopedSecretCollection;
+typedef std::unique_ptr<SecretService, ScopedMaybeDelete<SecretService>>
+    ScopedSecretService;
+
 LibSecret::LibSecret() {}
 
-LibSecret::~LibSecret() {}
+LibSecret::~LibSecret() {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!NS_IsMainThread()) {
+    return;
+  }
+  if (libsecret) {
+    secret_collection_for_alias_sync = nullptr;
+    secret_service_get_sync = nullptr;
+    secret_service_lock_sync = nullptr;
+    secret_password_clear_sync = nullptr;
+    secret_password_lookup_sync = nullptr;
+    secret_password_store_sync = nullptr;
+    secret_password_free = nullptr;
+    secret_error_get_quark = nullptr;
+    PR_UnloadLibrary(libsecret);
+    libsecret = nullptr;
+  }
+}
 
 static const SecretSchema kSchema = {
     "mozilla.firefox",
     SECRET_SCHEMA_NONE,
     {{"string", SECRET_SCHEMA_ATTRIBUTE_STRING}, /* the label */
      {"NULL", SECRET_SCHEMA_ATTRIBUTE_STRING}}};
 
 nsresult GetScopedServices(ScopedSecretService& aSs,
                            ScopedSecretCollection& aSc) {
+  MOZ_ASSERT(secret_service_get_sync && secret_collection_for_alias_sync);
+  if (!secret_service_get_sync || !secret_collection_for_alias_sync) {
+    return NS_ERROR_FAILURE;
+  }
   GError* raw_error = nullptr;
   aSs = ScopedSecretService(secret_service_get_sync(
       static_cast<SecretServiceFlags>(
           SECRET_SERVICE_OPEN_SESSION),  // SecretServiceFlags
       nullptr,                           // GCancellable
       &raw_error));
   ScopedGError error(raw_error);
   if (error || !aSs) {
@@ -50,16 +228,20 @@ nsresult GetScopedServices(ScopedSecretS
     MOZ_LOG(gLibSecretLog, LogLevel::Debug,
             ("Couldn't get a secret collection"));
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult LibSecret::Lock() {
+  MOZ_ASSERT(secret_service_lock_sync);
+  if (!secret_service_lock_sync) {
+    return NS_ERROR_FAILURE;
+  }
   ScopedSecretService ss;
   ScopedSecretCollection sc;
   if (NS_FAILED(GetScopedServices(ss, sc))) {
     return NS_ERROR_FAILURE;
   }
 
   GError* raw_error = nullptr;
   GList* collections = nullptr;
@@ -85,56 +267,85 @@ nsresult LibSecret::Unlock() {
   if (NS_FAILED(GetScopedServices(ss, sc))) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult LibSecret::StoreSecret(const nsACString& aSecret,
                                 const nsACString& aLabel) {
+  MOZ_ASSERT(secret_password_store_sync);
+  if (!secret_password_store_sync) {
+    return NS_ERROR_FAILURE;
+  }
+  // libsecret expects a null-terminated string, so to be safe we store the
+  // secret (which could be arbitrary bytes) base64-encoded.
+  nsAutoCString base64;
+  nsresult rv = Base64Encode(aSecret, base64);
+  if (NS_FAILED(rv)) {
+    MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error base64-encoding secret"));
+    return rv;
+  }
   GError* raw_error = nullptr;
   bool stored = secret_password_store_sync(
       &kSchema, SECRET_COLLECTION_DEFAULT, PromiseFlatCString(aLabel).get(),
-      PromiseFlatCString(aSecret).get(),
+      PromiseFlatCString(base64).get(),
       nullptr,  // GCancellable
       &raw_error, "string", PromiseFlatCString(aLabel).get(), nullptr);
   ScopedGError error(raw_error);
   if (raw_error) {
     MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error storing secret"));
     return NS_ERROR_FAILURE;
   }
 
   return stored ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult LibSecret::DeleteSecret(const nsACString& aLabel) {
+  MOZ_ASSERT(secret_password_clear_sync && secret_error_get_quark);
+  if (!secret_password_clear_sync || !secret_error_get_quark) {
+    return NS_ERROR_FAILURE;
+  }
   GError* raw_error = nullptr;
-  bool r = secret_password_clear_sync(
+  Unused << secret_password_clear_sync(
       &kSchema,
       nullptr,  // GCancellable
       &raw_error, "string", PromiseFlatCString(aLabel).get(), nullptr);
   ScopedGError error(raw_error);
-  if (raw_error) {
+  if (raw_error && !(raw_error->domain == secret_error_get_quark() &&
+                     raw_error->code == SECRET_ERROR_NO_SUCH_OBJECT)) {
     MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error deleting secret"));
     return NS_ERROR_FAILURE;
   }
 
-  return r ? NS_OK : NS_ERROR_FAILURE;
+  return NS_OK;
 }
 
 nsresult LibSecret::RetrieveSecret(const nsACString& aLabel,
                                    /* out */ nsACString& aSecret) {
+  MOZ_ASSERT(secret_password_lookup_sync && secret_password_free);
+  if (!secret_password_lookup_sync || !secret_password_free) {
+    return NS_ERROR_FAILURE;
+  }
   GError* raw_error = nullptr;
   aSecret.Truncate();
   ScopedPassword s(secret_password_lookup_sync(
       &kSchema,
       nullptr,  // GCancellable
       &raw_error, "string", PromiseFlatCString(aLabel).get(), nullptr));
   ScopedGError error(raw_error);
   if (raw_error || !s) {
     MOZ_LOG(gLibSecretLog, LogLevel::Debug,
             ("Error retrieving secret or didn't find it"));
     return NS_ERROR_FAILURE;
   }
-  aSecret.Assign(s.get(), strlen(s.get()));
+  // libsecret expects a null-terminated string, so to be safe we store the
+  // secret (which could be arbitrary bytes) base64-encoded, which means we have
+  // to base64-decode it here.
+  nsAutoCString base64Encoded(s.get());
+  nsresult rv = Base64Decode(base64Encoded, aSecret);
+  if (NS_FAILED(rv)) {
+    MOZ_LOG(gLibSecretLog, LogLevel::Debug, ("Error base64-decoding secret"));
+    return rv;
+  }
 
   return NS_OK;
 }
--- a/security/manager/ssl/LibSecret.h
+++ b/security/manager/ssl/LibSecret.h
@@ -1,74 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef MOZ_LIB_SECRET
-#error LibSecret OSKeyStore included when MOZ_LIB_SECRET is not defined!
-#endif
-
 #ifndef LibSecret_h
 #define LibSecret_h
 
 #include "OSKeyStore.h"
 
-#include <libsecret/secret.h>
-#include <memory>
-#include <vector>
-
 #include "nsString.h"
 
-struct ScopedDelete {
-  void operator()(SecretService* ss) {
-    if (ss) g_object_unref(ss);
-  }
-  void operator()(SecretCollection* sc) {
-    if (sc) g_object_unref(sc);
-  }
-  void operator()(GError* error) {
-    if (error) g_error_free(error);
-  }
-  void operator()(GList* list) {
-    if (list) g_list_free(list);
-  }
-  void operator()(SecretValue* val) {
-    if (val) secret_value_unref(val);
-  }
-  void operator()(SecretItem* val) {
-    if (val) g_object_unref(val);
-  }
-  void operator()(char* val) {
-    if (val) secret_password_free(val);
-  }
-};
-
-template <class T>
-struct ScopedMaybeDelete {
-  void operator()(T* ptr) {
-    if (ptr) {
-      ScopedDelete del;
-      del(ptr);
-    }
-  }
-};
-
-#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x>> Scoped##x
-
-SCOPED(SecretService);
-SCOPED(SecretCollection);
-SCOPED(GError);
-SCOPED(GList);
-SCOPED(SecretValue);
-SCOPED(SecretItem);
-typedef std::unique_ptr<char, ScopedMaybeDelete<char>> ScopedPassword;
-
-#undef SCOPED
+nsresult MaybeLoadLibSecret();
 
 class LibSecret final : public AbstractOSKeyStore {
  public:
   LibSecret();
 
   virtual nsresult RetrieveSecret(const nsACString& label,
                                   /* out */ nsACString& secret) override;
   virtual nsresult StoreSecret(const nsACString& secret,
--- a/security/manager/ssl/OSKeyStore.cpp
+++ b/security/manager/ssl/OSKeyStore.cpp
@@ -7,46 +7,50 @@
 #include "OSKeyStore.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/dom/Promise.h"
 #include "nsIRandomGenerator.h"
 #include "nsXPCOM.h"
 #include "pk11pub.h"
 
-#ifdef MOZ_LIB_SECRET
-#include "LibSecret.h"
-#elif defined(XP_MACOSX)
+#if defined(XP_MACOSX)
 #include "KeychainSecret.h"
 #elif defined(XP_WIN)
 #include "CredentialManagerSecret.h"
+#elif defined(MOZ_WIDGET_GTK)
+#include "LibSecret.h"
+#include "NSSKeyStore.h"
 #else
 #include "NSSKeyStore.h"
 #endif
 
 NS_IMPL_ISUPPORTS(OSKeyStore, nsIOSKeyStore, nsIObserver)
 
 using namespace mozilla;
 using dom::Promise;
 
-mozilla::LazyLogModule gOSKeyStoreLog("oskeystore");
-
 OSKeyStore::OSKeyStore()
     : mKs(nullptr), mKsThread(nullptr), mKsIsNSSKeyStore(false) {
   MOZ_ASSERT(NS_IsMainThread());
   if (NS_WARN_IF(!NS_IsMainThread())) {
     return;
   }
 
-#ifdef MOZ_LIB_SECRET
-  mKs.reset(new LibSecret());
-#elif defined(XP_MACOSX)
+#if defined(XP_MACOSX)
   mKs.reset(new KeychainSecret());
 #elif defined(XP_WIN)
   mKs.reset(new CredentialManagerSecret());
+#elif defined(MOZ_WIDGET_GTK)
+  if (NS_SUCCEEDED(MaybeLoadLibSecret())) {
+    mKs.reset(new LibSecret());
+  } else {
+    mKs.reset(new NSSKeyStore());
+    mKsIsNSSKeyStore = true;
+  }
 #else
   mKs.reset(new NSSKeyStore());
   mKsIsNSSKeyStore = true;
 #endif
 
   nsCOMPtr<nsIThread> thread;
   nsresult rv = NS_NewNamedThread("OSKeyStore", getter_AddRefs(thread));
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -136,22 +136,22 @@ UNIFIED_SOURCES += [
     'PublicKeyPinningService.cpp',
     'RootCertificateTelemetryUtils.cpp',
     'SecretDecoderRing.cpp',
     'SharedSSLState.cpp',
     'SSLServerCertVerification.cpp',
     'TransportSecurityInfo.cpp',
 ]
 
-if CONFIG['MOZ_LIB_SECRET']:
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     UNIFIED_SOURCES += [
         'LibSecret.cpp',
     ]
-    CFLAGS += CONFIG['MOZ_LIB_SECRET_CFLAGS']
-    CXXFLAGS += CONFIG['MOZ_LIB_SECRET_CFLAGS']
+    CFLAGS += CONFIG['GLIB_CFLAGS']
+    CXXFLAGS += CONFIG['GLIB_CFLAGS']
 
 if CONFIG['OS_ARCH'] == 'Darwin':
     UNIFIED_SOURCES += [
         'KeychainSecret.cpp',
         'OSReauthenticatorDarwin.mm',
     ]
     OS_LIBS += [
         '-framework LocalAuthentication',
--- a/security/manager/ssl/tests/unit/test_oskeystore.js
+++ b/security/manager/ssl/tests/unit/test_oskeystore.js
@@ -156,8 +156,53 @@ add_task(async function() {
     let ciphertext = await promise;
     ok(ciphertext, "We should have a ciphertext now.");
   } catch (e) {
     ok(false, "Error encrypting " + e);
   }
 
   await delete_all_secrets();
 });
+
+// Test that using a recovery phrase works.
+add_task(async function() {
+  await delete_all_secrets();
+
+  let keystore = Cc["@mozilla.org/security/oskeystore;1"]
+                   .getService(Ci.nsIOSKeyStore);
+
+  let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+  ok(recoveryPhrase, "A recovery phrase should've been created.");
+
+  let text = new Uint8Array([0x01, 0x00, 0x01]);
+  let ciphertext = await keystore.asyncEncryptBytes(LABELS[0], text.length, text);
+  ok(ciphertext, "We should have a ciphertext now.");
+
+  await keystore.asyncDeleteSecret(LABELS[0]);
+  // Decrypting should fail after deleting the secret.
+  await keystore.asyncDecryptBytes(LABELS[0], ciphertext)
+    .then(() => ok(false, "decrypting didn't throw as expected after deleting the secret"))
+    .catch(() => ok(true, "decrypting threw as expected after deleting the secret"));
+
+  await keystore.asyncRecoverSecret(LABELS[0], recoveryPhrase);
+  let plaintext = await keystore.asyncDecryptBytes(LABELS[0], ciphertext);
+  ok(plaintext.toString() == text.toString(), "Decrypted plaintext should be the same as text.");
+
+  await delete_all_secrets();
+});
+
+// Test that trying to use a non-base64 recovery phrase fails.
+add_task(async function() {
+  await delete_all_secrets();
+
+  let keystore = Cc["@mozilla.org/security/oskeystore;1"]
+                   .getService(Ci.nsIOSKeyStore);
+  await keystore.asyncRecoverSecret(LABELS[0], "@##$^&*()#$^&*(@#%&*_")
+    .then(() => ok(false, "base64-decoding non-base64 should have failed but didn't"))
+    .catch(() => ok(true, "base64-decoding non-base64 failed as expected"));
+
+  ok(!await keystore.asyncSecretAvailable(LABELS[0]),
+     "we didn't recover a secret, so the secret shouldn't be available");
+  let recoveryPhrase = await keystore.asyncGenerateSecret(LABELS[0]);
+  ok(recoveryPhrase && recoveryPhrase.length > 0,
+     "we should be able to re-use that label to generate a new secret");
+  await delete_all_secrets();
+});
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -210,19 +210,16 @@ if CONFIG['MOZ_ANDROID_GOOGLE_VR']:
     OS_LIBS += [
         '-L%s' % CONFIG['MOZ_ANDROID_GOOGLE_VR_LIBS'],
         '-lgvr',
     ]
 
 OS_LIBS += CONFIG['MOZ_CAIRO_OSLIBS']
 OS_LIBS += CONFIG['MOZ_WEBRTC_X11_LIBS']
 
-if CONFIG['MOZ_LIB_SECRET']:
-    OS_LIBS += CONFIG['MOZ_LIB_SECRET_LIBS']
-
 if CONFIG['MOZ_SYSTEM_JPEG']:
     OS_LIBS += CONFIG['MOZ_JPEG_LIBS']
 
 if CONFIG['MOZ_SYSTEM_PNG']:
     OS_LIBS += CONFIG['MOZ_PNG_LIBS']
 
 if CONFIG['MOZ_SYSTEM_WEBP']:
     OS_LIBS += CONFIG['MOZ_WEBP_LIBS']