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

branch
TOR_BUG_9701
changeset 3
141e0f1194b1
equal deleted inserted replaced
-1:000000000000 0:112e766cebd0
1 "use strict";
2 /* To regenerate the certificates and apps for this test:
3
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
8
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.
12
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 */
17
18 // XXX from prio.h
19 const PR_RDWR = 0x04;
20 const PR_CREATE_FILE = 0x08;
21 const PR_TRUNCATE = 0x20;
22
23 do_get_profile(); // must be called before getting nsIX509CertDB
24 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(Ci.nsIX509CertDB);
25
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 }
71
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 }
79
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 }
99
100 function removeEntry(entry, entryInput) { return [null, null]; }
101
102 function truncateEntry(entry, entryInput) {
103 if (entryInput.available() == 0)
104 throw "Truncating already-zero length entry will result in identical entry.";
105
106 var content = Cc["@mozilla.org/io/string-input-stream;1"]
107 .createInstance(Ci.nsIStringInputStream);
108 content.data = "";
109
110 return [entry, content]
111 }
112
113 function run_test() {
114 run_next_test();
115 }
116
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 }
126
127 function original_app_path(test_name) {
128 return do_get_file("test_signed_apps/" + test_name + ".zip", false);
129 }
130
131 function tampered_app_path(test_name) {
132 return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
133 }
134
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 });
140
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 });
146
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 });
153
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 });
162
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 });
170
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 });
178
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 });
187
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 });
195
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 });
203
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 });
212
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 });
222
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