diff --git a/security/manager/ssl/RemoteSecuritySettings.jsm b/security/manager/ssl/RemoteSecuritySettings.jsm index b1c7ea788ae1..f1483acea86f 100644 --- a/security/manager/ssl/RemoteSecuritySettings.jsm +++ b/security/manager/ssl/RemoteSecuritySettings.jsm @@ -80,17 +80,6 @@ function bytesToString(bytes) { return String.fromCharCode.apply(null, bytes); } -class CRLiteState { - constructor(subject, spkiHash, state) { - this.subject = subject; - this.spkiHash = spkiHash; - this.state = state; - } -} -CRLiteState.prototype.QueryInterface = ChromeUtils.generateQI([ - "nsICRLiteState", -]); - class CRLiteCoverage { constructor(b64LogID, minTimestamp, maxTimestamp) { this.b64LogID = b64LogID; @@ -437,40 +426,6 @@ class IntermediatePreloads { log.debug(`Removing ${deleted.length} Intermediate certificates`); await this.removeCerts(deleted); - let hasPriorCRLiteData = await hasPriorData( - Ci.nsICertStorage.DATA_TYPE_CRLITE - ); - if (!hasPriorCRLiteData) { - deleted = []; - updated = []; - created = current; - } - const toAdd = created.concat(updated.map(u => u.new)); - let entries = []; - for (let entry of deleted) { - entries.push( - new CRLiteState( - entry.subjectDN, - entry.pubKeyHash, - Ci.nsICertStorage.STATE_UNSET - ) - ); - } - for (let entry of toAdd) { - entries.push( - new CRLiteState( - entry.subjectDN, - entry.pubKeyHash, - entry.crlite_enrolled - ? Ci.nsICertStorage.STATE_ENFORCE - : Ci.nsICertStorage.STATE_UNSET - ) - ); - } - let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService( - Ci.nsICertStorage - ); - await new Promise(resolve => certStorage.setCRLiteState(entries, resolve)); } /** @@ -687,9 +642,10 @@ class CRLiteFilters { ); } } + let enrollment = filter.enrolledIssuers ? filter.enrolledIssuers : []; await new Promise(resolve => { - certList.setFullCRLiteFilter(filter.bytes, coverage, rv => { + certList.setFullCRLiteFilter(filter.bytes, enrollment, coverage, rv => { log.debug(`setFullCRLiteFilter: ${rv}`); resolve(); }); diff --git a/security/manager/ssl/cert_storage/src/lib.rs b/security/manager/ssl/cert_storage/src/lib.rs index bf2669aea0a8..69e7b54a278a 100644 --- a/security/manager/ssl/cert_storage/src/lib.rs +++ b/security/manager/ssl/cert_storage/src/lib.rs @@ -59,21 +59,24 @@ use std::time::{Duration, SystemTime}; use storage_variant::VariantType; use thin_vec::ThinVec; use xpcom::interfaces::{ - nsICRLiteCoverage, nsICRLiteState, nsICRLiteTimestamp, nsICertInfo, nsICertStorage, - nsICertStorageCallback, nsIFile, nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, - nsIMemoryReporter, nsIMemoryReporterManager, nsIObserver, nsIPrefBranch, nsIRevocationState, - nsISerialEventTarget, nsISubjectAndPubKeyRevocationState, nsISupports, + nsICRLiteCoverage, nsICRLiteTimestamp, nsICertInfo, nsICertStorage, nsICertStorageCallback, + nsIFile, nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, nsIMemoryReporter, + nsIMemoryReporterManager, nsIObserver, nsIPrefBranch, nsIRevocationState, nsISerialEventTarget, + nsISubjectAndPubKeyRevocationState, nsISupports, }; use xpcom::{nsIID, GetterAddrefs, RefPtr, ThreadBoundRefPtr, XpCom}; const PREFIX_REV_IS: &str = "is"; const PREFIX_REV_SPK: &str = "spk"; -const PREFIX_CRLITE: &str = "crlite"; const PREFIX_SUBJECT: &str = "subject"; const PREFIX_CERT: &str = "cert"; const PREFIX_DATA_TYPE: &str = "datatype"; const COVERAGE_SERIALIZATION_VERSION: u8 = 1; +const COVERAGE_V1_ENTRY_BYTES: usize = 48; + +const ENROLLMENT_SERIALIZATION_VERSION: u8 = 1; +const ENROLLMENT_V1_ENTRY_BYTES: usize = 32; type Rkv = rkv::Rkv; type SingleStore = rkv::SingleStore; @@ -157,6 +160,8 @@ struct SecurityState { crlite_stash: Option, HashSet>>>, /// Maps an RFC 6962 LogID to a pair of 64 bit unix timestamps crlite_coverage: Option, (u64, u64)>>, + /// Set of `SHA256(subject || spki)` values for enrolled issuers + crlite_enrollment: Option>>, /// Tracks the number of asynchronous operations which have been dispatched but not completed. remaining_ops: i32, } @@ -172,6 +177,7 @@ impl SecurityState { crlite_filter: None, crlite_stash: None, crlite_coverage: None, + crlite_enrollment: None, remaining_ops: 0, }) } @@ -302,7 +308,9 @@ impl SecurityState { pub fn get_has_prior_data(&self, data_type: u8) -> Result { if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_FULL { - return Ok(self.crlite_filter.is_some() && self.crlite_coverage.is_some()); + return Ok(self.crlite_filter.is_some() + && self.crlite_coverage.is_some() + && self.crlite_enrollment.is_some()); } if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_INCREMENTAL { return Ok(self.crlite_stash.is_some()); @@ -401,24 +409,18 @@ impl SecurityState { } } - pub fn get_crlite_state( - &self, - subject: &[u8], - pub_key: &[u8], - ) -> Result { - let mut digest = Sha256::default(); - digest.input(pub_key); - let pub_key_hash = digest.result(); - - let subject_pubkey = make_key!(PREFIX_CRLITE, subject, &pub_key_hash); - match self.read_entry(&subject_pubkey) { - Ok(Some(value)) => Ok(value), - Ok(None) => Ok(nsICertStorage::STATE_UNSET), - Err(_) => Err(SecurityStateError::from("problem reading crlite state")), + fn issuer_is_enrolled(&self, subject: &[u8], pub_key: &[u8]) -> bool { + if let Some(crlite_enrollment) = self.crlite_enrollment.as_ref() { + let mut digest = Sha256::default(); + digest.input(subject); + digest.input(pub_key); + let issuer_id = digest.result(); + return crlite_enrollment.contains(&issuer_id.to_vec()); } + return false; } - pub fn filter_covers_some_timestamp(&self, timestamps: &[CRLiteTimestamp]) -> bool { + fn filter_covers_some_timestamp(&self, timestamps: &[CRLiteTimestamp]) -> bool { if let Some(crlite_coverage) = self.crlite_coverage.as_ref() { for entry in timestamps { if let Some(&(low, high)) = crlite_coverage.get(entry.log_id.as_ref()) { @@ -434,6 +436,7 @@ impl SecurityState { pub fn set_full_crlite_filter( &mut self, filter: Vec, + enrolled_issuers: Vec, coverage_entries: &[(nsCString, u64, u64)], ) -> Result<(), SecurityStateError> { // First drop any existing crlite filter and clear the accumulated stash. @@ -441,6 +444,7 @@ impl SecurityState { let _ = self.crlite_filter.take(); let _ = self.crlite_stash.take(); let _ = self.crlite_coverage.take(); + let _ = self.crlite_enrollment.take(); let mut path = get_store_path(&self.profile_path)?; path.push("crlite.stash"); // Truncate the stash file if it exists. @@ -461,9 +465,8 @@ impl SecurityState { // Serialize the coverage metadata as a 1 byte version number followed by any number of 48 // byte entries. Each entry is a 32 byte (opaque) log id, followed by two 8 byte // timestamps. Each timestamp is an 8 byte unsigned integer in little endian. - let mut coverage_bytes = Vec::with_capacity( - size_of::() + coverage_entries.len() * (32 + size_of::() + size_of::()), - ); + let mut coverage_bytes = + Vec::with_capacity(size_of::() + coverage_entries.len() * COVERAGE_V1_ENTRY_BYTES); coverage_bytes.push(COVERAGE_SERIALIZATION_VERSION); for (b64_log_id, min_t, max_t) in coverage_entries { let log_id = match base64::decode(&b64_log_id) { @@ -484,8 +487,32 @@ impl SecurityState { let mut coverage_file = File::create(&path)?; coverage_file.write_all(&coverage_bytes)?; } - self.load_crlite_filter()?; + // Serialize the enrollment list as a 1 byte version number followed by: + // Version 1: any number of 32 byte values of the form `SHA256(subject || spki)`. + let mut enrollment_bytes = Vec::with_capacity( + size_of::() + enrolled_issuers.len() * ENROLLMENT_V1_ENTRY_BYTES, + ); + enrollment_bytes.push(ENROLLMENT_SERIALIZATION_VERSION); + for b64_issuer_id in enrolled_issuers { + let issuer_id = match base64::decode(&b64_issuer_id) { + Ok(issuer_id) if issuer_id.len() == 32 => issuer_id, + _ => { + warn!("malformed issuer ID - skipping: {}", b64_issuer_id); + continue; + } + }; + enrollment_bytes.extend_from_slice(&issuer_id); + } + // Write the enrollment file for the new filter + let mut path = get_store_path(&self.profile_path)?; + path.push("crlite.enrollment"); + { + let mut enrollment_file = File::create(&path)?; + enrollment_file.write_all(&enrollment_bytes)?; + } + + self.load_crlite_filter()?; Ok(()) } @@ -521,53 +548,78 @@ impl SecurityState { // Deserialize the coverage metadata. // The format is described in `set_full_crlite_filter`. let coverage_file = File::open(path)?; + let coverage_file_len = coverage_file.metadata()?.len() as usize; let mut coverage_reader = BufReader::new(coverage_file); match coverage_reader.read_u8() { Ok(COVERAGE_SERIALIZATION_VERSION) => (), - _ => { - return Err(SecurityStateError::from( - "unable to initialize CRLite coverage", - )) - } + _ => return Err(SecurityStateError::from("unknown CRLite coverage version")), } + if (coverage_file_len - 1) % COVERAGE_V1_ENTRY_BYTES != 0 { + return Err(SecurityStateError::from("truncated CRLite coverage file")); + } + let coverage_count = (coverage_file_len - 1) / COVERAGE_V1_ENTRY_BYTES; let mut crlite_coverage: HashMap, (u64, u64)> = HashMap::new(); - loop { - let mut coverage_entry = [0u8; 48]; - match coverage_reader.read(&mut coverage_entry) { - Ok(48) => (), - Ok(0) => break, // end of file - _ => { - return Err(SecurityStateError::from( - "unable to initialize CRLite coverage", - )) - } + for _ in 0..coverage_count { + let mut coverage_entry = [0u8; COVERAGE_V1_ENTRY_BYTES]; + match coverage_reader.read_exact(&mut coverage_entry) { + Ok(()) => (), + _ => return Err(SecurityStateError::from("truncated CRLite coverage file")), }; let log_id = &coverage_entry[0..32]; let min_timestamp: u64; let max_timestamp: u64; match (&coverage_entry[32..40]).read_u64::() { Ok(value) => min_timestamp = value, - _ => { - return Err(SecurityStateError::from( - "unable to initialize CRLite coverage", - )) - } + _ => return Err(SecurityStateError::from("truncated CRLite coverage file")), } match (&coverage_entry[40..48]).read_u64::() { Ok(value) => max_timestamp = value, - _ => { - return Err(SecurityStateError::from( - "unable to initialize CRLite coverage", - )) - } + _ => return Err(SecurityStateError::from("truncated CRLite coverage file")), } crlite_coverage.insert(log_id.to_vec(), (min_timestamp, max_timestamp)); } + let mut path = get_store_path(&self.profile_path)?; + path.push("crlite.enrollment"); + if !path.exists() { + return Ok(()); + } + + // Deserialize the enrollment metadata. + // The format is described in `set_full_crlite_filter`. + let enrollment_file = File::open(path)?; + let enrollment_file_len = enrollment_file.metadata()?.len() as usize; + let mut enrollment_reader = BufReader::new(enrollment_file); + match enrollment_reader.read_u8() { + Ok(ENROLLMENT_SERIALIZATION_VERSION) => (), + _ => { + return Err(SecurityStateError::from( + "unknown CRLite enrollment version", + )) + } + } + if (enrollment_file_len - 1) % ENROLLMENT_V1_ENTRY_BYTES != 0 { + return Err(SecurityStateError::from("truncated CRLite enrollment file")); + } + let enrollment_count = (enrollment_file_len - 1) / ENROLLMENT_V1_ENTRY_BYTES; + let mut crlite_enrollment: HashSet> = HashSet::new(); + for _ in 0..enrollment_count { + let mut enrollment_entry = [0u8; ENROLLMENT_V1_ENTRY_BYTES]; + match enrollment_reader.read_exact(&mut enrollment_entry) { + Ok(()) => (), + _ => return Err(SecurityStateError::from("truncated CRLite enrollment file")), + }; + let issuer_id = &enrollment_entry[..]; + crlite_enrollment.insert(issuer_id.to_vec()); + } + let old_crlite_filter_should_be_none = self.crlite_filter.replace(crlite_filter); assert!(old_crlite_filter_should_be_none.is_none()); let old_crlite_coverage_should_be_none = self.crlite_coverage.replace(crlite_coverage); assert!(old_crlite_coverage_should_be_none.is_none()); + let old_crlite_enrollment_should_be_none = + self.crlite_enrollment.replace(crlite_enrollment); + assert!(old_crlite_enrollment_should_be_none.is_none()); Ok(()) } @@ -607,13 +659,12 @@ impl SecurityState { issuer_spki: &[u8], serial_number: &[u8], timestamps: &[CRLiteTimestamp], - ) -> Result { - let enrollment_state = self.get_crlite_state(issuer, issuer_spki)?; - if enrollment_state != nsICertStorage::STATE_ENFORCE { - return Ok(nsICertStorage::STATE_NOT_ENROLLED); + ) -> i16 { + if !self.issuer_is_enrolled(issuer, issuer_spki) { + return nsICertStorage::STATE_NOT_ENROLLED; } if !self.filter_covers_some_timestamp(timestamps) { - return Ok(nsICertStorage::STATE_NOT_COVERED); + return nsICertStorage::STATE_NOT_COVERED; } let mut digest = Sha256::default(); digest.input(issuer_spki); @@ -624,11 +675,11 @@ impl SecurityState { Some(crlite_filter) => crlite_filter.rent(|filter| filter.has(&lookup_key)), // This can only happen if the backing file was deleted or if it or our database has // become corrupted. In any case, we have no information. - None => return Ok(nsICertStorage::STATE_NOT_COVERED), + None => return nsICertStorage::STATE_NOT_COVERED, }; match result { - true => Ok(nsICertStorage::STATE_ENFORCE), - false => Ok(nsICertStorage::STATE_UNSET), + true => nsICertStorage::STATE_ENFORCE, + false => nsICertStorage::STATE_UNSET, } } @@ -1649,87 +1700,26 @@ impl CertStorage { NS_OK } - unsafe fn SetCRLiteState( - &self, - crlite_state: *const ThinVec>, - callback: *const nsICertStorageCallback, - ) -> nserror::nsresult { - if !is_main_thread() { - return NS_ERROR_NOT_SAME_THREAD; - } - if crlite_state.is_null() || callback.is_null() { - return NS_ERROR_NULL_POINTER; - } - - let crlite_state = &*crlite_state; - let mut crlite_entries = Vec::with_capacity(crlite_state.len()); - - // By continuing when an nsICRLiteState attribute value is invalid, we prevent errors - // relating to individual entries from causing sync to fail. - for crlite_entry in crlite_state { - let mut state: i16 = 0; - try_ns!(crlite_entry.GetState(&mut state).to_result(), or continue); - - let mut subject = nsCString::new(); - try_ns!(crlite_entry.GetSubject(&mut *subject).to_result(), or continue); - - let mut pub_key_hash = nsCString::new(); - try_ns!(crlite_entry.GetSpkiHash(&mut *pub_key_hash).to_result(), or continue); - - crlite_entries.push(EncodedSecurityState::new( - PREFIX_CRLITE, - subject, - pub_key_hash, - state, - )); - } - - let task = Box::new(try_ns!(SecurityStateTask::new( - &*callback, - &self.security_state, - move |ss| ss.set_batch_state(&crlite_entries, nsICertStorage::DATA_TYPE_CRLITE), - ))); - let runnable = try_ns!(TaskRunnable::new("SetCRLiteState", task)); - try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce())); - NS_OK - } - - unsafe fn GetCRLiteState( - &self, - subject: *const ThinVec, - pub_key: *const ThinVec, - state: *mut i16, - ) -> nserror::nsresult { - // TODO (bug 1541212): We really want to restrict this to non-main-threads only, but we - // can't do so until bug 1406854 is fixed. - if subject.is_null() || pub_key.is_null() { - return NS_ERROR_NULL_POINTER; - } - *state = nsICertStorage::STATE_UNSET; - let ss = get_security_state!(self); - match ss.get_crlite_state(&*subject, &*pub_key) { - Ok(st) => { - *state = st; - NS_OK - } - _ => NS_ERROR_FAILURE, - } - } - unsafe fn SetFullCRLiteFilter( &self, filter: *const ThinVec, + enrolled_issuers: *const ThinVec, coverage: *const ThinVec>, callback: *const nsICertStorageCallback, ) -> nserror::nsresult { if !is_main_thread() { return NS_ERROR_NOT_SAME_THREAD; } - if filter.is_null() || coverage.is_null() || callback.is_null() { + if filter.is_null() + || coverage.is_null() + || callback.is_null() + || enrolled_issuers.is_null() + { return NS_ERROR_NULL_POINTER; } let filter_owned = (*filter).to_vec(); + let enrolled_issuers_owned = (*enrolled_issuers).to_vec(); let coverage = &*coverage; let mut coverage_entries = Vec::with_capacity(coverage.len()); @@ -1746,7 +1736,11 @@ impl CertStorage { let task = Box::new(try_ns!(SecurityStateTask::new( &*callback, &self.security_state, - move |ss| ss.set_full_crlite_filter(filter_owned, &coverage_entries), + move |ss| ss.set_full_crlite_filter( + filter_owned, + enrolled_issuers_owned, + &coverage_entries + ), ))); let runnable = try_ns!(TaskRunnable::new("SetFullCRLiteFilter", task)); try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce())); @@ -1819,20 +1813,14 @@ impl CertStorage { try_ns!(timestamp_entry.GetTimestamp(&mut timestamp).to_result(), or continue); timestamp_entries.push(CRLiteTimestamp { log_id, timestamp }); } - *state = nsICertStorage::STATE_UNSET; let ss = get_security_state!(self); - match ss.get_crlite_revocation_state( + *state = ss.get_crlite_revocation_state( &*issuer, &*issuerSPKI, &*serialNumber, ×tamp_entries, - ) { - Ok(st) => { - *state = st; - NS_OK - } - _ => NS_ERROR_FAILURE, - } + ); + NS_OK } unsafe fn AddCerts( diff --git a/security/manager/ssl/nsICertStorage.idl b/security/manager/ssl/nsICertStorage.idl index 3b24748ae833..71af83ccef44 100644 --- a/security/manager/ssl/nsICertStorage.idl +++ b/security/manager/ssl/nsICertStorage.idl @@ -55,21 +55,6 @@ interface nsISubjectAndPubKeyRevocationState : nsIRevocationState { readonly attribute ACString pubKey; }; -/** - * An interface representing the CRLite enrollment state of a certificate - * identified by its subject and subject public key info hash. - * subject is a base 64-encoded DER subject distinguished name. - * spkiHash is a base 64-encoded SHA-256 hash of a DER subject public key info. - * state is nsICertStorage.STATE_ENFORCE or STATE_UNSET, meaning the certificate - * is or is not enrolled in CRLite, respectively. - */ -[scriptable, uuid(5d0d22be-185f-4cf0-b73b-c5a911273e77)] -interface nsICRLiteState : nsISupports { - readonly attribute ACString subject; - readonly attribute ACString spkiHash; - readonly attribute short state; -}; - /** * An interface representing a set of certificates that are covered by a CRLite * filter. The set is represented by a certificate transparency log ID and a @@ -174,30 +159,15 @@ interface nsICertStorage : nsISupports { boolean isBlocklistFresh(); /** - * Asynchronously set a batch of CRLite enrollment state. See the - * documentation for nsICRLiteState. - * Must only be called from the main thread. - */ - [must_use] - void setCRLiteState(in Array crliteState, - in nsICertStorageCallback callback); - - /** - * Get the CRLite enrollment state of a certificate identified by the given - * subject distinguished name and subject public key info (both as DER bytes). - * STATE_ENFORCE indicates the certificate is enrolled, whereas STATE_UNSET - * indicates it is not. - */ - [must_use] - short getCRLiteState(in Array subject, in Array spki); - - /** - * Given the contents of a new CRLite filter and a description of the new - * filter's coverage, replaces any existing filter with the new one. Also - * clears any previously-set incremental revocation updates ("stashes"). + * Given the contents of a new CRLite filter, a list containing + * `base64(sha256(subject DN || subject SPKI))` for each enrolled issuer, and + * the filter's timestamp coverage, replaces any existing filter with the new + * one. Also clears any previously-set incremental revocation updates + * ("stashes"). */ [must_use] void setFullCRLiteFilter(in Array filter, + in Array enrolledIssuers, in Array coverage, in nsICertStorageCallback callback); diff --git a/security/manager/ssl/tests/unit/corrupted_crlite_helper.js b/security/manager/ssl/tests/unit/corrupted_crlite_helper.js index f0ff13226c41..2587c5dad9e3 100644 --- a/security/manager/ssl/tests/unit/corrupted_crlite_helper.js +++ b/security/manager/ssl/tests/unit/corrupted_crlite_helper.js @@ -8,7 +8,7 @@ // // Usage: // Define nsILocalFile variables for the `crlite.filter`, `crlite.coverage`, -// and `data.safe.bin` files that should be copied to the new profile, and +// and `crlite.enrollment` files that should be copied to the new profile, and // then load this file. The variables should be called `filter`, `coverage`, // and `enrollment`, respectively. To omit a file, leave the corresponding // variable `undefined`. @@ -16,7 +16,7 @@ // Example: // let filter = do_get_file("some_test_dir/crlite.filter"); // let coverage = undefined; -// let enrollment = do_get_file("some_test_dir/data.safe.bin"); +// let enrollment = do_get_file("some_test_dir/crlite.enrollment"); // load("./corrupted_crlite_helper.js"); // // Note: @@ -40,7 +40,7 @@ add_task(async function test_crlite_corrupted() { coverage.copyTo(securityStateDirectory, "crlite.coverage"); } if (enrollment != undefined) { - enrollment.copyTo(securityStateDirectory, "data.safe.bin"); + enrollment.copyTo(securityStateDirectory, "crlite.enrollment"); } if (filter != undefined) { filter.copyTo(securityStateDirectory, "crlite.filter"); @@ -55,7 +55,7 @@ add_task(async function test_crlite_corrupted() { ); // This certificate is revoked according to `test_crlite_filters/20201017-0-filter`. - // Its issuer is enrolled according to `test_crlite_preexisting/data.safe.bin`, + // Its issuer is enrolled according to `test_crlite_preexisting/crlite.enrollment`, // and it is covered according to `test_crlite_preexisting/crlite.coverage`. let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem"); @@ -78,20 +78,7 @@ add_task(async function test_crlite_corrupted() { Ci.nsIX509CertDB.FLAG_LOCAL_ONLY ); - // The attempted revocation check should have at least partially initialized - // CRLite. We should have a database (an empty one is created if - // `data.safe.bin` is corrupted). But we should not have a filter or a stash. - let hasDB = await new Promise(resolve => { - certStorage.hasPriorData( - Ci.nsICertStorage.DATA_TYPE_CRLITE, - (rv, result) => { - Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); - resolve(result); - } - ); - }); - Assert.equal(hasDB, true, "CRLite should have a database"); - + // We should not have a filter or a stash. let hasFilter = await new Promise(resolve => { certStorage.hasPriorData( Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_FULL, diff --git a/security/manager/ssl/tests/unit/crlite_enrollment_id.py b/security/manager/ssl/tests/unit/crlite_enrollment_id.py new file mode 100755 index 000000000000..2deb5ad379b1 --- /dev/null +++ b/security/manager/ssl/tests/unit/crlite_enrollment_id.py @@ -0,0 +1,33 @@ +#!/usr/bin/python + +# Given a PEM encoded X.509 certificate, outputs +# base64(SHA256(subject || spki)) +# where `subject` is the RFC 5280 RDNSequence encoding +# the certificate's subject, and `spki` is the RFC 5280 +# SubjectPublicKeyInfo field encoding the certificate's +# public key. + +import sys +import base64 + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives import hashes + +if len(sys.argv) != 2: + print(f"Usage: {sys.argv[0]} ") + sys.exit(1) + +with open(sys.argv[1], "r") as f: + cert = x509.load_pem_x509_certificate(f.read().encode("utf-8"), backend=None) + +subj = cert.subject.public_bytes() +spki = cert.public_key().public_bytes( + format=serialization.PublicFormat.SubjectPublicKeyInfo, + encoding=serialization.Encoding.DER, +) + +digest = hashes.Hash(hashes.SHA256(), backend=None) +digest.update(subj) +digest.update(spki) +print(base64.b64encode(digest.finalize()).decode("utf-8")) diff --git a/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js b/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js index 070482f23682..185f7cff0027 100644 --- a/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js +++ b/security/manager/ssl/tests/unit/test_cert_storage_broken_db.js @@ -41,18 +41,6 @@ async function check_has_prior_cert_data(certStorage, expectedResult) { ); } -async function check_has_prior_crlite_data(certStorage, expectedResult) { - let hasPriorCRLiteData = await call_has_prior_data( - certStorage, - Ci.nsICertStorage.DATA_TYPE_CRLITE - ); - Assert.equal( - hasPriorCRLiteData, - expectedResult, - `should ${expectedResult ? "have" : "not have"} prior CRLite data` - ); -} - add_task(async function() { // Create an invalid database. let fileToCopy = do_get_file("test_cert_storage_broken_db.js"); @@ -65,7 +53,6 @@ add_task(async function() { ); check_has_prior_revocation_data(certStorage, false); check_has_prior_cert_data(certStorage, false); - check_has_prior_crlite_data(certStorage, false); let result = await new Promise(resolve => { certStorage.setRevocations([], resolve); @@ -74,7 +61,6 @@ add_task(async function() { check_has_prior_revocation_data(certStorage, true); check_has_prior_cert_data(certStorage, false); - check_has_prior_crlite_data(certStorage, false); result = await new Promise(resolve => { certStorage.addCerts([], resolve); @@ -83,13 +69,4 @@ add_task(async function() { check_has_prior_revocation_data(certStorage, true); check_has_prior_cert_data(certStorage, true); - check_has_prior_crlite_data(certStorage, false); - - result = await new Promise(resolve => { - certStorage.setCRLiteState([], resolve); - }); - Assert.equal(result, Cr.NS_OK, "setCRLiteState should succeed"); - check_has_prior_revocation_data(certStorage, true); - check_has_prior_cert_data(certStorage, true); - check_has_prior_crlite_data(certStorage, true); }); diff --git a/security/manager/ssl/tests/unit/test_cert_storage_direct.js b/security/manager/ssl/tests/unit/test_cert_storage_direct.js index b0093024a7a7..7105606ea49c 100644 --- a/security/manager/ssl/tests/unit/test_cert_storage_direct.js +++ b/security/manager/ssl/tests/unit/test_cert_storage_direct.js @@ -242,17 +242,6 @@ add_task(async function test_batched_removal() { Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now"); }); -class CRLiteState { - constructor(subject, spkiHash, state) { - this.subject = btoa(subject); - this.spkiHash = spkiHash; - this.state = state; - } -} -CRLiteState.prototype.QueryInterface = ChromeUtils.generateQI([ - "nsICRLiteState", -]); - class CRLiteCoverage { constructor(ctLogID, minTimestamp, maxTimestamp) { this.b64LogID = ctLogID; @@ -264,133 +253,6 @@ CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([ "nsICRLiteCoverage", ]); -async function addCRLiteState(state) { - let result = await new Promise(resolve => { - certStorage.setCRLiteState(state, resolve); - }); - Assert.equal(result, Cr.NS_OK, "setCRLiteState should succeed"); -} - -add_task(async function test_crlite_state() { - // echo -n "some spki 1" | sha256sum | xxd -r -p | base64 - let crliteState1 = new CRLiteState( - "some subject 1", - "bDlKlhR5ptlvuxclnZ3RQHznG8/3pgIybrRJ/Zvn9L8=", - Ci.nsICertStorage.STATE_ENFORCE - ); - // echo -n "some spki 2" | sha256sum | xxd -r -p | base64 - let crliteState2 = new CRLiteState( - "some subject 2", - "ZlXvlHhtdx4yKwkhZqg7Opv5T1ofwzorlsCoLf0wnlY=", - Ci.nsICertStorage.STATE_UNSET - ); - // echo -n "some spki 3" | sha256sum | xxd -r -p | base64 - let crliteState3 = new CRLiteState( - "some subject 3", - "pp1SRn6njaHX/c+b2uf82JPeBkWhPfTBp/Mxb3xkjRM=", - Ci.nsICertStorage.STATE_ENFORCE - ); - await addCRLiteState([crliteState1, crliteState2, crliteState3]); - - let state1 = certStorage.getCRLiteState( - stringToArray("some subject 1"), - stringToArray("some spki 1") - ); - Assert.equal(state1, Ci.nsICertStorage.STATE_ENFORCE); - let state2 = certStorage.getCRLiteState( - stringToArray("some subject 2"), - stringToArray("some spki 2") - ); - Assert.equal(state2, Ci.nsICertStorage.STATE_UNSET); - let state3 = certStorage.getCRLiteState( - stringToArray("some subject 3"), - stringToArray("some spki 3") - ); - Assert.equal(state3, Ci.nsICertStorage.STATE_ENFORCE); - - // Check that if we never set the state of a particular subject/spki pair, we get "unset" when we - // look it up. - let stateNeverSet = certStorage.getCRLiteState( - stringToArray("some unknown subject"), - stringToArray("some unknown spki") - ); - Assert.equal(stateNeverSet, Ci.nsICertStorage.STATE_UNSET); - - // "some subject 1"/"some spki 1" and "some subject 3"/"some spki 3" both have their CRLite state - // set. However, the combination of "some subject 3"/"some spki1" should not. - let stateDifferentSubjectSPKI = certStorage.getCRLiteState( - stringToArray("some subject 3"), - stringToArray("some spki 1") - ); - Assert.equal(stateDifferentSubjectSPKI, Ci.nsICertStorage.STATE_UNSET); - - let anotherStateDifferentSubjectSPKI = certStorage.getCRLiteState( - stringToArray("some subject 1"), - stringToArray("some spki 2") - ); - Assert.equal(anotherStateDifferentSubjectSPKI, Ci.nsICertStorage.STATE_UNSET); - let yetAnotherStateDifferentSubjectSPKI = certStorage.getCRLiteState( - stringToArray("some subject 2"), - stringToArray("some spki 1") - ); - Assert.equal( - yetAnotherStateDifferentSubjectSPKI, - Ci.nsICertStorage.STATE_UNSET - ); - - crliteState3 = new CRLiteState( - "some subject 3", - "pp1SRn6njaHX/c+b2uf82JPeBkWhPfTBp/Mxb3xkjRM=", - Ci.nsICertStorage.STATE_UNSET - ); - await addCRLiteState([crliteState3]); - state3 = certStorage.getCRLiteState( - stringToArray("some subject 3"), - stringToArray("some spki 3") - ); - Assert.equal(state3, Ci.nsICertStorage.STATE_UNSET); - - crliteState2 = new CRLiteState( - "some subject 2", - "ZlXvlHhtdx4yKwkhZqg7Opv5T1ofwzorlsCoLf0wnlY=", - Ci.nsICertStorage.STATE_ENFORCE - ); - await addCRLiteState([crliteState2]); - state2 = certStorage.getCRLiteState( - stringToArray("some subject 2"), - stringToArray("some spki 2") - ); - Assert.equal(state2, Ci.nsICertStorage.STATE_ENFORCE); - - // Inserting a subject/spki pair with a state value outside of our expected - // values will succeed. However, since our data type is a signed 16-bit value, - // values outside that range will be truncated. The least significant 16 bits - // of 2013003773 are FFFD, which when interpreted as a signed 16-bit integer - // comes out to -3. - // echo -n "some spki 4" | sha256sum | xxd -r -p | base64 - let bogusValueState = new CRLiteState( - "some subject 4", - "1eA0++hCqzt8vpzREYSqHAqpEOLchZca1Gx8viCVYzc=", - 2013003773 - ); - await addCRLiteState([bogusValueState]); - let bogusValueStateValue = certStorage.getCRLiteState( - stringToArray("some subject 4"), - stringToArray("some spki 4") - ); - Assert.equal(bogusValueStateValue, -3); -}); - -async function enrollCertForCRLite(nsCert) { - let { subjectString, spkiHashString } = getSubjectAndSPKIHash(nsCert); - let crliteState = new CRLiteState( - subjectString, - spkiHashString, - Ci.nsICertStorage.STATE_ENFORCE - ); - await addCRLiteState([crliteState]); -} - add_task(async function test_crlite_filter() { let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( Ci.nsIX509CertDB @@ -398,29 +260,27 @@ add_task(async function test_crlite_filter() { let validCertIssuer = constructCertFromFile( "test_cert_storage_direct/valid-cert-issuer.pem" ); - await enrollCertForCRLite(validCertIssuer); let validCert = constructCertFromFile( "test_cert_storage_direct/valid-cert.pem" ); let revokedCertIssuer = constructCertFromFile( "test_cert_storage_direct/revoked-cert-issuer.pem" ); - await enrollCertForCRLite(revokedCertIssuer); let revokedCert = constructCertFromFile( "test_cert_storage_direct/revoked-cert.pem" ); - let filterFile = do_get_file( "test_cert_storage_direct/test-filter.crlite", false ); ok(filterFile.exists(), "test filter file should exist"); + let enrollment = []; let coverage = []; let filterBytes = stringToArray(readFile(filterFile)); // First simualte a filter that does not cover any certificates. With CRLite // enabled, none of the certificates should appear to be revoked. let setFullCRLiteFilterResult = await new Promise(resolve => { - certStorage.setFullCRLiteFilter(filterBytes, coverage, resolve); + certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve); }); Assert.equal( setFullCRLiteFilterResult, @@ -463,8 +323,15 @@ add_task(async function test_crlite_filter() { ) ); + // crlite_enrollment_id.py test_crlite_filters/issuer.pem + enrollment.push("UbH9/ZAnjuqf79Xhah1mFOWo6ZvgQCgsdheWfjvVUM8="); + // crlite_enrollment_id.py test_crlite_filters/no-sct-issuer.pem + enrollment.push("Myn7EasO1QikOtNmo/UZdh6snCAw0BOY6wgU8OsUeeY="); + // crlite_enrollment_id.py test_cert_storage_direct/revoked-cert-issuer.pem + enrollment.push("HTvSp2263dqBYtgYA2fldKAoTYcEVLPVTlRia9XaoCQ="); + setFullCRLiteFilterResult = await new Promise(resolve => { - certStorage.setFullCRLiteFilter(filterBytes, coverage, resolve); + certStorage.setFullCRLiteFilter(filterBytes, enrollment, coverage, resolve); }); Assert.equal( setFullCRLiteFilterResult, diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js b/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js index 153fdc56f678..744cc7fc37c9 100644 --- a/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js +++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting.js @@ -45,15 +45,4 @@ add_task(async function() { ); }); Assert.equal(hasPriorCertData, true, "should have prior cert data"); - - let hasPriorCRLiteData = await new Promise(resolve => { - certStorage.hasPriorData( - Ci.nsICertStorage.DATA_TYPE_CRLITE, - (rv, hasPriorData) => { - Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); - resolve(hasPriorData); - } - ); - }); - Assert.equal(hasPriorCRLiteData, true, "should have prior cert data"); }); diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js index 131b1a6cf884..fc15ee217198 100644 --- a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js +++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite.js @@ -19,10 +19,6 @@ add_task(async function() { let dbDirectory = do_get_profile(); dbDirectory.append("security_state"); - let dbFile = do_get_file( - "test_cert_storage_preexisting_crlite/data.safe.bin" - ); - dbFile.copyTo(dbDirectory, "data.safe.bin"); let crliteFile = do_get_file( "test_cert_storage_preexisting_crlite/crlite.filter" ); @@ -31,6 +27,10 @@ add_task(async function() { "test_cert_storage_preexisting_crlite/crlite.coverage" ); coverageFile.copyTo(dbDirectory, "crlite.coverage"); + let enrollmentFile = do_get_file( + "test_cert_storage_preexisting_crlite/crlite.enrollment" + ); + enrollmentFile.copyTo(dbDirectory, "crlite.enrollment"); let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService( Ci.nsICertStorage diff --git a/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.enrollment b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.enrollment new file mode 100644 index 000000000000..aac0238188a6 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_cert_storage_preexisting_crlite/crlite.enrollment @@ -0,0 +1 @@ +3):fv 0yQ'jf@(,v~;P;ҧmځbgt(MTNTbkڠ$ \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_crlite_corrupted/trunc-issuer-id.enrollment b/security/manager/ssl/tests/unit/test_crlite_corrupted/trunc-issuer-id.enrollment new file mode 100644 index 000000000000..119fd67098e4 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_crlite_corrupted/trunc-issuer-id.enrollment @@ -0,0 +1,2 @@ + +  \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_crlite_corrupted/version-0.enrollment b/security/manager/ssl/tests/unit/test_crlite_corrupted/version-0.enrollment new file mode 100644 index 000000000000..3ef70ac1883f Binary files /dev/null and b/security/manager/ssl/tests/unit/test_crlite_corrupted/version-0.enrollment differ diff --git a/security/manager/ssl/tests/unit/test_crlite_coverage_missing.js b/security/manager/ssl/tests/unit/test_crlite_coverage_missing.js index 78a207b4fae8..2b71c3dfe284 100644 --- a/security/manager/ssl/tests/unit/test_crlite_coverage_missing.js +++ b/security/manager/ssl/tests/unit/test_crlite_coverage_missing.js @@ -11,7 +11,7 @@ /* eslint-disable no-unused-vars */ let coverage = undefined; -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc1.js b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc1.js index c6f5ba7c2643..178288596485 100644 --- a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc1.js +++ b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc1.js @@ -11,7 +11,7 @@ /* eslint-disable no-unused-vars */ let coverage = do_get_file("test_crlite_corrupted/trunc-log-id.coverage"); -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc2.js b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc2.js index 81d7b1312100..0eed16bac3f1 100644 --- a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc2.js +++ b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc2.js @@ -13,7 +13,7 @@ let coverage = do_get_file( "test_crlite_corrupted/trunc-min-timestamp.coverage" ); -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc3.js b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc3.js index 65b94d3c94f4..419a639b07e3 100644 --- a/security/manager/ssl/tests/unit/test_crlite_coverage_trunc3.js +++ b/security/manager/ssl/tests/unit/test_crlite_coverage_trunc3.js @@ -13,7 +13,7 @@ let coverage = do_get_file( "test_crlite_corrupted/trunc-max-timestamp.coverage" ); -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_coverage_version.js b/security/manager/ssl/tests/unit/test_crlite_coverage_version.js index d4f62db3b9bc..1764e5abaff5 100644 --- a/security/manager/ssl/tests/unit/test_crlite_coverage_version.js +++ b/security/manager/ssl/tests/unit/test_crlite_coverage_version.js @@ -11,7 +11,7 @@ /* eslint-disable no-unused-vars */ let coverage = do_get_file("test_crlite_corrupted/version-0.coverage"); -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_enrollment_trunc1.js b/security/manager/ssl/tests/unit/test_crlite_enrollment_trunc1.js new file mode 100644 index 000000000000..5f259f28a2d7 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_crlite_enrollment_trunc1.js @@ -0,0 +1,19 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Tests that CRLite is left in the uninitialized state when the profile +// contains a corrupted enrollment file. Specifically, this handles the case +// where the enrollment file is truncated in an issuer ID field. + +"use strict"; + +/* eslint-disable no-unused-vars */ +let coverage = do_get_file("test_crlite_preexisting/crlite.coverage"); +let enrollment = do_get_file( + "test_crlite_corrupted/trunc-issuer-id.enrollment" +); +let filter = do_get_file("test_crlite_filters/20201017-0-filter"); + +load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_enrollment_version.js b/security/manager/ssl/tests/unit/test_crlite_enrollment_version.js new file mode 100644 index 000000000000..8c673a47d511 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_crlite_enrollment_version.js @@ -0,0 +1,17 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Tests that CRLite is left in the uninitialized state when the profile +// contains a corrupted enrollment file. Specifically, this handles the case +// where the enrollment file's version is not recognized. + +"use strict"; + +/* eslint-disable no-unused-vars */ +let coverage = do_get_file("test_crlite_preexisting/crlite.coverage"); +let enrollment = do_get_file("test_crlite_corrupted/version-0.enrollment"); +let filter = do_get_file("test_crlite_filters/20201017-0-filter"); + +load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_filter_corrupted.js b/security/manager/ssl/tests/unit/test_crlite_filter_corrupted.js index 4cb598916408..cc947d287f58 100644 --- a/security/manager/ssl/tests/unit/test_crlite_filter_corrupted.js +++ b/security/manager/ssl/tests/unit/test_crlite_filter_corrupted.js @@ -15,7 +15,7 @@ /* eslint-disable no-unused-vars */ let coverage = do_get_file("test_crlite_preexisting/crlite.coverage"); -let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); +let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); let filter = do_get_file("test_crlite_corrupted/hash-alg-0.filter"); load("./corrupted_crlite_helper.js"); diff --git a/security/manager/ssl/tests/unit/test_crlite_filters.js b/security/manager/ssl/tests/unit/test_crlite_filters.js index 103149cf569c..b7ae1cf9a1af 100644 --- a/security/manager/ssl/tests/unit/test_crlite_filters.js +++ b/security/manager/ssl/tests/unit/test_crlite_filters.js @@ -15,10 +15,7 @@ const { TestUtils } = ChromeUtils.import( "resource://testing-common/TestUtils.jsm" ); -const { - CRLiteFiltersClient, - IntermediatePreloadsClient, -} = RemoteSecuritySettings.init(); +const { CRLiteFiltersClient } = RemoteSecuritySettings.init(); const CRLITE_FILTERS_ENABLED_PREF = "security.remote_settings.crlite_filters.enabled"; @@ -27,6 +24,11 @@ const INTERMEDIATES_ENABLED_PREF = const INTERMEDIATES_DL_PER_POLL_PREF = "security.remote_settings.intermediates.downloads_per_poll"; +// crlite_enrollment_id.py test_crlite_filters/issuer.pem +const ISSUER_PEM_UID = "UbH9/ZAnjuqf79Xhah1mFOWo6ZvgQCgsdheWfjvVUM8="; +// crlite_enrollment_id.py test_crlite_filters/no-sct-issuer.pem +const NO_SCT_ISSUER_PEM_UID = "Myn7EasO1QikOtNmo/UZdh6snCAw0BOY6wgU8OsUeeY="; + function getHashCommon(aStr, useBase64) { let hasher = Cc["@mozilla.org/security/hash;1"].createInstance( Ci.nsICryptoHash @@ -98,6 +100,8 @@ async function syncAndDownload(filters, clear = true) { parent: filter.type == "diff" ? filter.parent : undefined, id: filter.id, coverage: filter.type == "full" ? filter.coverage : undefined, + enrolledIssuers: + filter.type == "full" ? filter.enrolledIssuers : undefined, }; await localDB.create(record); @@ -357,15 +361,6 @@ add_task(async function test_crlite_filters_multiple_days() { ); }); -function getCRLiteEnrollmentRecordFor(nsCert) { - let { subjectString, spkiHashString } = getSubjectAndSPKIHash(nsCert); - return { - subjectDN: btoa(subjectString), - pubKeyHash: spkiHashString, - crlite_enrolled: true, - }; -} - add_task(async function test_crlite_confirm_revocations_mode() { Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true); Services.prefs.setIntPref( @@ -382,20 +377,6 @@ add_task(async function test_crlite_confirm_revocations_mode() { "test_crlite_filters/no-sct-issuer.pem" ); - let crliteEnrollmentRecords = [ - getCRLiteEnrollmentRecordFor(issuerCert), - getCRLiteEnrollmentRecordFor(noSCTCertIssuer), - ]; - - await IntermediatePreloadsClient.onSync({ - data: { - current: crliteEnrollmentRecords, - created: crliteEnrollmentRecords, - updated: [], - deleted: [], - }, - }); - let result = await syncAndDownload([ { timestamp: "2020-10-17T00:00:00Z", @@ -413,6 +394,7 @@ add_task(async function test_crlite_confirm_revocations_mode() { maxTimestamp: 9999999999999, }, ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], }, ]); equal( @@ -484,20 +466,6 @@ add_task(async function test_crlite_filters_and_check_revocation() { "test_crlite_filters/no-sct-issuer.pem" ); - let crliteEnrollmentRecords = [ - getCRLiteEnrollmentRecordFor(issuerCert), - getCRLiteEnrollmentRecordFor(noSCTCertIssuer), - ]; - - await IntermediatePreloadsClient.onSync({ - data: { - current: crliteEnrollmentRecords, - created: crliteEnrollmentRecords, - updated: [], - deleted: [], - }, - }); - let result = await syncAndDownload([ { timestamp: "2020-10-17T00:00:00Z", @@ -515,6 +483,7 @@ add_task(async function test_crlite_filters_and_check_revocation() { maxTimestamp: 9999999999999, }, ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], }, ]); equal( @@ -723,6 +692,7 @@ add_task(async function test_crlite_filters_and_check_revocation() { maxTimestamp: 9999999999999, }, ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], }, ]); equal( @@ -757,6 +727,7 @@ add_task(async function test_crlite_filters_avoid_reprocessing_filters() { maxTimestamp: 9999999999999, }, ], + enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID], }, { timestamp: "2019-01-01T06:00:00Z", diff --git a/security/manager/ssl/tests/unit/test_crlite_preexisting.js b/security/manager/ssl/tests/unit/test_crlite_preexisting.js index fa2e5495e32b..ad6f190174a0 100644 --- a/security/manager/ssl/tests/unit/test_crlite_preexisting.js +++ b/security/manager/ssl/tests/unit/test_crlite_preexisting.js @@ -67,10 +67,13 @@ add_task(async function test_preexisting_crlite_data() { Ci.nsICertStorage ); await new Promise(resolve => { - certStorage.hasPriorData(Ci.nsICertStorage.DATA_TYPE_CRLITE, (rv, _) => { - Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); - resolve(); - }); + certStorage.hasPriorData( + Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_INCREMENTAL, + (rv, _) => { + Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); + resolve(); + } + ); }); await checkCertErrorGenericAtTime( certdb, @@ -153,8 +156,12 @@ function run_test() { stashFile.copyTo(securityStateDirectory, "crlite.stash"); let coverageFile = do_get_file("test_crlite_preexisting/crlite.coverage"); coverageFile.copyTo(securityStateDirectory, "crlite.coverage"); - let certStorageFile = do_get_file("test_crlite_preexisting/data.safe.bin"); - certStorageFile.copyTo(securityStateDirectory, "data.safe.bin"); + let enrollmentFile = do_get_file("test_crlite_preexisting/crlite.enrollment"); + enrollmentFile.copyTo(securityStateDirectory, "crlite.enrollment"); + let certStorageFile = do_get_file( + "test_crlite_preexisting/crlite.enrollment" + ); + certStorageFile.copyTo(securityStateDirectory, "crlite.enrollment"); run_next_test(); } diff --git a/security/manager/ssl/tests/unit/test_crlite_preexisting/crlite.enrollment b/security/manager/ssl/tests/unit/test_crlite_preexisting/crlite.enrollment new file mode 100644 index 000000000000..7f34283ded13 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_crlite_preexisting/crlite.enrollment @@ -0,0 +1 @@ +3):fv 0yQ'jf@(,v~;P \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_crlite_preexisting/data.safe.bin b/security/manager/ssl/tests/unit/test_crlite_preexisting/data.safe.bin deleted file mode 100644 index b843968b62cb..000000000000 Binary files a/security/manager/ssl/tests/unit/test_crlite_preexisting/data.safe.bin and /dev/null differ diff --git a/security/manager/ssl/tests/unit/test_crlite_stash_corrupted.js b/security/manager/ssl/tests/unit/test_crlite_stash_corrupted.js index 649a85b1aba2..17bfb2db3d86 100644 --- a/security/manager/ssl/tests/unit/test_crlite_stash_corrupted.js +++ b/security/manager/ssl/tests/unit/test_crlite_stash_corrupted.js @@ -20,8 +20,8 @@ add_task(async function test_crlite_stash_corrupted() { let coverage = do_get_file("test_crlite_preexisting/crlite.coverage"); coverage.copyTo(securityStateDirectory, "crlite.coverage"); - let enrollment = do_get_file("test_crlite_preexisting/data.safe.bin"); - enrollment.copyTo(securityStateDirectory, "data.safe.bin"); + let enrollment = do_get_file("test_crlite_preexisting/crlite.enrollment"); + enrollment.copyTo(securityStateDirectory, "crlite.enrollment"); let filter = do_get_file("test_crlite_filters/20201017-0-filter"); filter.copyTo(securityStateDirectory, "crlite.filter"); @@ -39,14 +39,17 @@ add_task(async function test_crlite_stash_corrupted() { // Await a task that ensures the stash loading task has completed. await new Promise(resolve => { - certStorage.hasPriorData(Ci.nsICertStorage.DATA_TYPE_CRLITE, (rv, _) => { - Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); - resolve(); - }); + certStorage.hasPriorData( + Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_INCREMENTAL, + (rv, _) => { + Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); + resolve(); + } + ); }); // This certificate is revoked according to `test_crlite_filters/20201017-0-filter`. - // Its issuer is enrolled according to `test_crlite_preexisting/data.safe.bin`, + // Its issuer is enrolled according to `test_crlite_preexisting/crlite.enrollment`, // and it is covered according to `test_crlite_preexisting/crlite.coverage`. let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem"); @@ -67,17 +70,6 @@ add_task(async function test_crlite_stash_corrupted() { 0 ); - let hasDB = await new Promise(resolve => { - certStorage.hasPriorData( - Ci.nsICertStorage.DATA_TYPE_CRLITE, - (rv, result) => { - Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed"); - resolve(result); - } - ); - }); - Assert.equal(hasDB, true, "CRLite should have a database"); - let hasFilter = await new Promise(resolve => { certStorage.hasPriorData( Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_FULL, diff --git a/security/manager/ssl/tests/unit/test_intermediate_preloads.js b/security/manager/ssl/tests/unit/test_intermediate_preloads.js index 33f34b2decb3..0f9673c1fe1b 100644 --- a/security/manager/ssl/tests/unit/test_intermediate_preloads.js +++ b/security/manager/ssl/tests/unit/test_intermediate_preloads.js @@ -299,15 +299,6 @@ add_task(async function test_preload_basic() { let intermediateDERBytes = atob(pemToBase64(intermediateBytes)); let intermediateCert = new X509.Certificate(); intermediateCert.parse(stringToArray(intermediateDERBytes)); - let crliteStateBefore = certStorage.getCRLiteState( - intermediateCert.tbsCertificate.subject._der._bytes, - intermediateCert.tbsCertificate.subjectPublicKeyInfo._der._bytes - ); - equal( - crliteStateBefore, - Ci.nsICertStorage.STATE_UNSET, - "crlite state should be unset before" - ); const result = await syncAndDownload(["int.pem", "int2.pem"]); equal(result, "success", "Preloading update should have run"); @@ -366,16 +357,6 @@ add_task(async function test_preload_basic() { }, }); - let crliteStateAfter = certStorage.getCRLiteState( - intermediateCert.tbsCertificate.subject._der._bytes, - intermediateCert.tbsCertificate.subjectPublicKeyInfo._der._bytes - ); - equal( - crliteStateAfter, - Ci.nsICertStorage.STATE_ENFORCE, - "crlite state should be set after" - ); - // check that ee cert 2 does not verify - since we don't know the issuer of // this certificate await checkCertErrorGeneric( diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 239d0b47d01f..7183750f41c9 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -106,6 +106,8 @@ tags = remote-settings psm [test_crlite_preexisting.js] [test_crlite_filter_corrupted.js] [test_crlite_stash_corrupted.js] +[test_crlite_enrollment_version.js] +[test_crlite_enrollment_trunc1.js] [test_crlite_coverage_version.js] [test_crlite_coverage_trunc1.js] [test_crlite_coverage_trunc2.js]