browser_cert_export.js (4948B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { getPEMString } = ChromeUtils.importESModule( 7 "resource://gre/modules/psm/pippki.sys.mjs" 8 ); 9 10 var MockFilePicker = SpecialPowers.MockFilePicker; 11 12 function createTemporarySaveDirectory() { 13 var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); 14 saveDir.append("testsavedir"); 15 if (!saveDir.exists()) { 16 info("create testsavedir!"); 17 saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); 18 } 19 info("return from createTempSaveDir: " + saveDir.path); 20 return saveDir; 21 } 22 23 // Create the folder the certificates will be saved into. 24 var destDir = createTemporarySaveDirectory(); 25 registerCleanupFunction(function () { 26 destDir.remove(true); 27 BrowserTestUtils.removeTab(gBrowser.selectedTab); 28 }); 29 30 function stringOrArrayEquals(actual, expected, message) { 31 is( 32 typeof actual, 33 typeof expected, 34 "actual, expected should have the same type" 35 ); 36 if (typeof expected == "string") { 37 is(actual, expected, message); 38 } else { 39 is(actual.toString(), expected.toString(), message); 40 } 41 } 42 43 var dialogWin; 44 var exportButton; 45 var expectedCert; 46 47 async function setupTest() { 48 await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true }); 49 let certButton = gBrowser.selectedBrowser.contentDocument.getElementById( 50 "viewCertificatesButton" 51 ); 52 certButton.scrollIntoView(); 53 let certDialogLoaded = promiseLoadSubDialog( 54 "chrome://pippki/content/certManager.xhtml" 55 ); 56 certButton.click(); 57 dialogWin = await certDialogLoaded; 58 let doc = dialogWin.document; 59 doc.getElementById("certmanagertabs").selectedTab = 60 doc.getElementById("ca_tab"); 61 let treeView = doc.getElementById("ca-tree").view; 62 // Select any which cert. Ignore parent rows (ie rows without certs): 63 for (let i = 0; i < treeView.rowCount; i++) { 64 treeView.selection.select(i); 65 dialogWin.getSelectedCerts(); 66 let certs = dialogWin.selected_certs; // yuck... but this is how the dialog works. 67 if (certs && certs.length == 1 && certs[0]) { 68 expectedCert = certs[0]; 69 // OK, we managed to select a cert! 70 break; 71 } 72 } 73 74 exportButton = doc.getElementById("ca_exportButton"); 75 is(exportButton.disabled, false, "Should enable export button"); 76 } 77 78 async function checkCertExportWorks( 79 exportType, 80 encoding, 81 expectedFileContents 82 ) { 83 MockFilePicker.displayDirectory = destDir; 84 var destFile = destDir.clone(); 85 MockFilePicker.init(window.browsingContext); 86 MockFilePicker.filterIndex = exportType; 87 MockFilePicker.showCallback = function (fp) { 88 info("showCallback"); 89 let fileName = fp.defaultString; 90 info("fileName: " + fileName); 91 destFile.append(fileName); 92 MockFilePicker.setFiles([destFile]); 93 info("done showCallback"); 94 }; 95 let finishedExporting = TestUtils.topicObserved("cert-export-finished"); 96 exportButton.click(); 97 await finishedExporting; 98 MockFilePicker.cleanup(); 99 if (destFile && destFile.exists()) { 100 let contents; 101 if (encoding === "utf-8") { 102 contents = await IOUtils.readUTF8(destFile.path); 103 } else { 104 is(encoding, "", "expected either utf-8 or empty string for encoding"); 105 contents = await IOUtils.read(destFile.path); 106 } 107 stringOrArrayEquals( 108 contents, 109 expectedFileContents, 110 "Should have written correct contents" 111 ); 112 destFile.remove(false); 113 } else { 114 ok(false, "No cert saved!"); 115 } 116 } 117 118 add_task(setupTest); 119 120 add_task(async function checkCertPEMExportWorks() { 121 let expectedContents = getPEMString(expectedCert); 122 await checkCertExportWorks(0, /* 0 = PEM */ "utf-8", expectedContents); 123 }); 124 125 add_task(async function checkCertPEMChainExportWorks() { 126 let expectedContents = getPEMString(expectedCert); 127 await checkCertExportWorks( 128 1, // 1 = PEM chain, but the chain is of length 1 129 "utf-8", 130 expectedContents 131 ); 132 }); 133 134 add_task(async function checkCertDERExportWorks() { 135 let expectedContents = Uint8Array.from(expectedCert.getRawDER()); 136 await checkCertExportWorks(2, /* 2 = DER */ "", expectedContents); 137 }); 138 139 function stringToTypedArray(str) { 140 let arr = new Uint8Array(str.length); 141 for (let i = 0; i < arr.length; i++) { 142 arr[i] = str.charCodeAt(i); 143 } 144 return arr; 145 } 146 147 add_task(async function checkCertPKCS7ExportWorks() { 148 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 149 Ci.nsIX509CertDB 150 ); 151 let expectedContents = stringToTypedArray(certdb.asPKCS7Blob([expectedCert])); 152 await checkCertExportWorks(3, /* 3 = PKCS7 */ "", expectedContents); 153 }); 154 155 add_task(async function checkCertPKCS7ChainExportWorks() { 156 let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 157 Ci.nsIX509CertDB 158 ); 159 let expectedContents = stringToTypedArray(certdb.asPKCS7Blob([expectedCert])); 160 await checkCertExportWorks( 161 4, // 4 = PKCS7 chain, but the chain is of length 1 162 "", 163 expectedContents 164 ); 165 });