Skip to content

Instantly share code, notes, and snippets.

@mozkeeler
Created January 2, 2019 20:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mozkeeler/033f89c23da9307f386f6ef9357d74b7 to your computer and use it in GitHub Desktop.
Save mozkeeler/033f89c23da9307f386f6ef9357d74b7 to your computer and use it in GitHub Desktop.
# HG changeset patch
# User Dana Keeler <dkeeler@mozilla.com>
# Date 1540589411 25200
# Fri Oct 26 14:30:11 2018 -0700
# Node ID 1e46bd8dc997145cc29c7e8465d1648eab52c84e
# Parent 5826b2352ac08248205d3b0e29587ab8ad415bfe
bug 1498909 - dynamically load libsecret at runtime if available r?franziskus,jcj
Summary:
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.
Reviewers: franziskus, jcj
Tags: #secure-revision
Differential Revision: https://phabricator.services.mozilla.com/D9969
diff --git a/config/system-headers.mozbuild b/config/system-headers.mozbuild
--- 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',
- ]
diff --git a/security/manager/ssl/LibSecret.cpp b/security/manager/ssl/LibSecret.cpp
--- 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;
}
diff --git a/security/manager/ssl/LibSecret.h b/security/manager/ssl/LibSecret.h
--- 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,
diff --git a/security/manager/ssl/OSKeyStore.cpp b/security/manager/ssl/OSKeyStore.cpp
--- 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))) {
diff --git a/security/manager/ssl/moz.build b/security/manager/ssl/moz.build
--- 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',
diff --git a/security/manager/ssl/tests/unit/test_oskeystore.js b/security/manager/ssl/tests/unit/test_oskeystore.js
--- a/security/manager/ssl/tests/unit/test_oskeystore.js
+++ b/security/manager/ssl/tests/unit/test_oskeystore.js
@@ -156,8 +156,69 @@ 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();
+});
+
+// Test that "recovering" a zero-length secret doesn't throw but also doesn't result in that label
+// slot being unusable to store a new secret.
+add_task(async function() {
+ await delete_all_secrets();
+
+ let keystore = Cc["@mozilla.org/security/oskeystore;1"]
+ .getService(Ci.nsIOSKeyStore);
+ await keystore.asyncRecoverSecret(LABELS[0], "");
+ ok(!await keystore.asyncSecretAvailable(LABELS[0]),
+ "'recovering' a zero-length secret doesn't throw, but the secret is not 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();
+});
diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build
--- 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']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment