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