1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // This Source Code Form is subject to the terms of the Mozilla Public
3 // License, v. 2.0. If a copy of the MPL was not distributed with this
4 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 // Tests that certificates cannot be tampered with without being detected.
9 // Tests a combination of cases: RSA signatures, ECDSA signatures, certificate
10 // chains where the intermediate has been tampered with, chains where the
11 // end-entity has been tampered, tampering of the signature, and tampering in
12 // the rest of the certificate.
14 do_get_profile(); // must be called before getting nsIX509CertDB
15 var certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
19 // Reads a PEM-encoded certificate, modifies the nth byte (0-indexed), and
20 // returns the base64-encoded bytes of the certificate. Negative indices may be
21 // specified to modify a byte from the end of the certificate.
22 function readAndTamperWithNthByte(certificatePath, n) {
23 let pem = readFile(do_get_file(certificatePath, false));
24 let der = atob(pemToBase64(pem));
26 // remember, n is negative at this point
29 let replacement = "\x22";
30 if (der.charCodeAt(n) == replacement) {
33 der = der.substring(0, n) + replacement + der.substring(n + 1);
37 // The signature on certificates appears last. This should modify the contents
38 // of the signature such that it no longer validates correctly while still
39 // resulting in a structurally valid certificate.
40 const BYTE_IN_SIGNATURE = -8;
41 function addSignatureTamperedCertificate(certificatePath) {
42 let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
43 certdb.addCertFromBase64(base64, ",,");
46 function ensureSignatureVerificationFailure(certificatePath) {
47 let cert = constructCertFromFile(certificatePath);
48 return checkCertErrorGeneric(
51 SEC_ERROR_BAD_SIGNATURE,
52 certificateUsageSSLServer
56 function tamperWithSignatureAndEnsureVerificationFailure(certificatePath) {
57 let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
58 let cert = certdb.constructX509FromBase64(base64);
59 return checkCertErrorGeneric(
62 SEC_ERROR_BAD_SIGNATURE,
63 certificateUsageSSLServer
67 // The beginning of a certificate looks like this (in hex, using DER):
68 // 30 XX XX XX [the XX encode length - there are probably 3 bytes here]
69 // 30 XX XX XX [length again]
73 // 02 XX [length again - 1 byte as long as we're using pycert]
74 // XX XX ... [serial number - 20 bytes as long as we're using pycert]
75 // Since we want to modify the serial number, we need to change something from
76 // byte 15 to byte 34 (0-indexed). If it turns out that the two length sections
77 // we assumed were 3 bytes are shorter (they can't be longer), modifying
78 // something from byte 15 to byte 30 will still get us what we want. Since the
79 // serial number is a DER INTEGER and because it must be positive, it's best to
80 // skip the first two bytes of the serial number so as to not run into any
81 // issues there. Thus byte 17 is a good byte to modify.
82 const BYTE_IN_SERIAL_NUMBER = 17;
83 function addSerialNumberTamperedCertificate(certificatePath) {
84 let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SERIAL_NUMBER);
85 certdb.addCertFromBase64(base64, ",,");
88 function tamperWithSerialNumberAndEnsureVerificationFailure(certificatePath) {
89 let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SERIAL_NUMBER);
90 let cert = certdb.constructX509FromBase64(base64);
91 return checkCertErrorGeneric(
94 SEC_ERROR_BAD_SIGNATURE,
95 certificateUsageSSLServer
99 add_task(async function () {
100 addCertFromFile(certdb, "test_cert_signatures/ca-rsa.pem", "CTu,,");
101 addCertFromFile(certdb, "test_cert_signatures/ca-secp384r1.pem", "CTu,,");
103 // Tamper with the signatures on intermediate certificates and ensure that
104 // end-entity certificates issued by those intermediates do not validate
106 addSignatureTamperedCertificate("test_cert_signatures/int-rsa.pem");
107 addSignatureTamperedCertificate("test_cert_signatures/int-secp384r1.pem");
108 await ensureSignatureVerificationFailure("test_cert_signatures/ee-rsa.pem");
109 await ensureSignatureVerificationFailure(
110 "test_cert_signatures/ee-secp384r1.pem"
113 // Tamper with the signatures on end-entity certificates and ensure that they
114 // do not validate successfully.
115 await tamperWithSignatureAndEnsureVerificationFailure(
116 "test_cert_signatures/ee-rsa-direct.pem"
118 await tamperWithSignatureAndEnsureVerificationFailure(
119 "test_cert_signatures/ee-secp384r1-direct.pem"
122 // Tamper with the serial numbers of intermediate certificates and ensure
123 // that end-entity certificates issued by those intermediates do not validate
125 addSerialNumberTamperedCertificate("test_cert_signatures/int-rsa.pem");
126 addSerialNumberTamperedCertificate("test_cert_signatures/int-secp384r1.pem");
127 await ensureSignatureVerificationFailure("test_cert_signatures/ee-rsa.pem");
128 await ensureSignatureVerificationFailure(
129 "test_cert_signatures/ee-secp384r1.pem"
132 // Tamper with the serial numbers of end-entity certificates and ensure that
133 // they do not validate successfully.
134 await tamperWithSerialNumberAndEnsureVerificationFailure(
135 "test_cert_signatures/ee-rsa-direct.pem"
137 await tamperWithSerialNumberAndEnsureVerificationFailure(
138 "test_cert_signatures/ee-secp384r1-direct.pem"