tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_cert_signatures.js (5737B)


      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/.
      5 
      6 "use strict";
      7 
      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.
     13 
     14 do_get_profile(); // must be called before getting nsIX509CertDB
     15 var certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
     16  Ci.nsIX509CertDB
     17 );
     18 
     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));
     25  if (n < 0) {
     26    // remember, n is negative at this point
     27    n = der.length + n;
     28  }
     29  let replacement = "\x22";
     30  if (der.charCodeAt(n) == replacement) {
     31    replacement = "\x23";
     32  }
     33  der = der.substring(0, n) + replacement + der.substring(n + 1);
     34  return btoa(der);
     35 }
     36 
     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, ",,");
     44 }
     45 
     46 function ensureSignatureVerificationFailure(certificatePath) {
     47  let cert = constructCertFromFile(certificatePath);
     48  return checkCertErrorGeneric(
     49    certdb,
     50    cert,
     51    SEC_ERROR_BAD_SIGNATURE,
     52    Ci.nsIX509CertDB.verifyUsageTLSServer
     53  );
     54 }
     55 
     56 function tamperWithSignatureAndEnsureVerificationFailure(certificatePath) {
     57  let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SIGNATURE);
     58  let cert = certdb.constructX509FromBase64(base64);
     59  return checkCertErrorGeneric(
     60    certdb,
     61    cert,
     62    SEC_ERROR_BAD_SIGNATURE,
     63    Ci.nsIX509CertDB.verifyUsageTLSServer
     64  );
     65 }
     66 
     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]
     70 //       A0 03
     71 //          02 01
     72 //             02
     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, ",,");
     86 }
     87 
     88 function tamperWithSerialNumberAndEnsureVerificationFailure(certificatePath) {
     89  let base64 = readAndTamperWithNthByte(certificatePath, BYTE_IN_SERIAL_NUMBER);
     90  let cert = certdb.constructX509FromBase64(base64);
     91  return checkCertErrorGeneric(
     92    certdb,
     93    cert,
     94    SEC_ERROR_BAD_SIGNATURE,
     95    Ci.nsIX509CertDB.verifyUsageTLSServer
     96  );
     97 }
     98 
     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,,");
    102 
    103  // Tamper with the signatures on intermediate certificates and ensure that
    104  // end-entity certificates issued by those intermediates do not validate
    105  // successfully.
    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"
    111  );
    112 
    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"
    117  );
    118  await tamperWithSignatureAndEnsureVerificationFailure(
    119    "test_cert_signatures/ee-secp384r1-direct.pem"
    120  );
    121 
    122  // Tamper with the serial numbers of intermediate certificates and ensure
    123  // that end-entity certificates issued by those intermediates do not validate
    124  // successfully.
    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"
    130  );
    131 
    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"
    136  );
    137  await tamperWithSerialNumberAndEnsureVerificationFailure(
    138    "test_cert_signatures/ee-secp384r1-direct.pem"
    139  );
    140 });