Bug 1949027 - remove support for legacy CRLite filter format. r=keeler

Differential Revision: https://phabricator.services.mozilla.com/D238680
This commit is contained in:
John Schanck
2025-02-20 06:08:06 +00:00
parent 6960c618af
commit b3319016c8
46 changed files with 132 additions and 1686 deletions

1
Cargo.lock generated
View File

@@ -751,7 +751,6 @@ dependencies = [
"nserror",
"nsstring",
"rkv",
"rust_cascade",
"sha2",
"static_prefs",
"storage_variant",

View File

@@ -702,34 +702,7 @@ Result BuildCRLiteTimestampArray(
return Success;
}
Result NSSCertDBTrustDomain::CheckCRLiteStash(
const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
const nsTArray<uint8_t>& serialNumberBytes) {
// This information is deterministic and has already been validated by our
// infrastructure (it comes from signed CRLs), so if the stash says a
// certificate is revoked, it is.
bool isRevokedByStash = false;
nsresult rv = mCertStorage->IsCertRevokedByStash(
issuerSubjectPublicKeyInfoBytes, serialNumberBytes, &isRevokedByStash);
if (NS_FAILED(rv)) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
"failed"));
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
if (isRevokedByStash) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
("NSSCertDBTrustDomain::CheckCRLiteStash: IsCertRevokedByStash "
"returned true"));
mozilla::glean::cert_verifier::crlite_status.Get("revoked_in_stash"_ns)
.Add(1);
return Result::ERROR_REVOKED_CERTIFICATE;
}
return Success;
}
Result NSSCertDBTrustDomain::CheckCRLite(
const nsTArray<uint8_t>& issuerBytes,
const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
const nsTArray<uint8_t>& serialNumberBytes,
const nsTArray<RefPtr<nsICRLiteTimestamp>>& timestamps,
@@ -737,8 +710,8 @@ Result NSSCertDBTrustDomain::CheckCRLite(
filterCoversCertificate = false;
int16_t crliteRevocationState;
nsresult rv = mCertStorage->GetCRLiteRevocationState(
issuerBytes, issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
timestamps, &crliteRevocationState);
issuerSubjectPublicKeyInfoBytes, serialNumberBytes, timestamps,
&crliteRevocationState);
if (NS_FAILED(rv)) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
("NSSCertDBTrustDomain::CheckCRLite: CRLite call failed"));
@@ -885,29 +858,17 @@ Result NSSCertDBTrustDomain::CheckRevocationByCRLite(
nsTArray<uint8_t> serialNumberBytes;
serialNumberBytes.AppendElements(certID.serialNumber.UnsafeGetData(),
certID.serialNumber.GetLength());
// The CRLite stash is essentially a subset of a collection of CRLs, so if
// it says a certificate is revoked, it is.
Result rv =
CheckCRLiteStash(issuerSubjectPublicKeyInfoBytes, serialNumberBytes);
if (rv != Success) {
crliteCoversCertificate = (rv == Result::ERROR_REVOKED_CERTIFICATE);
return rv;
}
nsTArray<uint8_t> issuerBytes;
issuerBytes.AppendElements(certID.issuer.UnsafeGetData(),
certID.issuer.GetLength());
nsTArray<RefPtr<nsICRLiteTimestamp>> timestamps;
rv = BuildCRLiteTimestampArray(sctExtension, timestamps);
Result rv = BuildCRLiteTimestampArray(sctExtension, timestamps);
if (rv != Success) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
("decoding SCT extension failed - CRLite will be not be "
"consulted"));
return Success;
}
return CheckCRLite(issuerBytes, issuerSubjectPublicKeyInfoBytes,
serialNumberBytes, timestamps, crliteCoversCertificate);
return CheckCRLite(issuerSubjectPublicKeyInfoBytes, serialNumberBytes,
timestamps, crliteCoversCertificate);
}
Result NSSCertDBTrustDomain::CheckRevocationByOCSP(

View File

@@ -248,11 +248,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
IssuerSources GetIssuerSources() { return mIssuerSources; }
private:
Result CheckCRLiteStash(
const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
const nsTArray<uint8_t>& serialNumberBytes);
Result CheckCRLite(
const nsTArray<uint8_t>& issuerBytes,
const nsTArray<uint8_t>& issuerSubjectPublicKeyInfoBytes,
const nsTArray<uint8_t>& serialNumberBytes,
const nsTArray<RefPtr<nsICRLiteTimestamp>>& crliteTimestamps,

View File

@@ -57,17 +57,6 @@ function bytesToString(bytes) {
return String.fromCharCode.apply(null, bytes);
}
class CRLiteCoverage {
constructor(b64LogID, minTimestamp, maxTimestamp) {
this.b64LogID = b64LogID;
this.minTimestamp = minTimestamp;
this.maxTimestamp = maxTimestamp;
}
}
CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([
"nsICRLiteCoverage",
]);
class CertInfo {
constructor(cert, subject) {
this.cert = cert;
@@ -124,9 +113,9 @@ function hasPriorData(dataType) {
Ci.nsICertStorage
);
return new Promise(resolve => {
certStorage.hasPriorData(dataType, (rv, hasPriorData) => {
certStorage.hasPriorData(dataType, (rv, out) => {
if (rv == Cr.NS_OK) {
resolve(hasPriorData);
resolve(out);
} else {
// If calling hasPriorData failed, assume we need to reload everything
// (even though it's unlikely doing so will succeed).
@@ -570,10 +559,10 @@ class CRLiteFilters {
toReset.map(r => ({ ...r, loaded_into_cert_storage: false }))
);
}
let hasPriorStash = await hasPriorData(
let hasPriorDelta = await hasPriorData(
Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_INCREMENTAL
);
if (!hasPriorStash) {
if (!hasPriorDelta) {
let current = await this.getFilteredRecords();
let toReset = current.filter(
record => record.incremental && record.loaded_into_cert_storage
@@ -651,56 +640,14 @@ class CRLiteFilters {
}
let filter = fullFiltersDownloaded[0];
let coverage = [];
if (filter.coverage) {
for (let entry of filter.coverage) {
coverage.push(
new CRLiteCoverage(
entry.logID,
entry.minTimestamp,
entry.maxTimestamp
)
);
}
}
let enrollment = filter.enrolledIssuers ? filter.enrolledIssuers : [];
await new Promise(resolve => {
certList.setFullCRLiteFilter(filter.bytes, enrollment, coverage, rv => {
certList.setFullCRLiteFilter(filter.bytes, rv => {
lazy.log.debug(`setFullCRLiteFilter: ${rv}`);
resolve();
});
});
}
let stashes = filtersDownloaded.filter(
filter =>
filter.incremental && filter.attachment.filename.endsWith("stash")
);
let totalLength = stashes.reduce(
(sum, filter) => sum + filter.bytes.length,
0
);
let concatenatedStashes = new Uint8Array(totalLength);
let offset = 0;
for (let filter of stashes) {
concatenatedStashes.set(filter.bytes, offset);
offset += filter.bytes.length;
}
if (concatenatedStashes.length) {
lazy.log.debug(
`adding concatenated incremental updates of total length ${concatenatedStashes.length}`
);
await new Promise(resolve => {
certList.addCRLiteStash(concatenatedStashes, rv => {
lazy.log.debug(`addCRLiteStash: ${rv}`);
resolve();
});
});
}
let deltas = filtersDownloaded.filter(
filter =>
filter.incremental && filter.attachment.filename.endsWith("delta")
);
let deltas = filtersDownloaded.filter(filter => filter.incremental);
for (let filter of deltas) {
lazy.log.debug(`adding delta update of size ${filter.bytes.length}`);
await new Promise(resolve => {

View File

@@ -18,7 +18,6 @@ moz_task = { path = "../../../../xpcom/rust/moz_task" }
nserror = { path = "../../../../xpcom/rust/nserror" }
nsstring = { path = "../../../../xpcom/rust/nsstring" }
rkv = { version = "0.19", default-features = false }
rust_cascade = "1.4.0"
sha2 = "0.10.2"
static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
storage_variant = { path = "../../../../storage/variant" }

View File

@@ -16,7 +16,6 @@ extern crate moz_task;
extern crate nserror;
extern crate nsstring;
extern crate rkv;
extern crate rust_cascade;
extern crate sha2;
extern crate thin_vec;
extern crate time;
@@ -29,7 +28,7 @@ extern crate wr_malloc_size_of;
use wr_malloc_size_of as malloc_size_of;
use base64::prelude::*;
use byteorder::{LittleEndian, NetworkEndian, ReadBytesExt, WriteBytesExt};
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
use clubcard::{ApproximateSizeOf, Queryable};
use clubcard_crlite::{CRLiteClubcard, CRLiteKey, CRLiteQuery, CRLiteStatus};
use crossbeam_utils::atomic::AtomicCell;
@@ -42,14 +41,12 @@ use nserror::{
use nsstring::{nsACString, nsCStr, nsCString, nsString};
use rkv::backend::{BackendEnvironmentBuilder, SafeMode, SafeModeDatabase, SafeModeEnvironment};
use rkv::{StoreError, StoreOptions, Value};
use rust_cascade::Cascade;
use sha2::{Digest, Sha256};
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use std::ffi::{CString, OsString};
use std::fmt::Display;
use std::fs::{create_dir_all, remove_file, File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::fs::{create_dir_all, remove_file, File};
use std::io::{BufRead, BufReader};
use std::mem::size_of;
use std::path::{Path, PathBuf};
use std::str;
@@ -58,8 +55,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
use storage_variant::VariantType;
use thin_vec::ThinVec;
use xpcom::interfaces::{
nsICRLiteCoverage, nsICRLiteTimestamp, nsICertInfo, nsICertStorage, nsICertStorageCallback,
nsIFile, nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, nsIMemoryReporter,
nsICRLiteTimestamp, nsICertInfo, nsICertStorage, nsICertStorageCallback, nsIFile,
nsIHandleReportCallback, nsIIssuerAndSerialRevocationState, nsIMemoryReporter,
nsIMemoryReporterManager, nsIProperties, nsIRevocationState, nsISerialEventTarget,
nsISubjectAndPubKeyRevocationState, nsISupports,
};
@@ -73,12 +70,6 @@ const PREFIX_DATA_TYPE: &str = "datatype";
const LAST_CRLITE_UPDATE_KEY: &str = "last_crlite_update";
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<SafeModeEnvironment>;
type SingleStore = rkv::SingleStore<SafeModeDatabase>;
@@ -133,134 +124,7 @@ impl MallocSizeOf for EnvAndStore {
}
}
struct CascadeWithMetadata {
cascade: Cascade,
/// Maps an RFC 6962 LogID to a pair of 64 bit unix timestamps
coverage: HashMap<Vec<u8>, (u64, u64)>,
/// Set of `SHA256(subject || spki)` values for enrolled issuers
enrollment: HashSet<Vec<u8>>,
}
impl CascadeWithMetadata {
fn new(
cascade: Cascade,
coverage_path: &PathBuf,
enrollment_path: &PathBuf,
) -> Result<Self, SecurityStateError> {
if !coverage_path.exists() {
return Err(SecurityStateError::from("missing coverage file"));
}
if !enrollment_path.exists() {
return Err(SecurityStateError::from("missing enrollment file"));
}
// Deserialize the coverage metadata.
// The format is described in `set_full_crlite_filter`.
let coverage_file = File::open(coverage_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("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 coverage = HashMap::new();
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::<LittleEndian>() {
Ok(value) => min_timestamp = value,
_ => return Err(SecurityStateError::from("truncated CRLite coverage file")),
}
match (&coverage_entry[40..48]).read_u64::<LittleEndian>() {
Ok(value) => max_timestamp = value,
_ => return Err(SecurityStateError::from("truncated CRLite coverage file")),
}
coverage.insert(log_id.to_vec(), (min_timestamp, max_timestamp));
}
// Deserialize the enrollment metadata.
// The format is described in `set_full_crlite_filter`.
let enrollment_file = File::open(enrollment_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 enrollment = 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[..];
enrollment.insert(issuer_id.to_vec());
}
Ok(Self {
cascade,
coverage,
enrollment,
})
}
fn filter_covers_some_timestamp(&self, timestamps: &[CRLiteTimestamp]) -> bool {
let mut covered_timestamp_count = 0;
for entry in timestamps {
if let Some(&(low, high)) = self.coverage.get(entry.log_id.as_ref()) {
if low <= entry.timestamp && entry.timestamp <= high {
covered_timestamp_count += 1;
}
}
}
covered_timestamp_count
>= static_prefs::pref!("security.pki.crlite_timestamps_for_coverage")
}
fn issuer_is_enrolled(&self, subject: &[u8], pub_key: &[u8]) -> bool {
let mut digest = Sha256::default();
digest.update(subject);
digest.update(pub_key);
let issuer_id = digest.finalize();
self.enrollment.contains(issuer_id.as_slice())
}
fn has(&self, crlite_key: &[u8]) -> bool {
self.cascade.has(crlite_key.to_vec())
}
}
impl MallocSizeOf for CascadeWithMetadata {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.cascade.approximate_size_of()
+ self.coverage.size_of(ops)
+ self.enrollment.size_of(ops)
}
}
enum Filter {
Cascade(CascadeWithMetadata),
Clubcard(CRLiteClubcard),
}
@@ -278,47 +142,13 @@ impl Filter {
if let Ok(clubcard) = CRLiteClubcard::from_bytes(&filter_bytes) {
Ok(Some(Filter::Clubcard(clubcard)))
} else if let Ok(maybe_cascade) = Cascade::from_bytes(filter_bytes) {
let Some(cascade) = maybe_cascade else {
return Err(SecurityStateError::from("expecting non-empty filter"));
};
let coverage_path = store_path.join("crlite.coverage");
let enrollment_path = store_path.join("crlite.enrollment");
if !coverage_path.exists() || !enrollment_path.exists() {
return Ok(None);
}
let cascade_with_metadata =
CascadeWithMetadata::new(cascade, &coverage_path, &enrollment_path)?;
Ok(Some(Filter::Cascade(cascade_with_metadata)))
} else {
Err(SecurityStateError::from("invalid CRLite filter"))
}
}
fn has(
&self,
issuer_dn: &[u8],
issuer_spki: &[u8],
issuer_spki_hash: &[u8],
serial: &[u8],
clubcard_crlite_key: &CRLiteKey,
timestamps: &[CRLiteTimestamp],
) -> i16 {
fn has(&self, clubcard_crlite_key: &CRLiteKey, timestamps: &[CRLiteTimestamp]) -> i16 {
match self {
Filter::Cascade(cascade) => {
let mut crlite_key = Vec::with_capacity(issuer_spki_hash.len() + serial.len());
crlite_key.extend_from_slice(issuer_spki_hash);
crlite_key.extend_from_slice(serial);
if !cascade.issuer_is_enrolled(issuer_dn, issuer_spki) {
nsICertStorage::STATE_NOT_ENROLLED
} else if !cascade.filter_covers_some_timestamp(timestamps) {
nsICertStorage::STATE_NOT_COVERED
} else if cascade.has(&crlite_key) {
nsICertStorage::STATE_ENFORCE
} else {
nsICertStorage::STATE_UNSET
}
}
Filter::Clubcard(clubcard) => {
let timestamp_iter = timestamps
.iter()
@@ -354,9 +184,8 @@ impl Filter {
}
impl MallocSizeOf for Filter {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
fn size_of(&self, _: &mut MallocSizeOfOps) -> usize {
match self {
Filter::Cascade(cascade) => cascade.size_of(ops),
Filter::Clubcard(clubcard) => clubcard.approximate_size_of(),
}
}
@@ -367,8 +196,6 @@ struct SecurityState {
profile_path: PathBuf,
env_and_store: Option<EnvAndStore>,
crlite_filters: Vec<Filter>,
/// Maps issuer spki hashes to sets of serial numbers.
crlite_stash: Option<HashMap<Vec<u8>, HashSet<Vec<u8>>>>,
/// Tracks the number of asynchronous operations which have been dispatched but not completed.
remaining_ops: i32,
}
@@ -381,7 +208,6 @@ impl SecurityState {
profile_path,
env_and_store: None,
crlite_filters: vec![],
crlite_stash: None,
remaining_ops: 0,
}
}
@@ -515,7 +341,7 @@ impl SecurityState {
return Ok(!self.crlite_filters.is_empty());
}
if data_type == nsICertStorage::DATA_TYPE_CRLITE_FILTER_INCREMENTAL {
return Ok(self.crlite_stash.is_some() || self.crlite_filters.len() > 1);
return Ok(self.crlite_filters.len() > 1);
}
let env_and_store = match self.env_and_store.as_ref() {
@@ -652,19 +478,15 @@ impl SecurityState {
}
}
pub fn set_full_crlite_filter(
&mut self,
filter: Vec<u8>,
enrolled_issuers: Vec<nsCString>,
coverage_entries: &[(nsCString, u64, u64)],
) -> Result<(), SecurityStateError> {
pub fn set_full_crlite_filter(&mut self, filter: Vec<u8>) -> Result<(), SecurityStateError> {
let store_path = get_store_path(&self.profile_path)?;
// Drop any existing crlite filter and clear the accumulated stash.
// Drop any existing crlite filters
self.crlite_filters.clear();
self.crlite_stash = None;
// Delete the backing data for any stash or delta files.
// Delete the backing data for the previous collection of filters. We may be migrating
// from a bloom filter cascade, so check for and delete "coverage", "enrollment", and
// "stash" files in addition to "delta" and "filter" files.
for entry in std::fs::read_dir(&store_path)? {
let Ok(entry) = entry else {
continue;
@@ -674,7 +496,12 @@ impl SecurityState {
.extension()
.map(|os_str| os_str.to_str())
.flatten();
if extension == Some("stash") || extension == Some("delta") {
if extension == Some("coverage")
|| extension == Some("delta")
|| extension == Some("enrollment")
|| extension == Some("filter")
|| extension == Some("stash")
{
let _ = std::fs::remove_file(entry_path);
}
}
@@ -682,46 +509,6 @@ impl SecurityState {
// Write the new full filter.
std::fs::write(store_path.join("crlite.filter"), &filter)?;
// 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::<u8>() + 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_STANDARD.decode(&b64_log_id) {
Ok(log_id) if log_id.len() == 32 => log_id,
_ => {
warn!("malformed log ID - skipping: {}", b64_log_id);
continue;
}
};
coverage_bytes.extend_from_slice(&log_id);
coverage_bytes.extend_from_slice(&min_t.to_le_bytes());
coverage_bytes.extend_from_slice(&max_t.to_le_bytes());
}
// Write the coverage file for the new filter
std::fs::write(store_path.join("crlite.coverage"), &coverage_bytes)?;
// 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::<u8>() + 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_STANDARD.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
std::fs::write(store_path.join("crlite.enrollment"), &enrollment_bytes)?;
self.note_crlite_update_time()?;
self.load_crlite_filter()?;
self.note_memory_usage();
@@ -743,19 +530,6 @@ impl SecurityState {
Ok(())
}
pub fn add_crlite_stash(&mut self, stash: Vec<u8>) -> Result<(), SecurityStateError> {
// Append the update to the previously-seen stashes.
let mut path = get_store_path(&self.profile_path)?;
path.push("crlite.stash");
let mut stash_file = OpenOptions::new().append(true).create(true).open(path)?;
stash_file.write_all(&stash)?;
let crlite_stash = self.crlite_stash.get_or_insert(HashMap::new());
load_crlite_stash_from_reader_into_map(&mut stash.as_slice(), crlite_stash)?;
self.note_crlite_update_time()?;
self.note_memory_usage();
Ok(())
}
pub fn add_crlite_delta(
&mut self,
delta: Vec<u8>,
@@ -772,28 +546,8 @@ impl SecurityState {
Ok(())
}
pub fn is_cert_revoked_by_stash(
&self,
issuer_spki: &[u8],
serial: &[u8],
) -> Result<bool, SecurityStateError> {
let crlite_stash = match self.crlite_stash.as_ref() {
Some(crlite_stash) => crlite_stash,
None => return Ok(false),
};
let mut digest = Sha256::default();
digest.update(issuer_spki);
let lookup_key = digest.finalize().to_vec();
let serials = match crlite_stash.get(&lookup_key) {
Some(serials) => serials,
None => return Ok(false),
};
Ok(serials.contains(&serial.to_vec()))
}
pub fn get_crlite_revocation_state(
&self,
issuer: &[u8],
issuer_spki: &[u8],
serial_number: &[u8],
timestamps: &[CRLiteTimestamp],
@@ -812,14 +566,7 @@ impl SecurityState {
let issuer_spki_hash = Sha256::digest(issuer_spki);
let clubcard_crlite_key = CRLiteKey::new(issuer_spki_hash.as_ref(), serial_number);
for filter in &self.crlite_filters {
match filter.has(
issuer,
issuer_spki,
issuer_spki_hash.as_ref(),
serial_number,
&clubcard_crlite_key,
timestamps,
) {
match filter.has(&clubcard_crlite_key, timestamps) {
nsICertStorage::STATE_ENFORCE => return nsICertStorage::STATE_ENFORCE,
nsICertStorage::STATE_UNSET => maybe_good = true,
nsICertStorage::STATE_NOT_ENROLLED => covered = true,
@@ -1015,7 +762,6 @@ impl MallocSizeOf for SecurityState {
.iter()
.map(|filter| filter.size_of(ops))
.sum::<usize>()
+ self.crlite_stash.size_of(ops)
+ self.remaining_ops.size_of(ops)
}
}
@@ -1302,71 +1048,26 @@ fn remove_db(path: &Path) -> Result<(), SecurityStateError> {
Ok(())
}
// Helper function to read stash information from the given reader and insert the results into the
// given stash map.
fn load_crlite_stash_from_reader_into_map(
reader: &mut dyn Read,
dest: &mut HashMap<Vec<u8>, HashSet<Vec<u8>>>,
) -> Result<(), SecurityStateError> {
// The basic unit of the stash file is an issuer subject public key info
// hash (sha-256) followed by a number of serial numbers corresponding
// to revoked certificates issued by that issuer. More specifically,
// each unit consists of:
// 4 bytes little-endian: the number of serial numbers following the issuer spki hash
// 1 byte: the length of the issuer spki hash
// issuer spki hash length bytes: the issuer spki hash
// as many times as the indicated serial numbers:
// 1 byte: the length of the serial number
// serial number length bytes: the serial number
// The stash file consists of any number of these units concatenated
// together.
loop {
let num_serials = match reader.read_u32::<LittleEndian>() {
Ok(num_serials) => num_serials,
Err(_) => break, // end of input, presumably
};
let issuer_spki_hash_len = reader.read_u8().map_err(|e| {
SecurityStateError::from(format!("error reading stash issuer_spki_hash_len: {}", e))
})?;
let mut issuer_spki_hash = vec![0; issuer_spki_hash_len.into()];
reader.read_exact(&mut issuer_spki_hash).map_err(|e| {
SecurityStateError::from(format!("error reading stash issuer_spki_hash: {}", e))
})?;
let serials = dest.entry(issuer_spki_hash).or_insert(HashSet::new());
for _ in 0..num_serials {
let serial_len = reader.read_u8().map_err(|e| {
SecurityStateError::from(format!("error reading stash serial_len: {}", e))
})?;
let mut serial = vec![0; serial_len.into()];
reader.read_exact(&mut serial).map_err(|e| {
SecurityStateError::from(format!("error reading stash serial: {}", e))
})?;
let _ = serials.insert(serial);
}
}
Ok(())
}
// This is a helper struct that implements the task that asynchronously reads the CRLite stash on a
// background thread.
struct BackgroundReadStashTask {
// This is a helper struct that implements the task that asynchronously reads CRLite deltas on
// a background thread.
struct BackgroundReadDeltasTask {
profile_path: PathBuf,
security_state: Arc<RwLock<SecurityState>>,
}
impl BackgroundReadStashTask {
impl BackgroundReadDeltasTask {
fn new(
profile_path: PathBuf,
security_state: &Arc<RwLock<SecurityState>>,
) -> BackgroundReadStashTask {
BackgroundReadStashTask {
) -> BackgroundReadDeltasTask {
BackgroundReadDeltasTask {
profile_path,
security_state: Arc::clone(security_state),
}
}
}
impl Task for BackgroundReadStashTask {
impl Task for BackgroundReadDeltasTask {
fn run(&self) {
let Ok(store_path) = get_store_path(&self.profile_path) else {
error!("error getting security_state path");
@@ -1390,23 +1091,6 @@ impl Task for BackgroundReadStashTask {
}
}
let mut maybe_crlite_stash = None;
let stash_path = store_path.join("crlite.stash");
// Before we've downloaded any stashes, this file won't exist.
if stash_path.exists() {
if let Ok(stash_file) = File::open(stash_path) {
let mut stash_reader = BufReader::new(stash_file);
let mut crlite_stash = HashMap::new();
match load_crlite_stash_from_reader_into_map(&mut stash_reader, &mut crlite_stash) {
Ok(()) => maybe_crlite_stash = Some(crlite_stash),
Err(e) => {
error!("error loading crlite stash: {}", e.message);
}
}
} else {
error!("error opening stash file");
}
}
let Ok(mut ss) = self.security_state.write() else {
return;
};
@@ -1414,7 +1098,6 @@ impl Task for BackgroundReadStashTask {
error!("error opening security state db: {}", e.message);
}
ss.crlite_filters.append(&mut delta_filters);
ss.crlite_stash = maybe_crlite_stash;
}
fn done(&self) -> Result<(), nsresult> {
@@ -1434,23 +1117,21 @@ fn do_construct_cert_storage(
});
let memory_reporter = MemoryReporter::allocate(InitMemoryReporter { security_state });
// Dispatch a task to the background task queue to asynchronously read the CRLite stash file (if
// present) and load it into cert_storage. This task does not hold the
// Dispatch a task to the background task queue to asynchronously read CRLite delta files (if
// present) and load them into cert_storage. This task does not hold the
// cert_storage.security_state mutex for the majority of its operation, which allows certificate
// verification threads to query cert_storage without blocking. This is important for
// performance, but it means that certificate verifications that happen before the task has
// completed will not have stash information, and thus may not know of revocations that have
// occurred since the last full CRLite filter was downloaded. As long as the last full filter
// was downloaded no more than 10 days ago, this is no worse than relying on OCSP responses,
// which have a maximum validity of 10 days.
// completed will not have delta information, and thus may not know of revocations that have
// occurred since the last full CRLite filter was downloaded.
// NB: because the background task queue is serial, this task will complete before other tasks
// later dispatched to the queue run. This means that other tasks that interact with the stash
// will do so with the correct set of preconditions.
let load_crlite_stash_task = Box::new(BackgroundReadStashTask::new(
// later dispatched to the queue run. This means that other tasks that depend on deltas will do
// so with the correct set of preconditions.
let load_crlite_deltas_task = Box::new(BackgroundReadDeltasTask::new(
path_buf,
&cert_storage.security_state,
));
let runnable = TaskRunnable::new("LoadCrliteStash", load_crlite_stash_task)?;
let runnable = TaskRunnable::new("LoadCrliteDeltas", load_crlite_deltas_task)?;
TaskRunnable::dispatch(runnable, cert_storage.queue.coerce())?;
if let Some(reporter) = memory_reporter.query_interface::<nsIMemoryReporter>() {
@@ -1729,72 +1410,27 @@ impl CertStorage {
unsafe fn SetFullCRLiteFilter(
&self,
filter: *const ThinVec<u8>,
enrolled_issuers: *const ThinVec<nsCString>,
coverage: *const ThinVec<Option<RefPtr<nsICRLiteCoverage>>>,
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()
|| enrolled_issuers.is_null()
{
if filter.is_null() || callback.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());
for entry in coverage.iter().flatten() {
let mut b64_log_id = nsCString::new();
try_ns!((*entry).GetB64LogID(&mut *b64_log_id).to_result(), or continue);
let mut min_timestamp: u64 = 0;
try_ns!((*entry).GetMinTimestamp(&mut min_timestamp).to_result(), or continue);
let mut max_timestamp: u64 = 0;
try_ns!((*entry).GetMaxTimestamp(&mut max_timestamp).to_result(), or continue);
coverage_entries.push((b64_log_id, min_timestamp, max_timestamp));
}
let task = Box::new(try_ns!(SecurityStateTask::new(
&*callback,
&self.security_state,
move |ss| ss.set_full_crlite_filter(
filter_owned,
enrolled_issuers_owned,
&coverage_entries
),
move |ss| ss.set_full_crlite_filter(filter_owned),
)));
let runnable = try_ns!(TaskRunnable::new("SetFullCRLiteFilter", task));
try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
NS_OK
}
unsafe fn AddCRLiteStash(
&self,
stash: *const ThinVec<u8>,
callback: *const nsICertStorageCallback,
) -> nserror::nsresult {
if !is_main_thread() {
return NS_ERROR_NOT_SAME_THREAD;
}
if stash.is_null() || callback.is_null() {
return NS_ERROR_NULL_POINTER;
}
let stash_owned = (*stash).to_vec();
let task = Box::new(try_ns!(SecurityStateTask::new(
&*callback,
&self.security_state,
move |ss| ss.add_crlite_stash(stash_owned),
)));
let runnable = try_ns!(TaskRunnable::new("AddCRLiteStash", task));
try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
NS_OK
}
unsafe fn AddCRLiteDelta(
&self,
delta: *const ThinVec<u8>,
@@ -1819,26 +1455,25 @@ impl CertStorage {
NS_OK
}
unsafe fn IsCertRevokedByStash(
unsafe fn TestNoteCRLiteUpdateTime(
&self,
issuer_spki: *const ThinVec<u8>,
serial_number: *const ThinVec<u8>,
is_revoked: *mut bool,
callback: *const nsICertStorageCallback,
) -> nserror::nsresult {
if issuer_spki.is_null() || serial_number.is_null() || is_revoked.is_null() {
return NS_ERROR_NULL_POINTER;
if !is_main_thread() {
return NS_ERROR_NOT_SAME_THREAD;
}
let ss = get_security_state!(self);
*is_revoked = match ss.is_cert_revoked_by_stash(&*issuer_spki, &*serial_number) {
Ok(is_revoked) => is_revoked,
Err(_) => return NS_ERROR_FAILURE,
};
let task = Box::new(try_ns!(SecurityStateTask::new(
&*callback,
&self.security_state,
move |ss| ss.note_crlite_update_time(),
)));
let runnable = try_ns!(TaskRunnable::new("TestNoteCRLiteUpdateTime", task));
try_ns!(TaskRunnable::dispatch(runnable, self.queue.coerce()));
NS_OK
}
unsafe fn GetCRLiteRevocationState(
&self,
issuer: *const ThinVec<u8>,
issuerSPKI: *const ThinVec<u8>,
serialNumber: *const ThinVec<u8>,
timestamps: *const ThinVec<Option<RefPtr<nsICRLiteTimestamp>>>,
@@ -1846,11 +1481,7 @@ impl CertStorage {
) -> 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 issuer.is_null()
|| issuerSPKI.is_null()
|| serialNumber.is_null()
|| state.is_null()
|| timestamps.is_null()
if issuerSPKI.is_null() || serialNumber.is_null() || state.is_null() || timestamps.is_null()
{
return NS_ERROR_NULL_POINTER;
}
@@ -1864,12 +1495,7 @@ impl CertStorage {
timestamp_entries.push(CRLiteTimestamp { log_id, timestamp });
}
let ss = get_security_state!(self);
*state = ss.get_crlite_revocation_state(
&*issuer,
&*issuerSPKI,
&*serialNumber,
&timestamp_entries,
);
*state = ss.get_crlite_revocation_state(&*issuerSPKI, &*serialNumber, &timestamp_entries);
NS_OK
}

View File

@@ -51,23 +51,6 @@ interface nsISubjectAndPubKeyRevocationState : nsIRevocationState {
readonly attribute ACString pubKey;
};
/**
* 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
* pair of timestamps. The timestamps are such that the CRLite aggregator has
* seen every certificate from the specified log with an SCT between the two
* timestamps.
* b64LogID is a base 64-encoded RFC 6962 LogID.
* minTimestamp is the smallest timestamp that the CRLite filter covers.
* maxTimestamp is the largest timestamp that the CRLite filter covers.
*/
[scriptable, uuid(416453f7-29bd-4820-a039-9c2e055d3715)]
interface nsICRLiteCoverage : nsISupports {
readonly attribute ACString b64LogID;
readonly attribute unsigned long long minTimestamp;
readonly attribute unsigned long long maxTimestamp;
};
/**
* An interface representing the id and timestamp fields from an RFC 6962
* SignedCertificateTimestamp struct.
@@ -148,23 +131,20 @@ interface nsICertStorage : nsISupports {
in Array<octet> pubkey);
/**
* 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" or "deltas").
* Given the contents of a new CRLite filter, replaces any existing filter
* with the new one. Also clears any previously-set incremental revocation
* updates ("deltas").
*/
[must_use]
void setFullCRLiteFilter(in Array<octet> filter,
in Array<ACString> enrolledIssuers,
in Array<nsICRLiteCoverage> coverage,
in nsICertStorageCallback callback);
/**
* Given the DER-encoded issuer distinguished name, DER-encoded issuer subject public key info,
* the bytes of the value of the serial number (so, not including the DER tag and length) of a
* certificate, and the timestamps from that certificate's embedded SCTs, returns the result of
* looking up the corresponding entry in the currently-saved CRLite filter (if any).
* Given the DER-encoded issuer subject public key info, the bytes of the
* value of the serial number (so, not including the DER tag and length) of a
* certificate, and the timestamps from that certificate's embedded SCTs,
* returns the result of looking up the corresponding entry in the
* currently-saved CRLite filter (if any).
* Returns
* - STATE_ENFORCE if the lookup indicates the certificate is revoked via CRLite,
* - STATE_UNSET if the lookup indicates the certificate is not revoked via CRLite,
@@ -175,28 +155,10 @@ interface nsICertStorage : nsISupports {
* No lookup is performed in the STATE_NOT_ENROLLED and STATE_NOT_COVERED cases.
*/
[must_use, noscript]
short getCRLiteRevocationState(in Array<octet> issuer,
in Array<octet> issuerSPKI,
short getCRLiteRevocationState(in Array<octet> issuerSPKI,
in Array<octet> serialNumber,
in Array<nsICRLiteTimestamp> timestamps);
/**
* Given the contents of a CRLite incremental revocation update ("stash"), adds the revocation
* information to the current set of stashed revocations. The basic unit of the stash file is an
* issuer subject public key info hash (sha-256) followed by a number of serial numbers
* corresponding to revoked certificates issued by that issuer. More specifically, each unit
* consists of:
* 4 bytes little-endian: the number of serial numbers following the issuer spki hash
* 1 byte: the length of the issuer spki hash
* issuer spki hash length bytes: the issuer spki hash
* as many times as the indicated serial numbers:
* 1 byte: the length of the serial number
* serial number length bytes: the serial number
* The stash file consists of any number of these units concatenated together.
*/
[must_use]
void addCRLiteStash(in Array<octet> stash, in nsICertStorageCallback callback);
/**
* Add a new CRLite filter for consideration in revocation checks. This
* filter is treated as a delta update to the current full filter. Calling
@@ -208,13 +170,10 @@ interface nsICertStorage : nsISupports {
void addCRLiteDelta(in Array<octet> delta, in ACString filename, in nsICertStorageCallback callback);
/**
* Given a DER-encoded issuer subject public key info and the bytes of the value of the serial
* number (so, not including the DER tag and length), determines if the certificate identified by
* this issuer SPKI and serial number is revoked according to the current set of stashed CRLite
* revocation information.
* Mark CRLite filters as fresh. For use in tests only.
*/
[must_use, noscript]
boolean isCertRevokedByStash(in Array<octet> issuerSPKI, in Array<octet> serialNumber);
[must_use]
void testNoteCRLiteUpdateTime(in nsICertStorageCallback callback);
/**
* Trust flags to use when adding a adding a certificate.

View File

@@ -1,103 +0,0 @@
// -*- 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/.
// Helper file for tests that initialize CRLite with corrupted `security_state`
// files.
//
// Usage:
// Define nsILocalFile variables for the `crlite.filter`, `crlite.coverage`,
// 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`.
//
// Example:
// let filter = do_get_file("some_test_dir/crlite.filter");
// let coverage = undefined;
// let enrollment = do_get_file("some_test_dir/crlite.enrollment");
// load("./corrupted_crlite_helper.js");
//
// Note:
// The cert_storage library only attempts to read security_state once. So
// this task can only be included once per test file.
"use strict";
/* eslint-disable no-undef */
add_task(async function test_crlite_corrupted() {
let securityStateDirectory = do_get_profile();
securityStateDirectory.append("security_state");
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
if (coverage != undefined) {
coverage.copyTo(securityStateDirectory, "crlite.coverage");
}
if (enrollment != undefined) {
enrollment.copyTo(securityStateDirectory, "crlite.enrollment");
}
if (filter != undefined) {
filter.copyTo(securityStateDirectory, "crlite.filter");
}
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
Ci.nsICertStorage
);
// This certificate is revoked according to `test_crlite_filters/20201017-0-filter`.
// 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");
// The issuer's certificate needs to be available for path building.
let issuerCert = constructCertFromFile("test_crlite_filters/issuer.pem");
ok(issuerCert, "issuer certificate should decode successfully");
// If we copied a corrupted file to security_state, then CRLite should not be
// initialized, and we should fall back to OCSP. By setting
// Ci.nsIX509CertDB.FLAG_LOCAL_ONLY here we skip the OCSP test, so there's no
// revocation checking, and the revoked certificate should pass inspection.
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
undefined,
"us-datarecovery.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// We should not have a filter or a stash.
let hasFilter = await new Promise(resolve => {
certStorage.hasPriorData(
Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_FULL,
(rv, result) => {
Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
resolve(result);
}
);
});
Assert.equal(hasFilter, false, "CRLite should not have a filter");
let hasStash = await new Promise(resolve => {
certStorage.hasPriorData(
Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_INCREMENTAL,
(rv, result) => {
Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
resolve(result);
}
);
});
Assert.equal(hasStash, false, "CRLite should not have a stash");
});

View File

@@ -241,177 +241,3 @@ add_task(async function test_batched_removal() {
);
Assert.equal(storedCerts.length, 0, "shouldn't have any certificates now");
});
class CRLiteCoverage {
constructor(ctLogID, minTimestamp, maxTimestamp) {
this.b64LogID = ctLogID;
this.minTimestamp = minTimestamp;
this.maxTimestamp = maxTimestamp;
}
}
CRLiteCoverage.prototype.QueryInterface = ChromeUtils.generateQI([
"nsICRLiteCoverage",
]);
add_task(async function test_crlite_filter() {
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
addCertFromFile(
certdb,
"test_cert_storage_direct/valid-cert-issuer.pem",
",,"
);
let validCert = constructCertFromFile(
"test_cert_storage_direct/valid-cert.pem"
);
addCertFromFile(
certdb,
"test_cert_storage_direct/revoked-cert-issuer.pem",
",,"
);
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, enrollment, coverage, resolve);
});
Assert.equal(
setFullCRLiteFilterResult,
Cr.NS_OK,
"setFullCRLiteFilter should succeed"
);
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"skynew.jp",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"schunk-group.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// Now replace the filter with one that covers the "valid" and "revoked"
// certificates. CRLite should flag the revoked certificate.
coverage.push(
new CRLiteCoverage(
"pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
0,
1641612275000
)
);
// 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, enrollment, coverage, resolve);
});
Assert.equal(
setFullCRLiteFilterResult,
Cr.NS_OK,
"setFullCRLiteFilter should succeed"
);
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"skynew.jp",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
SEC_ERROR_REVOKED_CERTIFICATE,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"schunk-group.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// If we're only collecting telemetry, none of the certificates should appear to be revoked.
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeTelemetryOnlyPrefValue
);
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"skynew.jp",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"schunk-group.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// If CRLite is disabled, none of the certificates should appear to be revoked.
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeDisabledPrefValue
);
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"skynew.jp",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"schunk-group.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
});

View File

@@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEoDCCA4igAwIBAgIQBpaPlkroI1bHThfCtTZbADANBgkqhkiG9w0BAQsFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTE3MTEwNjEyMjI1N1oXDTI3MTEwNjEyMjI1N1owXzEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTEeMBwGA1UEAxMVVGhhd3RlIEVWIFJTQSBDQSAyMDE4MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0Cu52zmdJFnSezXMKvL0rso
WgA/1X7OxjMQHsAllID1eDG836ptJXSTPg+DoEenHfkKyw++wXobgahr0cU/2v8R
WR3fID53ZDhEGHzS+Ol7V+HRtZG5teMWCY7gldtBQH0r7xUEp/3ISVsZUVBqtUmL
VJlf9nxJD6Cxp4LBlcJJ8+N6kSkV+fA+WdQc0HYhXSg3PxJP7XSU28Wc7gf6y9kZ
zQhK4WrZLRrHHbHC2QXdqQYUxR927QV+UCNXnlbTcZy2QpxWTPLzK+/cKXX4cwP6
MGF7+8RnUgHlij/5V2k/tIF9ep4B72ucqaS/UhEPpIN/T7A3OAw995yrB38glQID
AQABo4IBSTCCAUUwHQYDVR0OBBYEFOcB/AwWGMp9sozshyejb2GBO4Q5MB8GA1Ud
IwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0
BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0
LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v
RGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2MDQwMgYE
VR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
MA0GCSqGSIb3DQEBCwUAA4IBAQAWGka+5ffLpfFuzT+WlwDRwhyTZSunnvecZWZT
PPKXipynjpXx5dK8YG+2XoH74285GR1UABuvHMFV94XeDET9Pzz5s/NHS1/eAr5e
GdwfBl80XwPkwXaYqzRtw6J4RAxeLqcbibhUQv9Iev9QcP0kNPyJu413Xov76mSu
JlGThKzcurJPive2eLmwmoIgTPH11N/IIO9nHLVe8KTkt+FGgZCOWHA3kbFBZR39
Mn2hFS974rhUkM+VS9KbCiQQ5OwkfbZ/6BINkE1CMtiESZ2WkbxJKPsF3dN7p9DF
YWiQSbYjFP+rCT0/MkaHHYUkEvLNPgyJ6z29eMf0DjLu/SXJ
-----END CERTIFICATE-----

View File

@@ -1,41 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIHOzCCBiOgAwIBAgIQBi31aKBRMQgg1+xDJ+G6/TANBgkqhkiG9w0BAQsFADBf
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMR4wHAYDVQQDExVUaGF3dGUgRVYgUlNBIENBIDIwMTgw
HhcNMTgwNTI4MDAwMDAwWhcNMjAwNTIxMTIwMDAwWjCB6zEdMBsGA1UEDwwUUHJp
dmF0ZSBPcmdhbml6YXRpb24xEzARBgsrBgEEAYI3PAIBAxMCREUxFjAUBgsrBgEE
AYI3PAIBAhMFSGVzc2UxGDAWBgsrBgEEAYI3PAIBAQwHR2llw59lbjERMA8GA1UE
BRMISFJCIDY5MDIxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIZXNzZW4xFDASBgNV
BAcTC0hldWNoZWxoZWltMRQwEgYDVQQKEwtTY2h1bmsgR21iSDELMAkGA1UECxMC
SVQxGTAXBgNVBAMTEHNjaHVuay1ncm91cC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCvkuQZz2ExPv9paJb622OOk+o4bWnjDe1zHGK6qnK25mMT
Zldk74sXF+Wfr9lbwqHTcjGhQFwmVDqvtr55KVX8FOv0CSqNaewOrnNrFz8Xg4rn
OlIs3+MmqD5CIK+el0rA+xltEY8WvNlwZKG7yeJYrdsr+5DAThDuwCVe8bU7it4h
sjsMsof5ocee9zDkFThNVGR4sMk5EgBxb1Gt4n9wXUj4OBT78whhlkLH/pVZrrhs
tQwC3q90MOPC5RJcEolSCNjGdHCKRbexmRqJgbJj/qZ9JT+fQ+Ko6a+UAWvc2BUc
POnzGV2GzCdFFGOubJb6RjU0nuPG4Lmdc/BuS9kFAgMBAAGjggNkMIIDYDAfBgNV
HSMEGDAWgBTnAfwMFhjKfbKM7Icno29hgTuEOTAdBgNVHQ4EFgQUNb1SY8Bkil98
tD8zoxE30jBA1NIwZwYDVR0RBGAwXoIQc2NodW5rLWdyb3VwLmNvbYIUd3d3LnNj
aHVuay1ncm91cC5jb22CG3NjaHVuay1jYXJib250ZWNobm9sb2d5LmNvbYIXc2No
dW5rLXNpbnRlcm1ldGFscy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG
CCsGAQUFBwMBBggrBgEFBQcDAjA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY2Rw
LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3JsMEsGA1UdIAREMEIwNwYJ
YIZIAYb9bAIBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv
bS9DUFMwBwYFZ4EMAQEwcQYIKwYBBQUHAQEEZTBjMCQGCCsGAQUFBzABhhhodHRw
Oi8vc3RhdHVzLnRoYXd0ZS5jb20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9jYWNlcnRz
LnRoYXd0ZS5jb20vVGhhd3RlRVZSU0FDQTIwMTguY3J0MAkGA1UdEwQCMAAwggF7
BgorBgEEAdZ5AgQCBIIBawSCAWcBZQB1AKS5CZC0GFgUh7sTosxncAo8NZgE+Rvf
uON3zQ7IDdwQAAABY6Ze9XAAAAQDAEYwRAIgNUeXL3GwlpGQtTS/wKBlOkHJvHR5
knSop0OPumeCfQECIEdxY7qr/WRVbWkQFvP48fgWkZHkd4vTq70Y0aaSZTbPAHUA
VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFjpl71tQAABAMARjBE
AiAtxKdc/wum3TE7r9BoRd/gkrjYLWyqeLuL/opRBRy9xwIgPF6uEZxyhEoLZ+9G
AFBAP+X89zjZphVALjIXu0RRea4AdQC72d+8H4pxtZOUI5eqkntHOFeVCqtS6BqQ
lmQ2jh7RhQAAAWOmXvY1AAAEAwBGMEQCIHc04ERlUbIkVrlC+I89C9xtugvRCwbR
a7qZzSdqHltUAiBRVwTacf1dnO9AgLSgrxft5LV32DvH3qNT7pWYh8dFNjANBgkq
hkiG9w0BAQsFAAOCAQEARJ+tbnM+yS6chgpyzfB3e7IWPq2Den46Ja1H6/4qaKrd
nsbElcvd4cCQf1zYY6jlQkO6qtfMUChKrEar5aqqnyX8x/8T9PkpHp8XyUxgGlmT
hrnHML0gDJFS8O4MB5pFnGkgoOQa+OIQokWCXr4/a4AwsTG3Ms+lC+R+vRYz90lg
TEJLNHB2fSvQyvpXDUL9aAjACBp/9pKxfM9iq06MFO5jP483xJUfdqtVteHMw75w
1mb8IrM9R1dP47GsblTrf2rZYdaoxdyLjtJQG2aaOdU5unE6QeFrXbz0qeTPePs8
ftuXSW9xb053HjAkCcVo48j07b2cHfU1hxzGGptVbQ==
-----END CERTIFICATE-----

View File

@@ -1,27 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEkDCCA3igAwIBAgIJIrmxUyPlrv3NMA0GCSqGSIb3DQEBCwUAMF0xCzAJBgNV
BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw
JQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTIwHhcNMTgwODIy
MDczMjI0WhcNMjgwODIyMDczMjI0WjBQMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVz
dCBEViBDQTUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnTNi5Kgrt
FL8qBuEmpL2gvLFY7f9MEgjzClvic/45ebM+DxZ2CMuqtMtImgf8XPIpLaFFbozx
3VgqH41cmGHbpAoDRKpwfF1f53peHYhRxpOVgcnsiVCPZJPBPCUM9St+cuEjfo0d
YGbr3aG5urdT2zeKIFyxKbggdkU0LVRHwvLFsIpXCn/YK/8Rmx87yW9VB80OXkzf
IQoZop83+aebq1VwzjNCN3u4bWSFLYDyJGqE40WlZ53NZh+TwBsa6gld9YXPGQfx
k8x38zkFXberlMQOYhX9KyuTOMdlFkbx6LfIUqVKJavpcr54+XPzVyeroNPpKxtZ
mEqUYiFjAqUVAgMBAAGjggFeMIIBWjAdBgNVHQ4EFgQUT4twz6lAHJbllF13rNZv
TS2b8ncwHwYDVR0jBBgwFoAUCoWpd2UFmHxAgfgPlyw48QrsPM8wEgYDVR0TAQH/
BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwSQYDVR0fBEIwQDA+oDygOoY4aHR0
cDovL3JlcG9zaXRvcnkuc2Vjb210cnVzdC5uZXQvU0MtUm9vdDIvU0NSb290MkNS
TC5jcmwwUgYDVR0gBEswSTBHBgoqgwiMmxtkhwUEMDkwNwYIKwYBBQUHAgEWK2h0
dHBzOi8vcmVwb3NpdG9yeS5zZWNvbXRydXN0Lm5ldC9TQy1Sb290Mi8wQAYIKwYB
BQUHAQEENDAyMDAGCCsGAQUFBzABhiRodHRwOi8vc2Nyb290Y2EyLm9jc3Auc2Vj
b210cnVzdC5uZXQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQAD
ggEBABEDSrrhhR+Js5q45yih2Ne4cMLZmrH0AZwU3eM+7HZplzi1EhppgvcYk/2k
LM9haQGWnAZ5wiixLqKu7WlWrHgblZbXyCxALmMBK1rqeP0omxXExqKVqWNHU8KZ
t3jahH1wDYSzfetM7guWR+PAPpb9oQCtAx8DVyI/3Ocswvti/uWb517Bdo6Nd0+9
mf0LiphNKcSzSFX0s1Cb47cJROYHGBe2J6NUSWR7wE0asPtKsznGyNO+NJCUR+0h
OLN2cA2KJwPhZjYJt8UkucAF/EE7qC0Fc8B9Q/gttQ52en5BZxdkDrHCi4qnsSvi
gueQme/RzYkEaQlNT1WCZ9AIgVE=
-----END CERTIFICATE-----

View File

@@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF4DCCBMigAwIBAgIQC3d196+a5UJlyc0yVxB3jjANBgkqhkiG9w0BAQsFADBQ
MQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4s
TFRELjEaMBgGA1UEAxMRQ3Jvc3NUcnVzdCBEViBDQTUwHhcNMTkwNjExMDUyMjEy
WhcNMjEwNjMwMTQ1OTU5WjAUMRIwEAYDVQQDEwlza3luZXcuanAwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeciw7C297026HA4oIwc29vL2h29GVrRF7
HGdeXzAJA7kh+qwo8rTBFfdX7sgHy6nnE1+flEtFt91Ss8i3BZMEqoFUZFb1jGXd
DbQtmIWxz7O5skkjR1gdKwt9GImy1hEPt8dwU52mwVsSUEKvlZlsjeofUPAEbnYY
+iA/nYaYXiXyCxJzk6Y09VlzghyMIhkLwDa7rL3S9FgUQI6tSUwsiNYNoQzlYgXF
yPfQfd57LbBwZJtqVPC6rjPOZZd0sw7uvrDNuxnAM2k2mzlML9Vwt8EvSlZX60xD
oGQCsQQ/ZgjEQTA8WRZ+fxW/LqQNYYm70KU/1M+e8o4MKmA9xkH5AgMBAAGjggLw
MIIC7DAfBgNVHSMEGDAWgBRPi3DPqUAcluWUXXes1m9NLZvydzA8BggrBgEFBQcB
AQQwMC4wLAYIKwYBBQUHMAGGIGh0dHA6Ly9kdmNhNS5vY3NwLnNlY29tdHJ1c3Qu
bmV0MCMGA1UdEQQcMBqCCXNreW5ldy5qcIINd3d3LnNreW5ldy5qcDBaBgNVHSAE
UzBRMEUGCiqDCIybG26BVQIwNzA1BggrBgEFBQcCARYpaHR0cHM6Ly9yZXBvMS5z
ZWNvbXRydXN0Lm5ldC9zcHBjYS94dGR2NS8wCAYGZ4EMAQIBMBMGA1UdJQQMMAoG
CCsGAQUFBwMBMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9yZXBvMS5zZWNvbXRy
dXN0Lm5ldC9zcHBjYS94dGR2NS9mdWxsY3JsLmNybDAdBgNVHQ4EFgQUuj9305tQ
JIeVAQtsz9JHx3PTqaQwDgYDVR0PAQH/BAQDAgWgMIIBfgYKKwYBBAHWeQIEAgSC
AW4EggFqAWgAdgCkuQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWtF
Bb6pAAAEAwBHMEUCIHUQkmFzUh01r1Px/zWMZSL21dNNQwM+rN1z0gutxV3JAiEA
jSb2/GAm4+2qiNWDtx1EkHsMXjNW+5S4GhJePexjJR8AdgDuS723dc5guuFCaR+r
4Z5mow9+X7By2IMAxHuJeqj9ywAAAWtFBcdeAAAEAwBHMEUCIDVHdfP9wnVgz45l
eX80DpRCRNEV/OCDwfW+B0g/dveYAiEArbpLQb5Z9hul3r00kF2LrivNuI7kwEBy
MpkYsLtSPJoAdgBvU3asMfAxGdiZAKRRFf93FRwR2QLBACkGjbIImjfZEwAAAWtF
BdCQAAAEAwBHMEUCIE2GUo6x3qDrIhacnCmjikBCHF2yT6Fv5GAehZB569YCAiEA
vXwMXV8+y3xNFys+A6u9EjKiy8CTKv+SQxqsJ4s6jK0wDQYJKoZIhvcNAQELBQAD
ggEBAAzlm9W+N5fviTJ9wDsc5nXKYur3744V/cm75+8dUM61Rko1isK6IZt5aNPN
wOfhBsTzHHSYmAFMR9Xjoq8iDYZtIk01IGI6LEWuls9F2hVcERiHMWJOLTiH35xN
vRNTG0AbBdIpTX2sURsoCPJ+8DTnVUr3pTzXnIY4EQ4UXfANuYwceOHShF6UJo/L
PK0uRdHcd5SmMa03gFUdkTc9gU6PIEO/UgubazGh9xDBHtHECeleL+gpSfOP3SkF
7W1RgmbE6WJdVPlto7FRQtl2xIzHs/gNaPezqNKPHgFlx4c+ECTjPLqoW8LdeXu+
N8dueJg1+h+lQifkmgl23DqEIiI=
-----END CERTIFICATE-----

View File

@@ -1,83 +0,0 @@
/* -*- 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/. */
"use strict";
// This file tests that cert_storage correctly persists its information across
// runs of the browser specifically in the case of CRLite.
// (The test DB files for this test were created by running the test
// `test_cert_storage_direct.js` and copying them from that test's profile
// directory.)
/* eslint-disable no-unused-vars */
add_task(async function () {
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
let dbDirectory = do_get_profile();
dbDirectory.append("security_state");
let crliteFile = do_get_file(
"test_cert_storage_preexisting_crlite/crlite.filter"
);
crliteFile.copyTo(dbDirectory, "crlite.filter");
let coverageFile = do_get_file(
"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
);
// Add an empty stash to ensure the filter is considered to be fresh.
await new Promise(resolve => {
certStorage.addCRLiteStash(new Uint8Array([]), (rv, _) => {
Assert.equal(rv, Cr.NS_OK, "marked filter as fresh");
resolve();
});
});
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
let validCertIssuer = constructCertFromFile(
"test_cert_storage_direct/valid-cert-issuer.pem"
);
let validCert = constructCertFromFile(
"test_cert_storage_direct/valid-cert.pem"
);
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-10-28T00:00:00Z").getTime() / 1000,
false,
"skynew.jp",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
let revokedCertIssuer = constructCertFromFile(
"test_cert_storage_direct/revoked-cert-issuer.pem"
);
let revokedCert = constructCertFromFile(
"test_cert_storage_direct/revoked-cert.pem"
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
SEC_ERROR_REVOKED_CERTIFICATE,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2019-11-04T00:00:00Z").getTime() / 1000,
false,
"schunk-group.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
});

View File

@@ -1 +0,0 @@
3)<29><11><0E><08>:<3A>f<EFBFBD><66>v<1E><> 0<><13><><14><>y<>Q<EFBFBD><51><EFBFBD><EFBFBD>'<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jf<14><><EFBFBD><EFBFBD><EFBFBD>@(,v<17>~;<3B>P<EFBFBD>;ҧm<D2A7><6D>ځb<DA81>g<>t<EFBFBD>(M<>T<><54>NTbk<62>ڠ$

View File

@@ -1 +0,0 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@@ -1,17 +0,0 @@
// -*- 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 coverage file. Specifically, this handles the case
// where the coverage file is missing.
"use strict";
/* eslint-disable no-unused-vars */
let coverage = undefined;
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");

View File

@@ -1,17 +0,0 @@
// -*- 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 coverage file. Specifically, this handles the case
// where the coverage file is truncated in a LogID field.
"use strict";
/* 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/crlite.enrollment");
let filter = do_get_file("test_crlite_filters/20201017-0-filter");
load("./corrupted_crlite_helper.js");

View File

@@ -1,19 +0,0 @@
// -*- 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 coverage file. Specifically, this handles the case
// where the coverage file is truncated in a MinTimestamp field.
"use strict";
/* eslint-disable no-unused-vars */
let coverage = do_get_file(
"test_crlite_corrupted/trunc-min-timestamp.coverage"
);
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");

View File

@@ -1,19 +0,0 @@
// -*- 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 coverage file. Specifically, this handles the case
// where the coverage file is truncated in a MaxTimestamp field.
"use strict";
/* eslint-disable no-unused-vars */
let coverage = do_get_file(
"test_crlite_corrupted/trunc-max-timestamp.coverage"
);
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");

View File

@@ -1,17 +0,0 @@
// -*- 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 coverage file. Specifically, this handles the case
// where the coverage file's version is not recognized.
"use strict";
/* eslint-disable no-unused-vars */
let coverage = do_get_file("test_crlite_corrupted/version-0.coverage");
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");

View File

@@ -1,19 +0,0 @@
// -*- 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");

View File

@@ -1,17 +0,0 @@
// -*- 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");

View File

@@ -1,21 +0,0 @@
// -*- 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 filter file.
//
// There are many ways that a filter file could be corrupted, but the parsing
// is done in rust-cascade, not cert_storage, so it is sufficient for us to
// test any form of corruption here. For simplicity we just try to load a
// single \x00 byte as the filter.
"use strict";
/* eslint-disable no-unused-vars */
let coverage = do_get_file("test_crlite_preexisting/crlite.coverage");
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");

View File

@@ -49,7 +49,6 @@
// "MinEntry": 0
// }]
//
// $ rust-create-cascade --filter-type cascade --known ./known/ --revoked ./revoked --outdir ./cascade
// $ rust-create-cascade --filter-type clubcard --ct-logs-json ./ct-logs.json --known ./known/ --revoked ./revoked --outdir ./clubcard
//
// Additional revoked certificates were then added to the /known/ and /revoked/
@@ -118,28 +117,14 @@ function getHash(aStr) {
// Get the name of the file in the test directory to serve as the attachment
// for the given filter.
function getFilenameForFilter(filter) {
if (filter.type == "clubcard") {
return "20201017-0-clubcard-filter";
}
if (filter.type == "cascade") {
if (filter.type == "filter") {
return "20201017-0-filter";
}
if (filter.id == "0001") {
return "20201017-1-filter.stash";
}
if (filter.id == "1000") {
return "20201017-1-filter.delta";
}
if (filter.id == "2000") {
return "20201201-3-filter.delta";
}
// The addition of another stash file was written more than a month after
// other parts of this test. As such, the second stash file for October 17th,
// 2020 was not readily available. Since the structure of stash files don't
// depend on each other, though, any two stash files are compatible, and so
// this stash from December 1st is used instead.
return "20201201-3-filter.stash";
}
/**
* Simulate a Remote Settings synchronization by filling up the local data with
@@ -217,7 +202,7 @@ add_task(async function test_crlite_filters_disabled() {
let result = await syncAndDownload([
{
timestamp: "2019-01-01T00:00:00Z",
type: "cascade",
type: "filter",
id: "0000",
coverage: [
{
@@ -250,7 +235,7 @@ add_task(async function test_crlite_no_filters_in_channel() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
let result = await syncAndDownload(
[{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" }],
[{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" }],
true,
"other"
);
@@ -299,7 +284,7 @@ add_task(async function test_crlite_incremental_filters_with_wrong_parent() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
let result = await syncAndDownload([
{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" },
{
timestamp: "2019-01-01T06:00:00Z",
type: "diff",
@@ -320,7 +305,7 @@ add_task(async function test_crlite_incremental_filters_with_wrong_parent() {
},
]);
expectDownloads(result, [
"2019-01-01T00:00:00Z-cascade",
"2019-01-01T00:00:00Z-filter",
"2019-01-01T06:00:00Z-diff",
]);
@@ -331,7 +316,7 @@ add_task(async function test_crlite_incremental_filter_too_early() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
let result = await syncAndDownload([
{ timestamp: "2019-01-02T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-02T00:00:00Z", type: "filter", id: "0000" },
{
timestamp: "2019-01-01T00:00:00Z",
type: "diff",
@@ -341,7 +326,7 @@ add_task(async function test_crlite_incremental_filter_too_early() {
]);
equal(
result,
"finished;2019-01-02T00:00:00Z-cascade",
"finished;2019-01-02T00:00:00Z-filter",
"CRLite filter download should have run"
);
@@ -352,11 +337,11 @@ add_task(async function test_crlite_filters_basic() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
let result = await syncAndDownload([
{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" },
]);
equal(
result,
"finished;2019-01-01T00:00:00Z-cascade",
"finished;2019-01-01T00:00:00Z-filter",
"CRLite filter download should have run"
);
@@ -366,12 +351,12 @@ add_task(async function test_crlite_filters_basic() {
add_task(async function test_crlite_filters_not_cached() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
let filters = [
{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" },
];
let result = await syncAndDownload(filters);
equal(
result,
"finished;2019-01-01T00:00:00Z-cascade",
"finished;2019-01-01T00:00:00Z-filter",
"CRLite filter download should have run"
);
@@ -399,7 +384,7 @@ add_task(async function test_crlite_filters_full_and_incremental() {
id: "0001",
parent: "0000",
},
{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" },
{
timestamp: "2019-01-01T18:00:00Z",
type: "diff",
@@ -414,7 +399,7 @@ add_task(async function test_crlite_filters_full_and_incremental() {
},
]);
expectDownloads(result, [
"2019-01-01T00:00:00Z-cascade",
"2019-01-01T00:00:00Z-filter",
"2019-01-01T06:00:00Z-diff",
"2019-01-01T12:00:00Z-diff",
"2019-01-01T18:00:00Z-diff",
@@ -458,8 +443,8 @@ add_task(async function test_crlite_filters_multiple_days() {
id: "0013",
parent: "0012",
},
{ timestamp: "2019-01-02T00:00:00Z", type: "cascade", id: "0010" },
{ timestamp: "2019-01-03T00:00:00Z", type: "cascade", id: "0020" },
{ timestamp: "2019-01-02T00:00:00Z", type: "filter", id: "0010" },
{ timestamp: "2019-01-03T00:00:00Z", type: "filter", id: "0020" },
{
timestamp: "2019-01-01T06:00:00Z",
type: "diff",
@@ -478,7 +463,7 @@ add_task(async function test_crlite_filters_multiple_days() {
id: "0002",
parent: "0001",
},
{ timestamp: "2019-01-01T00:00:00Z", type: "cascade", id: "0000" },
{ timestamp: "2019-01-01T00:00:00Z", type: "filter", id: "0000" },
{
timestamp: "2019-01-03T06:00:00Z",
type: "diff",
@@ -487,7 +472,7 @@ add_task(async function test_crlite_filters_multiple_days() {
},
]);
expectDownloads(result, [
"2019-01-03T00:00:00Z-cascade",
"2019-01-03T00:00:00Z-filter",
"2019-01-03T06:00:00Z-diff",
"2019-01-03T12:00:00Z-diff",
"2019-01-03T18:00:00Z-diff",
@@ -513,7 +498,7 @@ add_task(async function test_crlite_confirm_revocations_mode() {
let result = await syncAndDownload([
{
timestamp: "2020-10-17T00:00:00Z",
type: "cascade",
type: "filter",
id: "0000",
coverage: [
{
@@ -532,7 +517,7 @@ add_task(async function test_crlite_confirm_revocations_mode() {
]);
equal(
result,
"finished;2020-10-17T00:00:00Z-cascade",
"finished;2020-10-17T00:00:00Z-filter",
"CRLite filter download should have run"
);
@@ -565,39 +550,10 @@ add_task(async function test_crlite_confirm_revocations_mode() {
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// Reload the filter w/o coverage and enrollment metadata.
result = await syncAndDownload([
{
timestamp: "2020-10-17T00:00:00Z",
type: "cascade",
id: "0000",
coverage: [],
enrolledIssuers: [],
},
]);
equal(
result,
"finished;2020-10-17T00:00:00Z-cascade",
"CRLite filter download should have run"
);
// OCSP will be consulted for the revoked certificate, but a soft-failure
// should now result in a Success return.
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
undefined,
"us-datarecovery.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await syncAndDownload([], true);
});
async function test_crlite_filters_and_check_revocation(filter_type) {
add_task(async function test_crlite_filters_and_check_revocation() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
Services.prefs.setIntPref(
"security.pki.crlite_mode",
@@ -614,32 +570,13 @@ async function test_crlite_filters_and_check_revocation(filter_type) {
let result = await syncAndDownload([
{
timestamp: "2020-10-17T00:00:00Z",
type: filter_type,
type: "filter",
id: "0000",
coverage:
filter_type == "clubcard"
? undefined
: [
{
logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=",
minTimestamp: 0,
maxTimestamp: 9999999999999,
},
{
logID: "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
minTimestamp: 0,
maxTimestamp: 9999999999999,
},
],
enrolledIssuers:
filter_type == "clubcard"
? undefined
: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID],
},
]);
equal(
result,
`finished;2020-10-17T00:00:00Z-${filter_type}`,
`finished;2020-10-17T00:00:00Z-filter`,
"CRLite filter download should have run"
);
@@ -692,7 +629,7 @@ async function test_crlite_filters_and_check_revocation(filter_type) {
{
timestamp: "2020-10-17T03:00:00Z",
type: "diff",
id: filter_type == "clubcard" ? "1000" : "0001",
id: "0001",
parent: "0000",
},
],
@@ -736,8 +673,8 @@ async function test_crlite_filters_and_check_revocation(filter_type) {
{
timestamp: "2020-10-17T06:00:00Z",
type: "diff",
id: filter_type == "clubcard" ? "2000" : "0002",
parent: filter_type == "clubcard" ? "1000" : "0001",
id: "0002",
parent: "0001",
},
],
false
@@ -840,123 +777,6 @@ async function test_crlite_filters_and_check_revocation(filter_type) {
Services.prefs.clearUserPref("security.OCSP.require");
Services.prefs.clearUserPref("security.OCSP.enabled");
// The revoked certificate example has one SCT from the log with ID "9ly...="
// at time 1598140096613 and another from the log with ID "XNx...=" at time
// 1598140096917. The filter we construct here fails to cover it by one
// millisecond in each case. The implementation will fall back to OCSP
// fetching. Since this would result in a crash and test failure, the
// Ci.nsIX509CertDB.FLAG_LOCAL_ONLY is used.
result = await syncAndDownload([
{
timestamp: "2020-10-17T00:00:00Z",
type: "cascade",
id: "0000",
coverage: [
{
logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=",
minTimestamp: 0,
maxTimestamp: 1598140096612,
},
{
logID: "XNxDkv7mq0VEsV6a1FbmEDf71fpH3KFzlLJe5vbHDso=",
minTimestamp: 1598140096917,
maxTimestamp: 9999999999999,
},
],
enrolledIssuers: [ISSUER_PEM_UID, NO_SCT_ISSUER_PEM_UID],
},
]);
equal(
result,
"finished;2020-10-17T00:00:00Z-cascade",
"CRLite filter download should have run"
);
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"us-datarecovery.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
await syncAndDownload([], true);
}
add_task(async function test_crlite_cascade_filter() {
await test_crlite_filters_and_check_revocation("cascade");
});
add_task(async function test_crlite_clubcard_filter() {
await test_crlite_filters_and_check_revocation("clubcard");
});
add_task(async function test_crlite_clubcard_bad_coverage_in_remote_settings() {
Services.prefs.setBoolPref(CRLITE_FILTERS_ENABLED_PREF, true);
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
Services.prefs.setBoolPref(INTERMEDIATES_ENABLED_PREF, true);
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
addCertFromFile(certdb, "test_crlite_filters/issuer.pem", ",,");
addCertFromFile(certdb, "test_crlite_filters/no-sct-issuer.pem", ",,");
let result = await syncAndDownload([
{
timestamp: "2020-10-17T00:00:00Z",
type: "clubcard",
id: "0000",
coverage: [
{
logID: "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=",
minTimestamp: 0,
maxTimestamp: 0,
},
{
logID: "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
minTimestamp: 0,
maxTimestamp: 0,
},
],
enrolledIssuers: [],
},
]);
equal(
result,
`finished;2020-10-17T00:00:00Z-clubcard`,
"CRLite filter download should have run"
);
let validCert = constructCertFromFile("test_crlite_filters/valid.pem");
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"vpn.worldofspeed.org",
0
);
let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem");
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
SEC_ERROR_REVOKED_CERTIFICATE,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"us-datarecovery.com",
0
);
await syncAndDownload([], true);
});
@@ -966,7 +786,7 @@ add_task(async function test_crlite_filters_avoid_reprocessing_filters() {
let result = await syncAndDownload([
{
timestamp: "2019-01-01T00:00:00Z",
type: "cascade",
type: "filter",
id: "0000",
coverage: [
{
@@ -997,7 +817,7 @@ add_task(async function test_crlite_filters_avoid_reprocessing_filters() {
},
]);
expectDownloads(result, [
"2019-01-01T00:00:00Z-cascade",
"2019-01-01T00:00:00Z-filter",
"2019-01-01T06:00:00Z-diff",
"2019-01-01T12:00:00Z-diff",
"2019-01-01T18:00:00Z-diff",
@@ -1038,7 +858,7 @@ add_task(
[
{
timestamp: "2019-01-01T00:00:00Z",
type: "cascade",
type: "filter",
id: "0000",
coverage: [
{
@@ -1060,7 +880,7 @@ add_task(
"specified"
);
expectDownloads(result, [
"2019-01-01T00:00:00Z-cascade",
"2019-01-01T00:00:00Z-filter",
"2019-01-01T06:00:00Z-diff",
]);
@@ -1070,7 +890,7 @@ add_task(
[
{
timestamp: "2020-01-01T00:00:00Z",
type: "cascade",
type: "filter",
id: "0002",
coverage: [
{
@@ -1099,7 +919,7 @@ add_task(
Services.prefs.setStringPref(CRLITE_FILTER_CHANNEL_PREF, "priority");
result = await syncAndDownload([], false);
expectDownloads(result, [
"2020-01-01T00:00:00Z-cascade",
"2020-01-01T00:00:00Z-filter",
"2020-01-01T06:00:00Z-diff",
]);
@@ -1108,7 +928,7 @@ add_task(
Services.prefs.setStringPref(CRLITE_FILTER_CHANNEL_PREF, "specified");
result = await syncAndDownload([], false);
expectDownloads(result, [
"2019-01-01T00:00:00Z-cascade",
"2019-01-01T00:00:00Z-filter",
"2019-01-01T06:00:00Z-diff",
]);

View File

@@ -8,12 +8,31 @@
"use strict";
add_task(async function test_preexisting_crlite_data() {
add_task(async function () {
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
let securityStateDirectory = do_get_profile();
securityStateDirectory.append("security_state");
// For simplicity, re-use the filters from test_crlite_filters.js.
do_get_file("test_crlite_filters/20201017-0-filter").copyTo(
securityStateDirectory,
"crlite.filter"
);
do_get_file("test_crlite_filters/20201017-1-filter.delta").copyTo(
securityStateDirectory,
"20201017-1-filter.delta"
);
do_get_file("test_crlite_filters/20201201-3-filter.delta").copyTo(
securityStateDirectory,
"20201201-3-filter.delta"
);
let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
Ci.nsICertStorage
);
@@ -21,65 +40,20 @@ add_task(async function test_preexisting_crlite_data() {
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
// These need to be available to be able to find them during path building
// for certificate verification.
// This needs to be available for path building.
let issuerCert = constructCertFromFile("test_crlite_filters/issuer.pem");
ok(issuerCert, "issuer certificate should decode successfully");
let noSCTCertIssuer = constructCertFromFile(
"test_crlite_filters/no-sct-issuer.pem"
);
ok(
noSCTCertIssuer,
"issuer certificate for certificate without SCTs should decode successfully"
);
let validCert = constructCertFromFile("test_crlite_filters/valid.pem");
let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem");
// We didn't load a data.bin file, so the filter is not considered fresh and
// we should get a "no filter" result. We later test that CRLite considers
// this cert to be revoked. So success here shows that CRLite is not
// consulted when the filter is stale.
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"us-datarecovery.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
// Add an empty stash to ensure the filter is considered to be fresh.
// Mark CRLite filter as fresh
await new Promise(resolve => {
certStorage.addCRLiteStash(new Uint8Array([]), (rv, _) => {
certStorage.testNoteCRLiteUpdateTime((rv, _) => {
Assert.equal(rv, Cr.NS_OK, "marked filter as fresh");
resolve();
});
});
// NB: by not specifying Ci.nsIX509CertDB.FLAG_LOCAL_ONLY, this tests that
// the implementation does not fall back to OCSP fetching, because if it
// did, the implementation would attempt to connect to a server outside the
// test infrastructure, which would result in a crash in the test
// environment, which would be treated as a test failure.
await checkCertErrorGenericAtTime(
certdb,
validCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"vpn.worldofspeed.org",
0
);
// NB: by not specifying Ci.nsIX509CertDB.FLAG_LOCAL_ONLY, this tests that
// the implementation does not fall back to OCSP fetching, because if it
// did, the implementation would attempt to connect to a server outside the
// test infrastructure, which would result in a crash in the test
// environment, which would be treated as a test failure.
let validCert = constructCertFromFile("test_crlite_filters/valid.pem");
await checkCertErrorGenericAtTime(
certdb,
validCert,
@@ -91,6 +65,7 @@ add_task(async function test_preexisting_crlite_data() {
0
);
let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem");
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
@@ -105,17 +80,6 @@ add_task(async function test_preexisting_crlite_data() {
let revokedInStashCert = constructCertFromFile(
"test_crlite_filters/revoked-in-stash.pem"
);
// The stash may not have loaded yet, so await a task that ensures the stash
// loading task has completed.
await new Promise(resolve => {
certStorage.hasPriorData(
Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_INCREMENTAL,
(rv, _) => {
Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
resolve();
}
);
});
await checkCertErrorGenericAtTime(
certdb,
revokedInStashCert,
@@ -140,69 +104,4 @@ add_task(async function test_preexisting_crlite_data() {
"icsreps.com",
0
);
// This certificate has no embedded SCTs, so it is not guaranteed to be in
// CT, so CRLite can't be guaranteed to give the correct answer, so it is
// not consulted, and the implementation falls back to OCSP. Since the real
// OCSP responder can't be reached, this results in a
// SEC_ERROR_OCSP_SERVER_ERROR.
let noSCTCert = constructCertFromFile("test_crlite_filters/no-sct.pem");
// NB: this will cause an OCSP request to be sent to localhost:80, but
// since an OCSP responder shouldn't be running on that port, this should
// fail safely.
Services.prefs.setCharPref("network.dns.localDomains", "ocsp.digicert.com");
Services.prefs.setBoolPref("security.OCSP.require", true);
Services.prefs.setIntPref("security.OCSP.enabled", 1);
await checkCertErrorGenericAtTime(
certdb,
noSCTCert,
SEC_ERROR_OCSP_SERVER_ERROR,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
false,
"mail233.messagelabs.com",
0
);
Services.prefs.clearUserPref("network.dns.localDomains");
Services.prefs.clearUserPref("security.OCSP.require");
Services.prefs.clearUserPref("security.OCSP.enabled");
let notCoveredCert = constructCertFromFile(
"test_crlite_filters/notcovered.pem"
);
await checkCertErrorGenericAtTime(
certdb,
notCoveredCert,
PRErrorCodeSuccess,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2022-01-07T00:00:00Z").getTime() / 1000,
false,
"peekaboophonics.com",
Ci.nsIX509CertDB.FLAG_LOCAL_ONLY
);
});
function run_test() {
let securityStateDirectory = do_get_profile();
securityStateDirectory.append("security_state");
// For simplicity, re-use the filter from test_crlite_filters.js.
let crilteFile = do_get_file("test_crlite_filters/20201017-0-filter");
crilteFile.copyTo(securityStateDirectory, "crlite.filter");
// This stash file and the following cert storage file were obtained by
// running just the task `test_crlite_filters_and_check_revocation` in
// test_crlite_filters.js, causing it to hang (by adding something like
// `add_test(() => {});`), and then copying the files from the temporary
// profile directory.
let stashFile = do_get_file("test_crlite_preexisting/crlite.stash");
stashFile.copyTo(securityStateDirectory, "crlite.stash");
let coverageFile = do_get_file("test_crlite_preexisting/crlite.coverage");
coverageFile.copyTo(securityStateDirectory, "crlite.coverage");
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();
}

View File

@@ -1 +0,0 @@
3)<29><11><0E><08>:<3A>f<EFBFBD><66>v<1E><> 0<><13><><14><>y<>Q<EFBFBD><51><EFBFBD><EFBFBD>'<27><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>jf<14><><EFBFBD><EFBFBD><EFBFBD>@(,v<17>~;<3B>P<EFBFBD>

View File

@@ -1,91 +0,0 @@
// -*- 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 stash file.
"use strict";
add_task(async function test_crlite_stash_corrupted() {
let securityStateDirectory = do_get_profile();
securityStateDirectory.append("security_state");
Services.prefs.setIntPref(
"security.pki.crlite_mode",
CRLiteModeEnforcePrefValue
);
let coverage = do_get_file("test_crlite_preexisting/crlite.coverage");
coverage.copyTo(securityStateDirectory, "crlite.coverage");
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");
let stash = do_get_file("test_crlite_corrupted/bad.stash");
stash.copyTo(securityStateDirectory, "crlite.stash");
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
let certStorage = Cc["@mozilla.org/security/certstorage;1"].getService(
Ci.nsICertStorage
);
// Add an empty stash to ensure the filter is considered to be fresh.
await new Promise(resolve => {
certStorage.addCRLiteStash(new Uint8Array([]), (rv, _) => {
Assert.equal(rv, Cr.NS_OK, "marked filter as fresh");
resolve();
});
});
// Await a task that ensures the stash loading task has completed.
await new Promise(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/crlite.enrollment`,
// and it is covered according to `test_crlite_preexisting/crlite.coverage`.
let revokedCert = constructCertFromFile("test_crlite_filters/revoked.pem");
// The issuer's certificate needs to be available for path building.
let issuerCert = constructCertFromFile("test_crlite_filters/issuer.pem");
ok(issuerCert, "issuer certificate should decode successfully");
// Loading the stash should not have caused any problems, and `revokedCert`
// should be marked as revoked.
await checkCertErrorGenericAtTime(
certdb,
revokedCert,
SEC_ERROR_REVOKED_CERTIFICATE,
Ci.nsIX509CertDB.verifyUsageTLSServer,
new Date("2020-10-20T00:00:00Z").getTime() / 1000,
undefined,
"us-datarecovery.com",
0
);
let hasFilter = await new Promise(resolve => {
certStorage.hasPriorData(
Ci.nsICertStorage.DATA_TYPE_CRLITE_FILTER_FULL,
(rv, result) => {
Assert.equal(rv, Cr.NS_OK, "hasPriorData should succeed");
resolve(result);
}
);
});
Assert.equal(hasFilter, true, "CRLite should have a filter");
});

View File

@@ -7,7 +7,6 @@ prefs = [
]
skip-if = ["os == 'win' && msix"] # https://bugzilla.mozilla.org/show_bug.cgi?id=1809477
support-files = [
"corrupted_crlite_helper.js",
"bad_certs/**",
"ocsp_certs/**",
"test_baseline_requirements/**",
@@ -19,17 +18,13 @@ support-files = [
"test_cert_overrides_read_only/**",
"test_cert_sha1/**",
"test_cert_signatures/**",
"test_cert_storage_direct/**",
"test_cert_storage_preexisting/**",
"test_cert_storage_preexisting_crlite/**",
"test_cert_trust/**",
"test_cert_utf8/**",
"test_cert_version/**",
"test_certDB_import/**",
"test_content_signing/**",
"test_crlite_filters/**",
"test_crlite_preexisting/**",
"test_crlite_corrupted/**",
"test_ct/**",
"test_delegated_credentials/**",
"test_encrypted_client_hello/**",
@@ -126,10 +121,6 @@ tags = "addons psm blocklist"
["test_cert_storage_preexisting.js"]
["test_cert_storage_preexisting_crlite.js"]
# This test cannot succeed on 32-bit platforms. See bugs 1546361 and 1548956.
skip-if = ["bits != 64"]
["test_cert_trust.js"]
["test_cert_utf8.js"]
@@ -143,29 +134,11 @@ skip-if = ["condprof"]
["test_content_signing.js"]
["test_crlite_coverage_missing.js"]
["test_crlite_coverage_trunc1.js"]
["test_crlite_coverage_trunc2.js"]
["test_crlite_coverage_trunc3.js"]
["test_crlite_coverage_version.js"]
["test_crlite_enrollment_trunc1.js"]
["test_crlite_enrollment_version.js"]
["test_crlite_filter_corrupted.js"]
["test_crlite_filters.js"]
tags = "remote-settings psm"
["test_crlite_preexisting.js"]
["test_crlite_stash_corrupted.js"]
["test_ct.js"]
# Requires hard-coded debug-only data
skip-if = ["!debug"]