copy from security/manager/ssl/tests/unit/test_sdr_preexisting.js
copy to security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
--- a/security/manager/ssl/tests/unit/test_sdr_preexisting.js
+++ b/security/manager/ssl/tests/unit/test_sdr_preexisting_with_password.js
@@ -1,187 +1,68 @@
// -*- indent-tabs-mode: nil; js-indent-level: 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/.
"use strict";
// Tests that the SDR implementation is able to decrypt strings encrypted using
-// a preexisting NSS key database. Creating the database is straight-forward:
-// simply run Firefox (or xpcshell) and encrypt something using
-// nsISecretDecoderRing (e.g. by saving a password or directly using the
-// interface). The resulting key3.db file (in the profile directory) now
-// contains the private key used to encrypt the data.
-// "Upgrading" a key3.db with certutil to use on Android appears not to work.
-// Because the keys have to be the same for this test to work the way it does,
-// the key from key3.db must be extracted and added to a new key4.db. This can
-// be done with NSS' PK11_* APIs like so (although note that the following code
-// is not guaranteed to compile or work, but is more of a guideline for how to
-// do this in the future if necessary):
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-//
-// void printPRError(const char* message) {
-// fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-// if (NSS_Initialize(".", "", "", "", NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT)
-// != SECSuccess) {
-// printPRError("NSS_Initialize failed");
-// return 1;
-// }
-//
-// PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-// if (!slot) {
-// printPRError("PK11_GetInternalKeySlot failed");
-// return 1;
-// }
-//
-// // Create a key to wrap the SDR key to export it.
-// unsigned char wrappingKeyIDBytes[] = { 0 };
-// SECItem wrappingKeyID = {
-// siBuffer,
-// wrappingKeyIDBytes,
-// sizeof(wrappingKeyIDBytes)
-// };
-// PK11SymKey* wrappingKey = PK11_TokenKeyGen(slot, CKM_DES3_CBC, 0, 0,
-// &wrappingKeyID, PR_FALSE, NULL);
-// if (!wrappingKey) {
-// printPRError("PK11_TokenKeyGen failed");
-// return 1;
-// }
-//
-// // This is the magic identifier NSS uses for the SDR key.
-// unsigned char sdrKeyIDBytes[] = {
-// 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
-// };
-// SECItem sdrKeyID = { siBuffer, sdrKeyIDBytes, sizeof(sdrKeyIDBytes) };
-// PK11SymKey* sdrKey = PK11_FindFixedKey(slot, CKM_DES3_CBC, &sdrKeyID,
-// NULL);
-// if (!sdrKey) {
-// printPRError("PK11_FindFixedKey failed");
-// return 1;
-// }
-//
-// // Wrap the SDR key.
-// unsigned char wrappedKeyBuf[1024];
-// SECItem wrapped = { siBuffer, wrappedKeyBuf, sizeof(wrappedKeyBuf) };
-// if (PK11_WrapSymKey(CKM_DES3_ECB, NULL, wrappingKey, sdrKey, &wrapped)
-// != SECSuccess) {
-// printPRError("PK11_WrapSymKey failed");
-// return 1;
-// }
-//
-// // Unwrap the SDR key (NSS considers the SDR key "sensitive" and so won't
-// // just export it as raw key material - we have to export it and then
-// // re-import it as non-sensitive to get that data.
-// PK11SymKey* unwrapped = PK11_UnwrapSymKey(wrappingKey, CKM_DES3_ECB, NULL,
-// &wrapped, CKM_DES3_CBC,
-// CKA_ENCRYPT, 0);
-// if (!unwrapped) {
-// printPRError("PK11_UnwrapSymKey failed");
-// return 1;
-// }
-// if (PK11_ExtractKeyValue(unwrapped) != SECSuccess) {
-// printPRError("PK11_ExtractKeyValue failed");
-// return 1;
-// }
-// SECItem* keyData = PK11_GetKeyData(unwrapped);
-// if (!keyData) {
-// printPRError("PK11_GetKeyData failed");
-// return 1;
-// }
-// for (int i = 0; i < keyData->len; i++) {
-// printf("0x%02hhx, ", keyData->data[i]);
-// }
-// printf("\n");
-//
-// PK11_FreeSymKey(unwrapped);
-// PK11_FreeSymKey(sdrKey);
-// PK11_FreeSymKey(wrappingKey);
-// PK11_FreeSlot(slot);
-//
-// if (NSS_Shutdown() != SECSuccess) {
-// printPRError("NSS_Shutdown failed");
-// return 1;
-// }
-// return 0;
-// }
-//
-// The output of compiling and running the above should be the bytes of the SDR
-// key. Given that, create a key4.db with an empty password using
-// `certutil -N -d sql:.` and then compile and run the following:
-//
-// #include <stdio.h>
-//
-// #include "nss.h"
-// #include "pk11pub.h"
-// #include "prerror.h"
-// #include "secerr.h"
-// #include "secmod.h"
-//
-// void printPRError(const char* message) {
-// fprintf(stderr, "%s: %s\n", message, PR_ErrorToString(PR_GetError(), 0));
-// }
-//
-// int main(int argc, char* argv[]) {
-// if (NSS_Initialize("sql:.", "", "", "",
-// NSS_INIT_NOMODDB | NSS_INIT_NOROOTINIT) != SECSuccess) {
-// printPRError("NSS_Initialize failed");
-// return 1;
-// }
-//
-// PK11SlotInfo* slot = PK11_GetInternalKeySlot();
-// if (!slot) {
-// printPRError("PK11_GetInternalKeySlot failed");
-// return 1;
-// }
-//
-// // These are the bytes of the SDR key from the previous step:
-// unsigned char keyBytes[] = {
-// 0x70, 0xab, 0xea, 0x1f, 0x8f, 0xe3, 0x4a, 0x7a, 0xb5, 0xb0, 0x43, 0xe5,
-// 0x51, 0x83, 0x86, 0xe5, 0xb3, 0x43, 0xa8, 0x1f, 0xc1, 0x57, 0x86, 0x46
-// };
-// SECItem keyItem = { siBuffer, keyBytes, sizeof(keyBytes) };
-// PK11SymKey* key = PK11_ImportSymKey(slot, CKM_DES3_CBC, PK11_OriginUnwrap,
-// CKA_ENCRYPT, &keyItem, NULL);
-// if (!key) {
-// printPRError("PK11_ImportSymKey failed");
-// return 1;
-// }
-//
-// PK11_FreeSymKey(key);
-// PK11_FreeSlot(slot);
-//
-// if (NSS_Shutdown() != SECSuccess) {
-// printPRError("NSS_Shutdown failed");
-// return 1;
-// }
-// return 0;
-// }
-//
-// This should create a key4.db file with the SDR key. (Note however that this
-// does not set the magic key ID for the SDR key. Currently this is not a
-// problem because the NSS implementation that backs the SDR simply tries all
-// applicable keys it has when decrypting, so this still works.)
+// a preexisting NSS key database that a) has a password and b) is in the old
+// dbm format.
+// To create such a database, run a version Firefox (or xpcshell) where the
+// default database format is the old dbm format (i.e. pre-bug 783994), set a
+// master password, and then encrypt something using nsISecretDecoderRing.
+// This does not apply to Android as the dbm implementation was never enabled on
+// that platform.
+
+var gMockPrompter = {
+ passwordToTry: "password",
+ numPrompts: 0,
+
+ // This intentionally does not use arrow function syntax to avoid an issue
+ // where in the context of the arrow function, |this != gMockPrompter| due to
+ // how objects get wrapped when going across xpcom boundaries.
+ promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
+ this.numPrompts++;
+ if (this.numPrompts > 1) { // don't keep retrying a bad password
+ return false;
+ }
+ equal(text,
+ "Please enter your master password.",
+ "password prompt text should be as expected");
+ equal(checkMsg, null, "checkMsg should be null");
+ ok(this.passwordToTry, "passwordToTry should be non-null");
+ password.value = this.passwordToTry;
+ return true;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+};
+
+// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
+// to call promptPassword. We return the mock one, above.
+var gWindowWatcher = {
+ getNewPrompter: () => gMockPrompter,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWindowWatcher]),
+};
function run_test() {
- const isAndroid = AppConstants.platform == "android";
- const keyDBName = isAndroid ? "key4.db" : "key3.db";
+ let windowWatcherCID =
+ MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
+ gWindowWatcher);
+ do_register_cleanup(() => {
+ MockRegistrar.unregister(windowWatcherCID);
+ });
+
+ Services.prefs.setBoolPref("security.use_sqldb", true);
+
let profile = do_get_profile();
- let keyDBFile = do_get_file(`test_sdr_preexisting/${keyDBName}`);
- keyDBFile.copyTo(profile, keyDBName);
+ let keyDBFile = do_get_file("test_sdr_preexisting_with_password/key3.db");
+ keyDBFile.copyTo(profile, "key3.db");
let sdr = Cc["@mozilla.org/security/sdr;1"]
.getService(Ci.nsISecretDecoderRing);
let testcases = [
// a full padding block
{ ciphertext: "MDoEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECGeDHwVfyFqzBBAYvqMq/kDMsrARVNdC1C8d",
plaintext: "password" },
@@ -206,9 +87,11 @@ function run_test() {
plaintext: "nnLbuwLRkhlongerandsotakesupmultipleblocks" },
];
for (let testcase of testcases) {
let decrypted = sdr.decryptString(testcase.ciphertext);
equal(decrypted, testcase.plaintext,
"decrypted ciphertext should match expected plaintext");
}
+ equal(gMockPrompter.numPrompts, 1,
+ "Should have been prompted for a password once");
}