/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function run_test() { /** * Signs a MAR file. * * @param inMAR The MAR file that should be signed * @param outMAR The MAR file to create */ function signMAR(inMAR, outMAR, certs, wantSuccess, useShortHandCmdLine) { // Get a process to the signmar binary from the dist/bin directory. let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); let signmarBin = do_get_file("signmar" + BIN_SUFFIX); // Make sure the signmar binary exists and is an executable. Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); // Setup the command line arguments to sign the MAR. let NSSConfigDir = do_get_file("data"); let args = ["-d", NSSConfigDir.path]; if (certs.length == 1 && useShortHandCmdLine) { args.push("-n", certs[0]); } else { for (let i = 0; i < certs.length; i++) { args.push("-n" + i, certs[i]); } } args.push("-s", inMAR.path, outMAR.path); let exitValue; process.init(signmarBin); try { process.run(true, args, args.length); exitValue = process.exitValue; } catch (e) { // On Windows negative return value throws an exception exitValue = -1; } // Verify signmar returned 0 for success. if (wantSuccess) { Assert.equal(exitValue, 0); } else { Assert.notEqual(exitValue, 0); } } /** * Extract a MAR signature. * * @param inMAR The MAR file who's signature should be extracted * @param sigIndex The index of the signature to extract * @param extractedSig The file where the extracted signature will be stored * @param wantSuccess True if a successful signmar return code is desired */ function extractMARSignature(inMAR, sigIndex, extractedSig, wantSuccess) { // Get a process to the signmar binary from the dist/bin directory. let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); let signmarBin = do_get_file("signmar" + BIN_SUFFIX); // Make sure the signmar binary exists and is an executable. Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); // Setup the command line arguments to extract the signature in the MAR. let args = ["-n" + sigIndex, "-X", inMAR.path, extractedSig.path]; let exitValue; process.init(signmarBin); try { process.run(true, args, args.length); exitValue = process.exitValue; } catch (e) { // On Windows negative return value throws an exception exitValue = -1; } // Verify signmar returned 0 for success. if (wantSuccess) { Assert.equal(exitValue, 0); } else { Assert.notEqual(exitValue, 0); } } /** * Import a MAR signature. * * @param inMAR The MAR file who's signature should be imported to * @param sigIndex The index of the signature to import to * @param sigFile The file where the base64 signature exists * @param outMAR The same as inMAR but with the specified signature * swapped at the specified index. * @param wantSuccess True if a successful signmar return code is desired */ function importMARSignature(inMAR, sigIndex, sigFile, outMAR, wantSuccess) { // Get a process to the signmar binary from the dist/bin directory. let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); let signmarBin = do_get_file("signmar" + BIN_SUFFIX); // Make sure the signmar binary exists and is an executable. Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); // Setup the command line arguments to import the signature in the MAR. let args = ["-n" + sigIndex, "-I", inMAR.path, sigFile.path, outMAR.path]; let exitValue; process.init(signmarBin); try { process.run(true, args, args.length); exitValue = process.exitValue; } catch (e) { // On Windows negative return value throws an exception exitValue = -1; } // Verify signmar returned 0 for success. if (wantSuccess) { Assert.equal(exitValue, 0); } else { Assert.notEqual(exitValue, 0); } } /** * Verifies a MAR file. * * @param signedMAR Verifies a MAR file */ function verifyMAR(signedMAR, wantSuccess, certs, useShortHandCmdLine) { // Get a process to the signmar binary from the dist/bin directory. let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); let signmarBin = do_get_file("signmar" + BIN_SUFFIX); // Make sure the signmar binary exists and is an executable. Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); // Will reference the arguments to use for verification in signmar let args = []; // Setup the command line arguments to create the MAR. // Windows & Mac vs. Linux/... have different command line for verification // since on Windows we verify with CryptoAPI, on Mac with Security // Transforms or CDSA/CSSM and on all other platforms we verify with NSS. So // on Windows and Mac we use an exported DER file and on other platforms we // use the NSS config db. if (mozinfo.os == "win" || mozinfo.os == "mac") { if (certs.length == 1 && useShortHandCmdLine) { args.push("-D", "data/" + certs[0] + ".der"); } else { for (let i = 0; i < certs.length; i++) { args.push("-D" + i, "data/" + certs[i] + ".der"); } } } else { let NSSConfigDir = do_get_file("data"); args = ["-d", NSSConfigDir.path]; if (certs.length == 1 && useShortHandCmdLine) { args.push("-n", certs[0]); } else { for (let i = 0; i < certs.length; i++) { args.push("-n" + i, certs[i]); } } } args.push("-v", signedMAR.path); let exitValue; process.init(signmarBin); try { // We put this in a try block because nsIProcess doesn't like -1 returns process.run(true, args, args.length); exitValue = process.exitValue; } catch (e) { // On Windows negative return value throws an exception exitValue = -1; } // Verify signmar returned 0 for success. if (wantSuccess) { Assert.equal(exitValue, 0); } else { Assert.notEqual(exitValue, 0); } } /** * Strips a MAR signature. * * @param signedMAR The MAR file that should be signed * @param outMAR The MAR file to write to with signature stripped */ function stripMARSignature(signedMAR, outMAR, wantSuccess) { // Get a process to the signmar binary from the dist/bin directory. let process = Cc["@mozilla.org/process/util;1"].createInstance( Ci.nsIProcess ); let signmarBin = do_get_file("signmar" + BIN_SUFFIX); // Make sure the signmar binary exists and is an executable. Assert.ok(signmarBin.exists()); Assert.ok(signmarBin.isExecutable()); // Setup the command line arguments to create the MAR. let args = ["-r", signedMAR.path, outMAR.path]; let exitValue; process.init(signmarBin); try { process.run(true, args, args.length); exitValue = process.exitValue; } catch (e) { // On Windows negative return value throws an exception exitValue = -1; } // Verify signmar returned 0 for success. if (wantSuccess) { Assert.equal(exitValue, 0); } else { Assert.notEqual(exitValue, 0); } } function cleanup() { let outMAR = tempDir.clone(); outMAR.append("signed_out.mar"); if (outMAR.exists()) { outMAR.remove(false); } outMAR = tempDir.clone(); outMAR.append("multiple_signed_out.mar"); if (outMAR.exists()) { outMAR.remove(false); } outMAR = tempDir.clone(); outMAR.append("out.mar"); if (outMAR.exists()) { outMAR.remove(false); } let outDir = tempDir.clone(); outDir.append("out"); if (outDir.exists()) { outDir.remove(true); } } const wantFailure = false; const wantSuccess = true; // Define the unit tests to run. let tests = { // Test signing a MAR file with a single signature test_sign_single: function _test_sign_single() { let inMAR = do_get_file("data/binary_data.mar"); let outMAR = tempDir.clone(); outMAR.append("signed_out.mar"); if (outMAR.exists()) { outMAR.remove(false); } signMAR(inMAR, outMAR, ["mycert"], wantSuccess, true); Assert.ok(outMAR.exists()); let outMARData = getBinaryFileData(outMAR); let refMAR = do_get_file("data/signed_pib.mar"); let refMARData = getBinaryFileData(refMAR); compareBinaryData(outMARData, refMARData); }, // Test signing a MAR file with multiple signatures test_sign_multiple: function _test_sign_multiple() { let inMAR = do_get_file("data/binary_data.mar"); let outMAR = tempDir.clone(); outMAR.append("multiple_signed_out.mar"); if (outMAR.exists()) { outMAR.remove(false); } Assert.ok(!outMAR.exists()); signMAR( inMAR, outMAR, ["mycert", "mycert2", "mycert3"], wantSuccess, true ); Assert.ok(outMAR.exists()); let outMARData = getBinaryFileData(outMAR); let refMAR = do_get_file("data/multiple_signed_pib.mar"); let refMARData = getBinaryFileData(refMAR); compareBinaryData(outMARData, refMARData); }, // Test verifying a signed MAR file test_verify_single: function _test_verify_single() { let signedMAR = do_get_file("data/signed_pib.mar"); verifyMAR(signedMAR, wantSuccess, ["mycert"], true); verifyMAR(signedMAR, wantSuccess, ["mycert"], false); }, // Test verifying a signed MAR file with too many certs fails. // Or if you want to look at it another way, One mycert signature // is missing. test_verify_single_too_many_certs: function _test_verify_single_too_many_certs() { let signedMAR = do_get_file("data/signed_pib.mar"); verifyMAR(signedMAR, wantFailure, ["mycert", "mycert"], true); verifyMAR(signedMAR, wantFailure, ["mycert", "mycert"], false); }, // Test verifying a signed MAR file fails when using a wrong cert test_verify_single_wrong_cert: function _test_verify_single_wrong_cert() { let signedMAR = do_get_file("data/signed_pib.mar"); verifyMAR(signedMAR, wantFailure, ["mycert2"], true); verifyMAR(signedMAR, wantFailure, ["mycert2"], false); }, // Test verifying a signed MAR file with multiple signatures test_verify_multiple: function _test_verify_multiple() { let signedMAR = do_get_file("data/multiple_signed_pib.mar"); verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]); }, // Test verifying an unsigned MAR file fails test_verify_unsigned_mar_file_fails: function _test_verify_unsigned_mar_file_fails() { let unsignedMAR = do_get_file("data/binary_data.mar"); verifyMAR(unsignedMAR, wantFailure, ["mycert", "mycert2", "mycert3"]); }, // Test verifying a signed MAR file with the same signature multiple // times fails. The input MAR has: mycert, mycert2, mycert3. // we're checking to make sure the number of verified signatures // is only 1 and not 3. Each signature should be verified once. test_verify_multiple_same_cert: function _test_verify_multiple_same_cert() { let signedMAR = do_get_file("data/multiple_signed_pib.mar"); verifyMAR(signedMAR, wantFailure, ["mycert", "mycert", "mycert"]); }, // Test verifying a signed MAR file with the correct signatures but in // a different order fails test_verify_multiple_wrong_order: function _test_verify_multiple_wrong_order() { let signedMAR = do_get_file("data/multiple_signed_pib.mar"); verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]); verifyMAR(signedMAR, wantFailure, ["mycert", "mycert3", "mycert2"]); verifyMAR(signedMAR, wantFailure, ["mycert2", "mycert", "mycert3"]); verifyMAR(signedMAR, wantFailure, ["mycert2", "mycert3", "mycert"]); verifyMAR(signedMAR, wantFailure, ["mycert3", "mycert", "mycert2"]); verifyMAR(signedMAR, wantFailure, ["mycert3", "mycert2", "mycert"]); }, // Test verifying a signed MAR file without a PIB test_verify_no_pib: function _test_verify_no_pib() { let signedMAR = do_get_file("data/signed_no_pib.mar"); verifyMAR(signedMAR, wantSuccess, ["mycert"], true); verifyMAR(signedMAR, wantSuccess, ["mycert"], false); }, // Test verifying a signed MAR file with multiple signatures without a PIB test_verify_no_pib_multiple: function _test_verify_no_pib_multiple() { let signedMAR = do_get_file("data/multiple_signed_no_pib.mar"); verifyMAR(signedMAR, wantSuccess, ["mycert", "mycert2", "mycert3"]); }, // Test verifying a crafted MAR file where the attacker tried to adjust // the version number manually. test_crafted_mar: function _test_crafted_mar() { let signedBadMAR = do_get_file("data/manipulated_signed.mar"); verifyMAR(signedBadMAR, wantFailure, ["mycert"], true); verifyMAR(signedBadMAR, wantFailure, ["mycert"], false); }, // Test verifying a file that doesn't exist fails test_bad_path_verify_fails: function _test_bad_path_verify_fails() { let noMAR = do_get_file("data/does_not_exist.mar", true); Assert.ok(!noMAR.exists()); verifyMAR(noMAR, wantFailure, ["mycert"], true); }, // Test to make sure a stripped MAR is the same as the original MAR test_strip_signature: function _test_strip_signature() { let originalMAR = do_get_file("data/binary_data.mar"); let signedMAR = tempDir.clone(); signedMAR.append("signed_out.mar"); let outMAR = tempDir.clone(); outMAR.append("out.mar", true); stripMARSignature(signedMAR, outMAR, wantSuccess); // Verify that the stripped MAR matches the original data MAR exactly let outMARData = getBinaryFileData(outMAR); let originalMARData = getBinaryFileData(originalMAR); compareBinaryData(outMARData, originalMARData); }, // Test to make sure a stripped multi-signature-MAR is the same as the original MAR test_strip_multiple_signatures: function _test_strip_multiple_signatures() { let originalMAR = do_get_file("data/binary_data.mar"); let signedMAR = tempDir.clone(); signedMAR.append("multiple_signed_out.mar"); let outMAR = tempDir.clone(); outMAR.append("out.mar"); stripMARSignature(signedMAR, outMAR, wantSuccess); // Verify that the stripped MAR matches the original data MAR exactly let outMARData = getBinaryFileData(outMAR); let originalMARData = getBinaryFileData(originalMAR); compareBinaryData(outMARData, originalMARData); }, // Test extracting the first signature in a MAR that has only a single signature test_extract_sig_single: function _test_extract_sig_single() { let inMAR = do_get_file("data/signed_pib.mar"); let extractedSig = do_get_file("extracted_signature", true); if (extractedSig.exists()) { extractedSig.remove(false); } extractMARSignature(inMAR, 0, extractedSig, wantSuccess); Assert.ok(extractedSig.exists()); let referenceSig = do_get_file("data/signed_pib_mar.signature.0"); compareBinaryData(extractedSig, referenceSig); }, // Test extracting the all signatures in a multi signature MAR // The input MAR has 3 signatures. test_extract_sig_multi: function _test_extract_sig_multi() { for (let i = 0; i < 3; i++) { let inMAR = do_get_file("data/multiple_signed_pib.mar"); let extractedSig = do_get_file("extracted_signature", true); if (extractedSig.exists()) { extractedSig.remove(false); } extractMARSignature(inMAR, i, extractedSig, wantSuccess); Assert.ok(extractedSig.exists()); let referenceSig = do_get_file("data/multiple_signed_pib_mar.sig." + i); compareBinaryData(extractedSig, referenceSig); } }, // Test extracting a signature that is out of range fails test_extract_sig_out_of_range: function _test_extract_sig_out_of_range() { let inMAR = do_get_file("data/signed_pib.mar"); let extractedSig = do_get_file("extracted_signature", true); if (extractedSig.exists()) { extractedSig.remove(false); } const outOfBoundsIndex = 5; extractMARSignature(inMAR, outOfBoundsIndex, extractedSig, wantFailure); Assert.ok(!extractedSig.exists()); }, // Test signing a file that doesn't exist fails test_bad_path_sign_fails: function _test_bad_path_sign_fails() { let inMAR = do_get_file("data/does_not_exist.mar", true); let outMAR = tempDir.clone(); outMAR.append("signed_out.mar"); Assert.ok(!inMAR.exists()); signMAR(inMAR, outMAR, ["mycert"], wantFailure, true); Assert.ok(!outMAR.exists()); }, // Test verifying only a subset of the signatures fails. // The input MAR has: mycert, mycert2, mycert3. // We're only verifying 2 of the 3 signatures and that should fail. test_verify_multiple_subset: function _test_verify_multiple_subset() { let signedMAR = do_get_file("data/multiple_signed_pib.mar"); verifyMAR(signedMAR, wantFailure, ["mycert", "mycert2"]); }, // Test importing the first signature in a MAR that has only // a single signature test_import_sig_single: function _test_import_sig_single() { // Make sure the input MAR was signed with mycert only let inMAR = do_get_file("data/signed_pib.mar"); verifyMAR(inMAR, wantSuccess, ["mycert"], false); verifyMAR(inMAR, wantFailure, ["mycert2"], false); verifyMAR(inMAR, wantFailure, ["mycert3"], false); // Get the signature file for this MAR signed with the key from mycert2 let sigFile = do_get_file("data/signed_pib_mar.signature.mycert2"); Assert.ok(sigFile.exists()); let outMAR = tempDir.clone(); outMAR.append("sigchanged_signed_pib.mar"); if (outMAR.exists()) { outMAR.remove(false); } // Run the import operation importMARSignature(inMAR, 0, sigFile, outMAR, wantSuccess); // Verify we have a new MAR file, that mycert no longer verifies and that, // mycert2 does verify Assert.ok(outMAR.exists()); verifyMAR(outMAR, wantFailure, ["mycert"], false); verifyMAR(outMAR, wantSuccess, ["mycert2"], false); verifyMAR(outMAR, wantFailure, ["mycert3"], false); // Compare the binary data to something that was signed originally // with the private key from mycert2 let refMAR = do_get_file("data/signed_pib_with_mycert2.mar"); Assert.ok(refMAR.exists()); let refMARData = getBinaryFileData(refMAR); let outMARData = getBinaryFileData(outMAR); compareBinaryData(outMARData, refMARData); }, // Test importing a signature that doesn't belong to the file // fails to verify. test_import_wrong_sig: function _test_import_wrong_sig() { // Make sure the input MAR was signed with mycert only let inMAR = do_get_file("data/signed_pib.mar"); verifyMAR(inMAR, wantSuccess, ["mycert"], false); verifyMAR(inMAR, wantFailure, ["mycert2"], false); verifyMAR(inMAR, wantFailure, ["mycert3"], false); // Get the signature file for multiple_signed_pib.mar signed with the // key from mycert let sigFile = do_get_file("data/multiple_signed_pib_mar.sig.0"); Assert.ok(sigFile.exists()); let outMAR = tempDir.clone(); outMAR.append("sigchanged_signed_pib.mar"); if (outMAR.exists()) { outMAR.remove(false); } // Run the import operation importMARSignature(inMAR, 0, sigFile, outMAR, wantSuccess); // Verify we have a new MAR file and that the mar file fails to verify // when using a signature for another mar file. Assert.ok(outMAR.exists()); verifyMAR(outMAR, wantFailure, ["mycert"], false); verifyMAR(outMAR, wantFailure, ["mycert2"], false); verifyMAR(outMAR, wantFailure, ["mycert3"], false); }, // Test importing to the second signature in a MAR that has multiple // signature test_import_sig_multiple: function _test_import_sig_multiple() { // Make sure the input MAR was signed with mycert only let inMAR = do_get_file("data/multiple_signed_pib.mar"); verifyMAR(inMAR, wantSuccess, ["mycert", "mycert2", "mycert3"], false); verifyMAR(inMAR, wantFailure, ["mycert", "mycert", "mycert3"], false); // Get the signature file for this MAR signed with the key from mycert let sigFile = do_get_file("data/multiple_signed_pib_mar.sig.0"); Assert.ok(sigFile.exists()); let outMAR = tempDir.clone(); outMAR.append("sigchanged_signed_pib.mar"); if (outMAR.exists()) { outMAR.remove(false); } // Run the import operation const secondSigPos = 1; importMARSignature(inMAR, secondSigPos, sigFile, outMAR, wantSuccess); // Verify we have a new MAR file and that mycert no longer verifies // and that mycert2 does verify Assert.ok(outMAR.exists()); verifyMAR(outMAR, wantSuccess, ["mycert", "mycert", "mycert3"], false); verifyMAR(outMAR, wantFailure, ["mycert", "mycert2", "mycert3"], false); // Compare the binary data to something that was signed originally // with the private keys from mycert, mycert, mycert3 let refMAR = do_get_file("data/multiple_signed_pib_2.mar"); Assert.ok(refMAR.exists()); let refMARData = getBinaryFileData(refMAR); let outMARData = getBinaryFileData(outMAR); compareBinaryData(outMARData, refMARData); }, // Test stripping a MAR that doesn't exist fails test_bad_path_strip_fails: function _test_bad_path_strip_fails() { let noMAR = do_get_file("data/does_not_exist.mar", true); Assert.ok(!noMAR.exists()); let outMAR = tempDir.clone(); outMAR.append("out.mar"); stripMARSignature(noMAR, outMAR, wantFailure); }, // Test extracting from a bad path fails test_extract_bad_path: function _test_extract_bad_path() { let noMAR = do_get_file("data/does_not_exist.mar", true); let extractedSig = do_get_file("extracted_signature", true); Assert.ok(!noMAR.exists()); if (extractedSig.exists()) { extractedSig.remove(false); } extractMARSignature(noMAR, 0, extractedSig, wantFailure); Assert.ok(!extractedSig.exists()); }, // Between each test make sure the out MAR does not exist. cleanup_per_test: function _cleanup_per_test() {}, }; cleanup(); // Run all the tests Assert.equal(run_tests(tests), Object.keys(tests).length - 1); registerCleanupFunction(cleanup); }