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