diff options
Diffstat (limited to 'modules/libmar/tests/unit/test_sign_verify.js')
-rw-r--r-- | modules/libmar/tests/unit/test_sign_verify.js | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/modules/libmar/tests/unit/test_sign_verify.js b/modules/libmar/tests/unit/test_sign_verify.js new file mode 100644 index 0000000000..20f0d691ac --- /dev/null +++ b/modules/libmar/tests/unit/test_sign_verify.js @@ -0,0 +1,591 @@ +/* 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); +} |