security/manager/ssl/tests/unit/test_signed_apps.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 "use strict";
     2 /* To regenerate the certificates and apps for this test:
     4         cd security/manager/ssl/tests/unit/test_signed_apps
     5         PATH=$NSS/bin:$NSS/lib:$PATH ./generate.sh
     6         cd ../../../../../..
     7         make -C $OBJDIR/security/manager/ssl/tests
     9    $NSS is the path to NSS binaries and libraries built for the host platform. 
    10    If you get error messages about "CertUtil" on Windows, then it means that
    11    the Windows CertUtil.exe is ahead of the NSS certutil.exe in $PATH.
    13    Check in the generated files. These steps are not done as part of the build
    14    because we do not want to add a build-time dependency on the OpenSSL or NSS
    15    tools or libraries built for the host platform.
    16 */
    18 // XXX from prio.h
    19 const PR_RDWR        = 0x04; 
    20 const PR_CREATE_FILE = 0x08;
    21 const PR_TRUNCATE    = 0x20;
    23 do_get_profile(); // must be called before getting nsIX509CertDB
    24 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
    26 // Creates a new app package based in the inFilePath package, with a set of
    27 // modifications (including possibly deletions) applied to the existing entries,
    28 // and/or a set of new entries to be included.
    29 function tamper(inFilePath, outFilePath, modifications, newEntries) {
    30   var writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
    31   writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
    32   try {
    33     var reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
    34     reader.open(inFilePath);
    35     try {
    36       var entries = reader.findEntries("");
    37       while (entries.hasMore()) {
    38         var entryName = entries.getNext();
    39         var inEntry = reader.getEntry(entryName);
    40         var entryInput = reader.getInputStream(entryName);
    41         try {
    42           var f = modifications[entryName];
    43           var outEntry, outEntryInput;
    44           if (f) {
    45             [outEntry, outEntryInput] = f(inEntry, entryInput);
    46             delete modifications[entryName];
    47           } else {
    48             [outEntry, outEntryInput] = [inEntry, entryInput];
    49           }
    50           // if f does not want the input entry to be copied to the output entry
    51           // at all (i.e. it wants it to be deleted), it will return null.
    52           if (outEntryInput) {
    53             try {
    54               writer.addEntryStream(entryName,
    55                                     outEntry.lastModifiedTime,
    56                                     outEntry.compression,
    57                                     outEntryInput,
    58                                     false);
    59             } finally {
    60               if (entryInput != outEntryInput)
    61                 outEntryInput.close();
    62             }
    63           }
    64         } finally {
    65           entryInput.close();
    66         }
    67       }
    68     } finally {
    69       reader.close();
    70     }
    72     // Any leftover modification means that we were expecting to modify an entry
    73     // in the input file that wasn't there.
    74     for(var name in modifications) {
    75       if (modifications.hasOwnProperty(name)) {
    76         throw "input file was missing expected entries: " + name;
    77       }
    78     }
    80     // Now, append any new entries to the end
    81     newEntries.forEach(function(newEntry) {
    82       var sis = Cc["@mozilla.org/io/string-input-stream;1"]
    83                   .createInstance(Ci.nsIStringInputStream);
    84       try {
    85         sis.setData(newEntry.content, newEntry.content.length);
    86         writer.addEntryStream(newEntry.name,
    87                               new Date(),
    88                               Ci.nsIZipWriter.COMPRESSION_BEST,
    89                               sis,
    90                               false);
    91       } finally {
    92         sis.close();
    93       }
    94     });
    95   } finally {
    96     writer.close();
    97   }
    98 }
   100 function removeEntry(entry, entryInput) { return [null, null]; }
   102 function truncateEntry(entry, entryInput) {
   103   if (entryInput.available() == 0)
   104     throw "Truncating already-zero length entry will result in identical entry.";
   106   var content = Cc["@mozilla.org/io/string-input-stream;1"]
   107                   .createInstance(Ci.nsIStringInputStream);
   108   content.data = "";
   110   return [entry, content]
   111 }
   113 function run_test() {
   114   run_next_test();
   115 }
   117 function check_open_result(name, expectedRv) {
   118   return function openSignedAppFileCallback(rv, aZipReader, aSignerCert) {
   119     do_print("openSignedAppFileCallback called for " + name);
   120     do_check_eq(rv, expectedRv);
   121     do_check_eq(aZipReader != null,  Components.isSuccessCode(expectedRv));
   122     do_check_eq(aSignerCert != null, Components.isSuccessCode(expectedRv));
   123     run_next_test();
   124   };
   125 }
   127 function original_app_path(test_name) {
   128   return do_get_file("test_signed_apps/" + test_name + ".zip", false);
   129 }
   131 function tampered_app_path(test_name) {
   132   return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
   133 }
   135 add_test(function () {
   136   certdb.openSignedAppFileAsync(
   137     Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
   138     check_open_result("valid", Cr.NS_OK));
   139 });
   141 add_test(function () {
   142   certdb.openSignedAppFileAsync(
   143     Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app_1"),
   144     check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
   145 });
   147 add_test(function () {
   148   certdb.openSignedAppFileAsync(
   149     Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app_1"),
   150     check_open_result("unknown_issuer",
   151                       getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
   152 });
   154 // Sanity check to ensure a no-op tampering gives a valid result
   155 add_test(function () {
   156   var tampered = tampered_app_path("identity_tampering");
   157   tamper(original_app_path("valid_app_1"), tampered, { }, []);
   158   certdb.openSignedAppFileAsync(
   159     Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
   160     check_open_result("identity_tampering", Cr.NS_OK));
   161 });
   163 add_test(function () {
   164   var tampered = tampered_app_path("missing_rsa");
   165   tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.RSA" : removeEntry }, []);
   166   certdb.openSignedAppFileAsync(
   167     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   168     check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
   169 });
   171 add_test(function () {
   172   var tampered = tampered_app_path("missing_sf");
   173   tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.SF" : removeEntry }, []);
   174   certdb.openSignedAppFileAsync(
   175     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   176     check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
   177 });
   179 add_test(function () {
   180   var tampered = tampered_app_path("missing_manifest_mf");
   181   tamper(original_app_path("valid_app_1"), tampered, { "META-INF/MANIFEST.MF" : removeEntry }, []);
   182   certdb.openSignedAppFileAsync(
   183     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   184     check_open_result("missing_manifest_mf",
   185                       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
   186 });
   188 add_test(function () {
   189   var tampered = tampered_app_path("missing_entry");
   190   tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp" : removeEntry }, []);
   191   certdb.openSignedAppFileAsync(
   192     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   193     check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
   194 });
   196 add_test(function () {
   197   var tampered = tampered_app_path("truncated_entry");
   198   tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp" : truncateEntry }, []);
   199   certdb.openSignedAppFileAsync(
   200     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   201     check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
   202 });
   204 add_test(function () {
   205   var tampered = tampered_app_path("unsigned_entry");
   206   tamper(original_app_path("valid_app_1"), tampered, {},
   207     [ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
   208   certdb.openSignedAppFileAsync(
   209     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   210     check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
   211 });
   213 add_test(function () {
   214   var tampered = tampered_app_path("unsigned_metainf_entry");
   215   tamper(original_app_path("valid_app_1"), tampered, {},
   216     [ { name: "META-INF/unsigned.txt", content: "unsigned content!" } ]);
   217   certdb.openSignedAppFileAsync(
   218     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
   219     check_open_result("unsigned_metainf_entry",
   220                       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
   221 });
   223 // TODO: tampered MF, tampered SF
   224 // TODO: too-large MF, too-large RSA, too-large SF
   225 // TODO: MF and SF that end immediately after the last main header
   226 //       (no CR nor LF)
   227 // TODO: broken headers to exercise the parser

mercurial