Bug 1949027 - remove support for legacy CRLite filter format. r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D238680
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -751,7 +751,6 @@ dependencies = [
|
||||
"nserror",
|
||||
"nsstring",
|
||||
"rkv",
|
||||
"rust_cascade",
|
||||
"sha2",
|
||||
"static_prefs",
|
||||
"storage_variant",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
×tamp_entries,
|
||||
);
|
||||
*state = ss.get_crlite_revocation_state(&*issuerSPKI, &*serialNumber, ×tamp_entries);
|
||||
NS_OK
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
Binary file not shown.
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
Binary file not shown.
@@ -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>ڠ$
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
@@ -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",
|
||||
]);
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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();
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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>
|
||||
Binary file not shown.
@@ -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");
|
||||
});
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user