no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / ssl / tests / unit / test_signed_apps.js
blob1fe15b0bada456a4d213abbf25488bf31e746e85
1 "use strict";
3 // Tests the API nsIX509CertDB.openSignedAppFileAsync, which backs add-on
4 // signature verification. Testcases include various ways of tampering with
5 // add-ons as well as different hash algorithms used in the various
6 // signature/metadata files.
8 // from prio.h
9 const PR_RDWR = 0x04;
10 const PR_CREATE_FILE = 0x08;
11 const PR_TRUNCATE = 0x20;
12 const PR_USEC_PER_MSEC = 1000;
14 do_get_profile(); // must be called before getting nsIX509CertDB
15 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
16   Ci.nsIX509CertDB
19 // Creates a new app package based in the inFilePath package, with a set of
20 // modifications (including possibly deletions) applied to the existing entries,
21 // and/or a set of new entries to be included.
22 function tamper(inFilePath, outFilePath, modifications, newEntries) {
23   let writer = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
24   writer.open(outFilePath, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
25   try {
26     let reader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
27       Ci.nsIZipReader
28     );
29     reader.open(inFilePath);
30     try {
31       for (let entryName of reader.findEntries("")) {
32         let inEntry = reader.getEntry(entryName);
33         let entryInput = reader.getInputStream(entryName);
34         try {
35           let f = modifications[entryName];
36           let outEntry, outEntryInput;
37           if (f) {
38             [outEntry, outEntryInput] = f(inEntry, entryInput);
39             delete modifications[entryName];
40           } else {
41             [outEntry, outEntryInput] = [inEntry, entryInput];
42           }
43           // if f does not want the input entry to be copied to the output entry
44           // at all (i.e. it wants it to be deleted), it will return null.
45           if (outEntryInput) {
46             try {
47               writer.addEntryStream(
48                 entryName,
49                 outEntry.lastModifiedTime,
50                 outEntry.compression,
51                 outEntryInput,
52                 false
53               );
54             } finally {
55               if (entryInput != outEntryInput) {
56                 outEntryInput.close();
57               }
58             }
59           }
60         } finally {
61           entryInput.close();
62         }
63       }
64     } finally {
65       reader.close();
66     }
68     // Any leftover modification means that we were expecting to modify an entry
69     // in the input file that wasn't there.
70     for (let name in modifications) {
71       if (modifications.hasOwnProperty(name)) {
72         throw new Error("input file was missing expected entries: " + name);
73       }
74     }
76     // Now, append any new entries to the end
77     newEntries.forEach(function (newEntry) {
78       let sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
79         Ci.nsIStringInputStream
80       );
81       try {
82         sis.setByteStringData(newEntry.content);
83         writer.addEntryStream(
84           newEntry.name,
85           new Date() * PR_USEC_PER_MSEC,
86           Ci.nsIZipWriter.COMPRESSION_BEST,
87           sis,
88           false
89         );
90       } finally {
91         sis.close();
92       }
93     });
94   } finally {
95     writer.close();
96   }
99 function removeEntry() {
100   return [null, null];
103 function truncateEntry(entry, entryInput) {
104   if (entryInput.available() == 0) {
105     throw new Error(
106       "Truncating already-zero length entry will result in " +
107         "identical entry."
108     );
109   }
111   let content = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
112     Ci.nsIStringInputStream
113   );
114   content.setByteStringData("");
116   return [entry, content];
119 function check_open_result(name, expectedRv, expectedSignatureAlgorithms) {
120   return function openSignedAppFileCallback(rv, aZipReader, aSignatureInfos) {
121     info("openSignedAppFileCallback called for " + name);
122     equal(rv, expectedRv, "Actual and expected return value should match");
123     equal(
124       aZipReader != null,
125       Components.isSuccessCode(expectedRv),
126       "ZIP reader should be null only if the return value denotes failure"
127     );
128     equal(
129       aSignatureInfos.length,
130       expectedSignatureAlgorithms.length,
131       "Should have the same number of expected signature infos"
132     );
133     for (let i = 0; i < expectedSignatureAlgorithms.length; i++) {
134       equal(
135         aSignatureInfos[i].signatureAlgorithm,
136         expectedSignatureAlgorithms[i],
137         "Should have expected signature algorithm"
138       );
139     }
140     run_next_test();
141   };
144 function original_app_path(test_name) {
145   return do_get_file("test_signed_apps/" + test_name + ".zip", false);
148 function tampered_app_path(test_name) {
149   return new FileUtils.File(
150     PathUtils.join(
151       Services.dirsvc.get("TmpD", Ci.nsIFile).path,
152       `test_signed_app-${test_name}.zip`
153     )
154   );
157 var hashTestcases = [
158   // SHA-256 in PKCS#7 + SHA-256 present elsewhere => OK
159   {
160     name: "app_mf-1-256_sf-1-256_p7-1-256",
161     expectedResult: Cr.NS_OK,
162     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
163   },
164   {
165     name: "app_mf-1-256_sf-1-256_p7-256",
166     expectedResult: Cr.NS_OK,
167     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
168   },
169   {
170     name: "app_mf-1-256_sf-256_p7-1-256",
171     expectedResult: Cr.NS_OK,
172     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
173   },
174   {
175     name: "app_mf-1-256_sf-256_p7-256",
176     expectedResult: Cr.NS_OK,
177     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
178   },
179   {
180     name: "app_mf-256_sf-1-256_p7-1-256",
181     expectedResult: Cr.NS_OK,
182     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
183   },
184   {
185     name: "app_mf-256_sf-1-256_p7-256",
186     expectedResult: Cr.NS_OK,
187     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
188   },
189   {
190     name: "app_mf-256_sf-256_p7-1-256",
191     expectedResult: Cr.NS_OK,
192     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
193   },
194   {
195     name: "app_mf-256_sf-256_p7-256",
196     expectedResult: Cr.NS_OK,
197     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256],
198   },
200   // SHA-1 in PKCS#7 + SHA-1 present elsewhere => OK
201   {
202     name: "app_mf-1-256_sf-1-256_p7-1",
203     expectedResult: Cr.NS_OK,
204     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
205   },
206   {
207     name: "app_mf-1-256_sf-1_p7-1",
208     expectedResult: Cr.NS_OK,
209     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
210   },
211   {
212     name: "app_mf-1_sf-1-256_p7-1",
213     expectedResult: Cr.NS_OK,
214     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
215   },
216   {
217     name: "app_mf-1_sf-1_p7-1",
218     expectedResult: Cr.NS_OK,
219     expectedSignatureAlgorithms: [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1],
220   },
222   // SHA-256 in PKCS#7 + SHA-256 not present elsewhere => INVALID
223   {
224     name: "app_mf-1-256_sf-1_p7-1-256",
225     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
226     expectedSignatureAlgorithms: [],
227   },
228   {
229     name: "app_mf-1-256_sf-1_p7-256",
230     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
231     expectedSignatureAlgorithms: [],
232   },
233   {
234     name: "app_mf-1_sf-1-256_p7-1-256",
235     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
236     expectedSignatureAlgorithms: [],
237   },
238   {
239     name: "app_mf-1_sf-1-256_p7-256",
240     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
241     expectedSignatureAlgorithms: [],
242   },
243   {
244     name: "app_mf-1_sf-1_p7-1-256",
245     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
246     expectedSignatureAlgorithms: [],
247   },
248   {
249     name: "app_mf-1_sf-1_p7-256",
250     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
251     expectedSignatureAlgorithms: [],
252   },
253   {
254     name: "app_mf-1_sf-256_p7-1-256",
255     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
256     expectedSignatureAlgorithms: [],
257   },
258   {
259     name: "app_mf-1_sf-256_p7-256",
260     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
261     expectedSignatureAlgorithms: [],
262   },
263   {
264     name: "app_mf-256_sf-1_p7-1-256",
265     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
266     expectedSignatureAlgorithms: [],
267   },
268   {
269     name: "app_mf-256_sf-1_p7-256",
270     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
271     expectedSignatureAlgorithms: [],
272   },
274   // SHA-1 in PKCS#7 + SHA-1 not present elsewhere => INVALID
275   {
276     name: "app_mf-1-256_sf-256_p7-1",
277     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
278     expectedSignatureAlgorithms: [],
279   },
280   {
281     name: "app_mf-1_sf-256_p7-1",
282     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
283     expectedSignatureAlgorithms: [],
284   },
285   {
286     name: "app_mf-256_sf-1-256_p7-1",
287     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
288     expectedSignatureAlgorithms: [],
289   },
290   {
291     name: "app_mf-256_sf-1_p7-1",
292     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
293     expectedSignatureAlgorithms: [],
294   },
295   {
296     name: "app_mf-256_sf-256_p7-1",
297     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
298     expectedSignatureAlgorithms: [],
299   },
302 // Policy values for the preference "security.signed_app_signatures.policy"
303 const PKCS7WithSHA1OrSHA256 = 0b0;
304 const PKCS7_WITH_SHA256 = 0b1;
305 const COSEAndPKCS7WithSHA1OrSHA256 = 0b10;
306 const COSEAndPKCS7WithSHA256 = 0b11;
307 const COSERequiredAndPKCS7WithSHA1OrSHA256 = 0b100;
308 const COSERequiredAndPKCS7WithSHA256 = 0b101;
309 const COSEOnly = 0b110;
310 const COSEOnlyAgain = 0b111;
312 function add_signature_test(policy, test) {
313   // First queue up a test to set the desired policy:
314   add_test(function () {
315     Services.prefs.setIntPref("security.signed_app_signatures.policy", policy);
316     run_next_test();
317   });
318   // Then queue up the test itself:
319   add_test(test);
322 for (let testcase of hashTestcases) {
323   add_signature_test(PKCS7WithSHA1OrSHA256, function () {
324     certdb.openSignedAppFileAsync(
325       Ci.nsIX509CertDB.AppXPCShellRoot,
326       original_app_path(testcase.name),
327       check_open_result(
328         testcase.name,
329         testcase.expectedResult,
330         testcase.expectedSignatureAlgorithms
331       )
332     );
333   });
336 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
337   certdb.openSignedAppFileAsync(
338     Ci.nsIX509CertDB.AppXPCShellRoot,
339     original_app_path("empty_signerInfos"),
340     check_open_result(
341       "the signerInfos in the PKCS#7 signature is empty",
342       Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED,
343       []
344     )
345   );
348 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
349   certdb.openSignedAppFileAsync(
350     Ci.nsIX509CertDB.AppXPCShellRoot,
351     original_app_path("unsigned_app"),
352     check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, [])
353   );
356 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
357   certdb.openSignedAppFileAsync(
358     Ci.nsIX509CertDB.AppXPCShellRoot,
359     original_app_path("unknown_issuer_app"),
360     check_open_result(
361       "unknown_issuer",
362       getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER),
363       []
364     )
365   );
368 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
369   certdb.openSignedAppFileAsync(
370     Ci.nsIX509CertDB.AppXPCShellRoot,
371     original_app_path("cose_signed_with_pkcs7"),
372     check_open_result("cose_signed_with_pkcs7", Cr.NS_OK, [
373       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
374       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
375     ])
376   );
379 add_signature_test(COSEAndPKCS7WithSHA256, function () {
380   certdb.openSignedAppFileAsync(
381     Ci.nsIX509CertDB.AppXPCShellRoot,
382     original_app_path("app_mf-256_sf-256_p7-256"),
383     check_open_result("no COSE but correct PK#7", Cr.NS_OK, [
384       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
385     ])
386   );
389 add_signature_test(COSEAndPKCS7WithSHA256, function () {
390   certdb.openSignedAppFileAsync(
391     Ci.nsIX509CertDB.AppXPCShellRoot,
392     original_app_path("app_mf-1_sf-256_p7-256"),
393     check_open_result(
394       "no COSE and wrong PK#7 hash",
395       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
396       []
397     )
398   );
401 add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function () {
402   certdb.openSignedAppFileAsync(
403     Ci.nsIX509CertDB.AppXPCShellRoot,
404     original_app_path("app_mf-256_sf-256_p7-256"),
405     check_open_result(
406       "COSE signature missing (SHA1 or 256)",
407       Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE,
408       []
409     )
410   );
413 add_signature_test(COSERequiredAndPKCS7WithSHA256, function () {
414   certdb.openSignedAppFileAsync(
415     Ci.nsIX509CertDB.AppXPCShellRoot,
416     original_app_path("app_mf-256_sf-256_p7-256"),
417     check_open_result(
418       "COSE signature missing (SHA256)",
419       Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE,
420       []
421     )
422   );
425 add_signature_test(COSERequiredAndPKCS7WithSHA256, function () {
426   certdb.openSignedAppFileAsync(
427     Ci.nsIX509CertDB.AppXPCShellRoot,
428     original_app_path("only_cose_signed"),
429     check_open_result(
430       "COSE signature only (PK#7 allowed, not present)",
431       Cr.NS_OK,
432       [Ci.nsIAppSignatureInfo.COSE_WITH_SHA256]
433     )
434   );
437 add_signature_test(COSERequiredAndPKCS7WithSHA1OrSHA256, function () {
438   certdb.openSignedAppFileAsync(
439     Ci.nsIX509CertDB.AppXPCShellRoot,
440     original_app_path("only_cose_signed"),
441     check_open_result(
442       "COSE signature only (PK#7 allowed, not present)",
443       Cr.NS_OK,
444       [Ci.nsIAppSignatureInfo.COSE_WITH_SHA256]
445     )
446   );
449 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
450   certdb.openSignedAppFileAsync(
451     Ci.nsIX509CertDB.AppXPCShellRoot,
452     original_app_path("cose_multiple_signed_with_pkcs7"),
453     check_open_result("cose_multiple_signed_with_pkcs7", Cr.NS_OK, [
454       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
455       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
456     ])
457   );
460 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
461   certdb.openSignedAppFileAsync(
462     Ci.nsIX509CertDB.AppXPCShellRoot,
463     original_app_path("cose_int_signed_with_pkcs7"),
464     check_open_result("COSE signed with an intermediate", Cr.NS_OK, [
465       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
466       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
467     ])
468   );
471 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
472   certdb.openSignedAppFileAsync(
473     Ci.nsIX509CertDB.AppXPCShellRoot,
474     original_app_path("only_cose_signed"),
475     check_open_result(
476       "PK7 signature missing",
477       Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED,
478       []
479     )
480   );
483 add_signature_test(COSEOnly, function () {
484   certdb.openSignedAppFileAsync(
485     Ci.nsIX509CertDB.AppXPCShellRoot,
486     original_app_path("cose_multiple_signed_with_pkcs7"),
487     check_open_result(
488       "Expected only COSE signature",
489       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
490       []
491     )
492   );
495 add_signature_test(COSEOnly, function () {
496   certdb.openSignedAppFileAsync(
497     Ci.nsIX509CertDB.AppXPCShellRoot,
498     original_app_path("only_cose_multiple_signed"),
499     check_open_result("only Multiple COSE signatures", Cr.NS_OK, [
500       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
501     ])
502   );
505 add_signature_test(COSEOnly, function () {
506   certdb.openSignedAppFileAsync(
507     Ci.nsIX509CertDB.AppXPCShellRoot,
508     original_app_path("only_cose_signed"),
509     check_open_result("only_cose_signed", Cr.NS_OK, [
510       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
511     ])
512   );
515 add_signature_test(COSEOnlyAgain, function () {
516   certdb.openSignedAppFileAsync(
517     Ci.nsIX509CertDB.AppXPCShellRoot,
518     original_app_path("only_cose_signed"),
519     check_open_result("only_cose_signed (again)", Cr.NS_OK, [
520       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
521     ])
522   );
525 add_signature_test(COSEOnly, function () {
526   certdb.openSignedAppFileAsync(
527     Ci.nsIX509CertDB.AppXPCShellRoot,
528     original_app_path("cose_signed_with_pkcs7"),
529     check_open_result(
530       "COSE only expected but also PK#7 signed",
531       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
532       []
533     )
534   );
537 // Sanity check to ensure a no-op tampering gives a valid result
538 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
539   let tampered = tampered_app_path("identity_tampering");
540   tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, []);
541   certdb.openSignedAppFileAsync(
542     Ci.nsIX509CertDB.AppXPCShellRoot,
543     original_app_path("app_mf-1_sf-1_p7-1"),
544     check_open_result("identity_tampering", Cr.NS_OK, [
545       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
546     ])
547   );
550 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
551   let tampered = tampered_app_path("missing_rsa");
552   tamper(
553     original_app_path("app_mf-1_sf-1_p7-1"),
554     tampered,
555     { "META-INF/A.RSA": removeEntry },
556     []
557   );
558   certdb.openSignedAppFileAsync(
559     Ci.nsIX509CertDB.AppXPCShellRoot,
560     tampered,
561     check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED, [])
562   );
565 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
566   let tampered = tampered_app_path("missing_sf");
567   tamper(
568     original_app_path("app_mf-1_sf-1_p7-1"),
569     tampered,
570     { "META-INF/A.SF": removeEntry },
571     []
572   );
573   certdb.openSignedAppFileAsync(
574     Ci.nsIX509CertDB.AppXPCShellRoot,
575     tampered,
576     check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID, [])
577   );
580 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
581   let tampered = tampered_app_path("missing_manifest_mf");
582   tamper(
583     original_app_path("app_mf-1_sf-1_p7-1"),
584     tampered,
585     { "META-INF/MANIFEST.MF": removeEntry },
586     []
587   );
588   certdb.openSignedAppFileAsync(
589     Ci.nsIX509CertDB.AppXPCShellRoot,
590     tampered,
591     check_open_result(
592       "missing_manifest_mf",
593       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
594       []
595     )
596   );
599 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
600   let tampered = tampered_app_path("missing_entry");
601   tamper(
602     original_app_path("app_mf-1_sf-1_p7-1"),
603     tampered,
604     { "manifest.json": removeEntry },
605     []
606   );
607   certdb.openSignedAppFileAsync(
608     Ci.nsIX509CertDB.AppXPCShellRoot,
609     tampered,
610     check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING, [])
611   );
614 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
615   let tampered = tampered_app_path("truncated_entry");
616   tamper(
617     original_app_path("app_mf-1_sf-1_p7-1"),
618     tampered,
619     { "manifest.json": truncateEntry },
620     []
621   );
622   certdb.openSignedAppFileAsync(
623     Ci.nsIX509CertDB.AppXPCShellRoot,
624     tampered,
625     check_open_result(
626       "truncated_entry",
627       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
628       []
629     )
630   );
633 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
634   let tampered = tampered_app_path("truncated_manifestFile");
635   tamper(
636     original_app_path("app_mf-1_sf-1_p7-1"),
637     tampered,
638     { "META-INF/MANIFEST.MF": truncateEntry },
639     []
640   );
641   certdb.openSignedAppFileAsync(
642     Ci.nsIX509CertDB.AppXPCShellRoot,
643     tampered,
644     check_open_result(
645       "truncated_manifestFile",
646       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
647       []
648     )
649   );
652 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
653   let tampered = tampered_app_path("truncated_signatureFile");
654   tamper(
655     original_app_path("app_mf-1_sf-1_p7-1"),
656     tampered,
657     { "META-INF/A.SF": truncateEntry },
658     []
659   );
660   certdb.openSignedAppFileAsync(
661     Ci.nsIX509CertDB.AppXPCShellRoot,
662     tampered,
663     check_open_result(
664       "truncated_signatureFile",
665       getXPCOMStatusFromNSS(SEC_ERROR_PKCS7_BAD_SIGNATURE),
666       []
667     )
668   );
671 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
672   let tampered = tampered_app_path("truncated_pkcs7File");
673   tamper(
674     original_app_path("app_mf-1_sf-1_p7-1"),
675     tampered,
676     { "META-INF/A.RSA": truncateEntry },
677     []
678   );
679   certdb.openSignedAppFileAsync(
680     Ci.nsIX509CertDB.AppXPCShellRoot,
681     tampered,
682     check_open_result(
683       "truncated_pkcs7File",
684       Cr.NS_ERROR_CMS_VERIFY_NOT_SIGNED,
685       []
686     )
687   );
690 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
691   let tampered = tampered_app_path("unsigned_entry");
692   tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [
693     { name: "unsigned.txt", content: "unsigned content!" },
694   ]);
695   certdb.openSignedAppFileAsync(
696     Ci.nsIX509CertDB.AppXPCShellRoot,
697     tampered,
698     check_open_result(
699       "unsigned_entry",
700       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
701       []
702     )
703   );
706 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
707   let tampered = tampered_app_path("unsigned_metainf_entry");
708   tamper(original_app_path("app_mf-1_sf-1_p7-1"), tampered, {}, [
709     { name: "META-INF/unsigned.txt", content: "unsigned content!" },
710   ]);
711   certdb.openSignedAppFileAsync(
712     Ci.nsIX509CertDB.AppXPCShellRoot,
713     tampered,
714     check_open_result(
715       "unsigned_metainf_entry",
716       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
717       []
718     )
719   );
722 add_signature_test(PKCS7_WITH_SHA256, function testSHA1Disabled() {
723   certdb.openSignedAppFileAsync(
724     Ci.nsIX509CertDB.AppXPCShellRoot,
725     original_app_path("app_mf-1_sf-1_p7-1"),
726     check_open_result(
727       "SHA-1 should not be accepted if disabled by policy",
728       Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE,
729       []
730     )
731   );
734 add_signature_test(
735   PKCS7_WITH_SHA256,
736   function testSHA256WorksWithSHA1Disabled() {
737     certdb.openSignedAppFileAsync(
738       Ci.nsIX509CertDB.AppXPCShellRoot,
739       original_app_path("app_mf-256_sf-256_p7-256"),
740       check_open_result(
741         "SHA-256 should work if SHA-1 is disabled by policy",
742         Cr.NS_OK,
743         [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256]
744       )
745     );
746   }
749 add_signature_test(
750   PKCS7_WITH_SHA256,
751   function testMultipleSignaturesWorkWithSHA1Disabled() {
752     certdb.openSignedAppFileAsync(
753       Ci.nsIX509CertDB.AppXPCShellRoot,
754       original_app_path("app_mf-1-256_sf-1-256_p7-1-256"),
755       check_open_result(
756         "Multiple signatures should work if SHA-1 is " +
757           "disabled by policy (if SHA-256 signature verifies)",
758         Cr.NS_OK,
759         [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256]
760       )
761     );
762   }
765 var cosePolicies = [
766   COSEAndPKCS7WithSHA1OrSHA256,
767   COSERequiredAndPKCS7WithSHA1OrSHA256,
770 // NOTE: The zip files referenced in coseTestcasesStage and coseTestcasesProd
771 // were originally generated with
772 // https://github.com/mozilla-services/autograph/blob/c890e14de5b04dcff9be0d07fdea4ae6bbb58557/tools/autograph-client/build_test_xpis.sh
773 // Since then, the mechanism to sign these packages have changed, see
774 // https://bugzilla.mozilla.org/show_bug.cgi?id=1885457 for details.
776 var coseTestcasesStage = [
777   {
778     name: "addons-stage-tomato-clock-sha1-es256-es384",
779     expectedResult: Cr.NS_OK,
780     expectedSignatureAlgorithms: [
781       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
782       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
783     ],
784     root: Ci.nsIX509CertDB.AddonsStageRoot,
785   },
786   {
787     name: "addons-stage-tomato-clock-sha1-es256-ps256",
788     // PS256 is not yet supported.
789     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
790     expectedSignatureAlgorithms: [],
791     root: Ci.nsIX509CertDB.AddonsStageRoot,
792   },
793   {
794     name: "addons-stage-tomato-clock-sha1-es256",
795     expectedResult: Cr.NS_OK,
796     expectedSignatureAlgorithms: [
797       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
798       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
799     ],
800     root: Ci.nsIX509CertDB.AddonsStageRoot,
801   },
802   {
803     name: "addons-stage-tomato-clock-sha1-ps256",
804     // PS256 is not yet supported.
805     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
806     expectedSignatureAlgorithms: [],
807     root: Ci.nsIX509CertDB.AddonsStageRoot,
808   },
811 var coseTestcasesProd = [
812   {
813     name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-ES384",
814     expectedResult: Cr.NS_OK,
815     expectedSignatureAlgorithms: [
816       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
817       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
818     ],
819     root: Ci.nsIX509CertDB.AddonsPublicRoot,
820   },
821   {
822     name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256-PS256",
823     // PS256 is not yet supported.
824     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
825     expectedSignatureAlgorithms: [],
826     root: Ci.nsIX509CertDB.AddonsPublicRoot,
827   },
828   {
829     name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-ES256",
830     expectedResult: Cr.NS_OK,
831     expectedSignatureAlgorithms: [
832       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
833       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1,
834     ],
835     root: Ci.nsIX509CertDB.AddonsPublicRoot,
836   },
837   {
838     name: "autograph-714ba248-prod-tomato-clock-PKCS7-SHA1-PS256",
839     // PS256 is not yet supported.
840     expectedResult: Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
841     expectedSignatureAlgorithms: [],
842     root: Ci.nsIX509CertDB.AddonsPublicRoot,
843   },
846 for (let policy of cosePolicies) {
847   for (let testcase of [...coseTestcasesStage, ...coseTestcasesProd]) {
848     add_signature_test(policy, function () {
849       certdb.openSignedAppFileAsync(
850         testcase.root,
851         original_app_path(testcase.name),
852         check_open_result(
853           testcase.name,
854           testcase.expectedResult,
855           testcase.expectedSignatureAlgorithms
856         )
857       );
858     });
859   }
862 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigTampered() {
863   let tampered = tampered_app_path("cose_sig_tampered");
864   tamper(
865     original_app_path("cose_signed_with_pkcs7"),
866     tampered,
867     { "META-INF/cose.sig": truncateEntry },
868     []
869   );
870   certdb.openSignedAppFileAsync(
871     Ci.nsIX509CertDB.AppXPCShellRoot,
872     tampered,
873     check_open_result(
874       "cose_sig_tampered",
875       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
876       []
877     )
878   );
881 // PKCS7 is processed before COSE, so if a COSE signature file is removed or
882 // tampered with, this appears as a PKCS7 signature verification failure.
883 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSESigRemoved() {
884   let tampered = tampered_app_path("cose_sig_removed");
885   tamper(
886     original_app_path("cose_signed_with_pkcs7"),
887     tampered,
888     { "META-INF/cose.sig": removeEntry },
889     []
890   );
891   certdb.openSignedAppFileAsync(
892     Ci.nsIX509CertDB.AppXPCShellRoot,
893     tampered,
894     check_open_result(
895       "cose_sig_removed",
896       Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
897       []
898     )
899   );
902 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestTampered() {
903   let tampered = tampered_app_path("cose_manifest_tampered");
904   tamper(
905     original_app_path("cose_signed_with_pkcs7"),
906     tampered,
907     { "META-INF/cose.manifest": truncateEntry },
908     []
909   );
910   certdb.openSignedAppFileAsync(
911     Ci.nsIX509CertDB.AppXPCShellRoot,
912     tampered,
913     check_open_result(
914       "cose_manifest_tampered",
915       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
916       []
917     )
918   );
921 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEManifestRemoved() {
922   let tampered = tampered_app_path("cose_manifest_removed");
923   tamper(
924     original_app_path("cose_signed_with_pkcs7"),
925     tampered,
926     { "META-INF/cose.manifest": removeEntry },
927     []
928   );
929   certdb.openSignedAppFileAsync(
930     Ci.nsIX509CertDB.AppXPCShellRoot,
931     tampered,
932     check_open_result(
933       "cose_manifest_removed",
934       Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
935       []
936     )
937   );
940 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileAdded() {
941   let tampered = tampered_app_path("cose_file_added");
942   tamper(original_app_path("cose_signed_with_pkcs7"), tampered, {}, [
943     { name: "unsigned.txt", content: "unsigned content!" },
944   ]);
945   certdb.openSignedAppFileAsync(
946     Ci.nsIX509CertDB.AppXPCShellRoot,
947     tampered,
948     check_open_result(
949       "cose_file_added",
950       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
951       []
952     )
953   );
956 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileRemoved() {
957   let tampered = tampered_app_path("cose_file_removed");
958   tamper(
959     original_app_path("cose_signed_with_pkcs7"),
960     tampered,
961     { "manifest.json": removeEntry },
962     []
963   );
964   certdb.openSignedAppFileAsync(
965     Ci.nsIX509CertDB.AppXPCShellRoot,
966     tampered,
967     check_open_result(
968       "cose_file_removed",
969       Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
970       []
971     )
972   );
975 add_signature_test(COSEAndPKCS7WithSHA256, function testCOSEFileTampered() {
976   let tampered = tampered_app_path("cose_file_tampered");
977   tamper(
978     original_app_path("cose_signed_with_pkcs7"),
979     tampered,
980     { "manifest.json": truncateEntry },
981     []
982   );
983   certdb.openSignedAppFileAsync(
984     Ci.nsIX509CertDB.AppXPCShellRoot,
985     tampered,
986     check_open_result(
987       "cose_file_tampered",
988       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
989       []
990     )
991   );
994 add_signature_test(COSEOnly, function testOnlyCOSESigTampered() {
995   let tampered = tampered_app_path("only_cose_sig_tampered");
996   tamper(
997     original_app_path("only_cose_signed"),
998     tampered,
999     { "META-INF/cose.sig": truncateEntry },
1000     []
1001   );
1002   certdb.openSignedAppFileAsync(
1003     Ci.nsIX509CertDB.AppXPCShellRoot,
1004     tampered,
1005     check_open_result(
1006       "only_cose_sig_tampered",
1007       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
1008       []
1009     )
1010   );
1013 add_signature_test(COSEOnly, function testOnlyCOSESigRemoved() {
1014   let tampered = tampered_app_path("only_cose_sig_removed");
1015   tamper(
1016     original_app_path("only_cose_signed"),
1017     tampered,
1018     { "META-INF/cose.sig": removeEntry },
1019     []
1020   );
1021   certdb.openSignedAppFileAsync(
1022     Ci.nsIX509CertDB.AppXPCShellRoot,
1023     tampered,
1024     check_open_result(
1025       "only_cose_sig_removed",
1026       Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE,
1027       []
1028     )
1029   );
1032 add_signature_test(COSEOnly, function testOnlyCOSEManifestTampered() {
1033   let tampered = tampered_app_path("only_cose_manifest_tampered");
1034   tamper(
1035     original_app_path("only_cose_signed"),
1036     tampered,
1037     { "META-INF/cose.manifest": truncateEntry },
1038     []
1039   );
1040   certdb.openSignedAppFileAsync(
1041     Ci.nsIX509CertDB.AppXPCShellRoot,
1042     tampered,
1043     check_open_result(
1044       "only_cose_manifest_tampered",
1045       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID,
1046       []
1047     )
1048   );
1051 add_signature_test(COSEOnly, function testOnlyCOSEManifestRemoved() {
1052   let tampered = tampered_app_path("only_cose_manifest_removed");
1053   tamper(
1054     original_app_path("only_cose_signed"),
1055     tampered,
1056     { "META-INF/cose.manifest": removeEntry },
1057     []
1058   );
1059   certdb.openSignedAppFileAsync(
1060     Ci.nsIX509CertDB.AppXPCShellRoot,
1061     tampered,
1062     check_open_result(
1063       "only_cose_manifest_removed",
1064       Cr.NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE,
1065       []
1066     )
1067   );
1070 add_signature_test(COSEOnly, function testOnlyCOSEFileAdded() {
1071   let tampered = tampered_app_path("only_cose_file_added");
1072   tamper(original_app_path("only_cose_signed"), tampered, {}, [
1073     { name: "unsigned.txt", content: "unsigned content!" },
1074   ]);
1075   certdb.openSignedAppFileAsync(
1076     Ci.nsIX509CertDB.AppXPCShellRoot,
1077     tampered,
1078     check_open_result(
1079       "only_cose_file_added",
1080       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY,
1081       []
1082     )
1083   );
1086 add_signature_test(COSEOnly, function testOnlyCOSEFileRemoved() {
1087   let tampered = tampered_app_path("only_cose_file_removed");
1088   tamper(
1089     original_app_path("only_cose_signed"),
1090     tampered,
1091     { "manifest.json": removeEntry },
1092     []
1093   );
1094   certdb.openSignedAppFileAsync(
1095     Ci.nsIX509CertDB.AppXPCShellRoot,
1096     tampered,
1097     check_open_result(
1098       "only_cose_file_removed",
1099       Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING,
1100       []
1101     )
1102   );
1105 add_signature_test(COSEOnly, function testOnlyCOSEFileTampered() {
1106   let tampered = tampered_app_path("only_cose_file_tampered");
1107   tamper(
1108     original_app_path("only_cose_signed"),
1109     tampered,
1110     { "manifest.json": truncateEntry },
1111     []
1112   );
1113   certdb.openSignedAppFileAsync(
1114     Ci.nsIX509CertDB.AppXPCShellRoot,
1115     tampered,
1116     check_open_result(
1117       "only_cose_file_tampered",
1118       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
1119       []
1120     )
1121   );
1124 // This was signed with only COSE first, and then the contents were tampered
1125 // with (making the signature invalid). Then, the file was signed with
1126 // PKCS7/SHA1. We need to ensure that if we're configured to process COSE, this
1127 // verification fails.
1128 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
1129   certdb.openSignedAppFileAsync(
1130     Ci.nsIX509CertDB.AppXPCShellRoot,
1131     original_app_path("cose_tampered_good_pkcs7"),
1132     check_open_result(
1133       "tampered COSE with good PKCS7 signature should fail " +
1134         "when COSE and PKCS7 is processed",
1135       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
1136       []
1137     )
1138   );
1141 add_signature_test(COSEOnly, function () {
1142   certdb.openSignedAppFileAsync(
1143     Ci.nsIX509CertDB.AppXPCShellRoot,
1144     original_app_path("cose_tampered_good_pkcs7"),
1145     check_open_result(
1146       "tampered COSE with good PKCS7 signature should fail " +
1147         "when only COSE is processed",
1148       Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY,
1149       []
1150     )
1151   );
1154 // If we're not processing COSE, this should verify successfully.
1155 add_signature_test(PKCS7WithSHA1OrSHA256, function () {
1156   certdb.openSignedAppFileAsync(
1157     Ci.nsIX509CertDB.AppXPCShellRoot,
1158     original_app_path("cose_tampered_good_pkcs7"),
1159     check_open_result(
1160       "tampered COSE with good PKCS7 signature should succeed " +
1161         "when COSE is not processed",
1162       Cr.NS_OK,
1163       [Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA1]
1164     )
1165   );
1168 add_test(function () {
1169   certdb.openSignedAppFileAsync(
1170     Ci.nsIX509CertDB.AppXPCShellRoot,
1171     original_app_path("bug_1411458"),
1172     check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO, [])
1173   );
1176 // This has a big manifest file (~2MB). It should verify correctly.
1177 add_test(function () {
1178   certdb.openSignedAppFileAsync(
1179     Ci.nsIX509CertDB.AppXPCShellRoot,
1180     original_app_path("big_manifest"),
1181     check_open_result("add-on with big manifest file", Cr.NS_OK, [
1182       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
1183     ])
1184   );
1187 // This has a huge manifest file (~10MB). Manifest files this large are not
1188 // supported (8MB is the limit). It should not verify correctly.
1189 add_test(function () {
1190   certdb.openSignedAppFileAsync(
1191     Ci.nsIX509CertDB.AppXPCShellRoot,
1192     original_app_path("huge_manifest"),
1193     check_open_result(
1194       "add-on with huge manifest file",
1195       Cr.NS_ERROR_SIGNED_JAR_ENTRY_INVALID,
1196       []
1197     )
1198   );
1201 // Verification should pass despite a not-yet-valid EE certificate.
1202 // Regression test for bug 1713628
1203 add_test(function () {
1204   certdb.openSignedAppFileAsync(
1205     Ci.nsIX509CertDB.AppXPCShellRoot,
1206     original_app_path("validity_not_yet_valid"),
1207     check_open_result("validity_not_yet_valid", Cr.NS_OK, [
1208       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
1209     ])
1210   );
1213 // Verification should pass despite an expired EE certificate.
1214 // Regression test for bug 1267318 and bug 1548973
1215 add_test(function () {
1216   certdb.openSignedAppFileAsync(
1217     Ci.nsIX509CertDB.AppXPCShellRoot,
1218     original_app_path("validity_expired"),
1219     check_open_result("validity_expired", Cr.NS_OK, [
1220       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
1221     ])
1222   );
1225 add_signature_test(COSEAndPKCS7WithSHA1OrSHA256, function () {
1226   certdb.openSignedAppFileAsync(
1227     Ci.nsIX509CertDB.AppXPCShellRoot,
1228     original_app_path("alternate-root"),
1229     check_open_result("alternate-root", Cr.NS_OK, [
1230       Ci.nsIAppSignatureInfo.COSE_WITH_SHA256,
1231       Ci.nsIAppSignatureInfo.PKCS7_WITH_SHA256,
1232     ])
1233   );
1236 // TODO: tampered MF, tampered SF
1237 // TODO: too-large MF, too-large RSA, too-large SF
1238 // TODO: MF and SF that end immediately after the last main header
1239 //       (no CR nor LF)
1240 // TODO: broken headers to exercise the parser