Skip to content

Commit 6e0ef48

Browse files
authored
Return missing measurements if verify_measurements fails (#343)
If we fail to verify the measurements it's useful to know which measurements were missing from the corpus. Return this information.
1 parent d4b09bb commit 6e0ef48

5 files changed

Lines changed: 111 additions & 5 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

attest-data/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ sha3.workspace = true
1818
static_assertions.workspace = true
1919

2020
[features]
21+
testing = []
2122
default = ["std"]
2223
std = ["der/std", "der/derive", "der/oid", "getrandom", "hex", "rats-corim", "sha3/oid", "thiserror" ]

attest-data/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,16 @@ pub enum Measurement {
192192
Sha3_256(Sha3_256Digest),
193193
}
194194

195+
impl Measurement {
196+
// This is useful for unit tesitng purposes. The name here
197+
// is intentional to indicate that this is unchecked and if you
198+
// are using it anywhere besides unit tests something has gone wrong!
199+
#[cfg(any(test, feature = "testing"))]
200+
pub fn fake(bytes: [u8; 32]) -> Self {
201+
Measurement::Sha3_256(Sha3_256Digest::from(bytes))
202+
}
203+
}
204+
195205
impl Default for Measurement {
196206
fn default() -> Self {
197207
Measurement::Sha3_256(Sha3_256Digest::default())

verifier/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ attest-data = { path = "../attest-data", features = ["std"] }
1010
const-oid.workspace = true
1111
ed25519-dalek = { workspace = true, features = ["std"] }
1212
env_logger.workspace = true
13+
hex = { workspace = true }
1314
hubpack.workspace = true
1415
libipcc = { workspace = true, optional = true }
1516
log.workspace = true
@@ -20,6 +21,11 @@ tempfile.workspace = true
2021
thiserror.workspace = true
2122
x509-cert = { workspace = true, default-features = true }
2223

24+
25+
[dev-dependencies]
26+
attest-data = { path = "../attest-data", features = ["std", "testing"] }
27+
2328
[features]
29+
testing = []
2430
ipcc = ["libipcc"]
2531
mock = ["ed25519-dalek/pem"]

verifier/src/lib.rs

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ pub enum MeasurementSetError {
404404
/// This is a collection to represent the measurements received from an
405405
/// attestor. These measurements will come from the measurement log and the
406406
/// DiceTcbInfo extension(s) in the attestation cert chain / pki path.
407+
#[derive(Debug, PartialEq)]
407408
pub struct MeasurementSet(HashSet<Measurement>);
408409

409410
/// Construct a MeasurementSet from the provided artifacts. The
@@ -458,6 +459,22 @@ impl MeasurementSet {
458459
pub fn is_subset(&self, corpus: &ReferenceMeasurements) -> bool {
459460
self.0.is_subset(&corpus.0)
460461
}
462+
463+
/// Return the actual differences from the corpus, useful for debugging
464+
pub fn difference(
465+
&self,
466+
corpus: &ReferenceMeasurements,
467+
) -> Option<MeasurementSet> {
468+
if self.is_subset(corpus) {
469+
None
470+
} else {
471+
let mut measurements = HashSet::new();
472+
for measurement in self.0.difference(&corpus.0) {
473+
measurements.insert(*measurement);
474+
}
475+
Some(MeasurementSet(measurements))
476+
}
477+
}
461478
}
462479

463480
impl std::iter::IntoIterator for MeasurementSet {
@@ -602,8 +619,8 @@ pub fn verify_attestation(
602619
/// process.
603620
#[derive(Debug, Error)]
604621
pub enum VerifyMeasurementsError {
605-
#[error("Measurements are not a subset of reference measurements")]
606-
NotSubset,
622+
#[error("Measurements are not a subset of reference measurements: {0}")]
623+
NotSubset(MeasurementSet),
607624
}
608625

609626
/// This function implements the core of our attestation appraisal policy.
@@ -613,10 +630,13 @@ pub fn verify_measurements(
613630
measurements: &MeasurementSet,
614631
corpus: &ReferenceMeasurements,
615632
) -> Result<(), VerifyMeasurementsError> {
616-
if measurements.is_subset(corpus) {
617-
Ok(())
633+
// This should be equivallent to measurements.subset(corpus) but
634+
// give us the entries that are not in the corpus for debugging
635+
// purposes
636+
if let Some(diff) = measurements.difference(corpus) {
637+
Err(VerifyMeasurementsError::NotSubset(diff))
618638
} else {
619-
Err(VerifyMeasurementsError::NotSubset)
639+
Ok(())
620640
}
621641
}
622642

@@ -785,4 +805,72 @@ ezRrVF9+9OkCymi+xqWG8UN87sN/9Qk=
785805

786806
assert!(res.is_err());
787807
}
808+
809+
const MEASUREMENT_A: [u8; 32] = [0x1; 32];
810+
const MEASUREMENT_B: [u8; 32] = [0x2; 32];
811+
const MEASUREMENT_C: [u8; 32] = [0x3; 32];
812+
813+
#[test]
814+
fn basic_measurement_set_tests() {
815+
let measurement_a = Measurement::fake(MEASUREMENT_A);
816+
let measurement_b = Measurement::fake(MEASUREMENT_B);
817+
let measurement_c = Measurement::fake(MEASUREMENT_C);
818+
819+
let mut corpus = HashSet::new();
820+
821+
corpus.insert(measurement_a);
822+
corpus.insert(measurement_b);
823+
corpus.insert(measurement_c);
824+
825+
let corpus = ReferenceMeasurements(corpus);
826+
827+
let mut set_a = HashSet::new();
828+
set_a.insert(measurement_a);
829+
let set_a = MeasurementSet(set_a);
830+
831+
assert!(set_a.is_subset(&corpus));
832+
833+
let mut set_b = HashSet::new();
834+
set_b.insert(measurement_b);
835+
let set_b = MeasurementSet(set_b);
836+
837+
assert!(set_b.is_subset(&corpus));
838+
839+
let mut set_c = HashSet::new();
840+
set_c.insert(measurement_c);
841+
let set_c = MeasurementSet(set_c);
842+
843+
assert!(verify_measurements(&set_c, &corpus).is_ok());
844+
}
845+
846+
#[test]
847+
fn missing_measurement_set_tests() {
848+
let measurement_a = Measurement::fake(MEASUREMENT_A);
849+
let measurement_b = Measurement::fake(MEASUREMENT_B);
850+
let measurement_c = Measurement::fake(MEASUREMENT_C);
851+
852+
let mut corpus = HashSet::new();
853+
854+
corpus.insert(measurement_a);
855+
corpus.insert(measurement_b);
856+
857+
let corpus = ReferenceMeasurements(corpus);
858+
859+
let mut set_c = HashSet::new();
860+
set_c.insert(measurement_c);
861+
let set_c = MeasurementSet(set_c);
862+
863+
let mut other_c = HashSet::new();
864+
other_c.insert(measurement_c);
865+
let other_c = MeasurementSet(other_c);
866+
867+
match verify_measurements(&set_c, &corpus) {
868+
Ok(()) => panic!("expected an error"),
869+
Err(e) => match e {
870+
VerifyMeasurementsError::NotSubset(set) => {
871+
assert!(other_c == set)
872+
}
873+
},
874+
}
875+
}
788876
}

0 commit comments

Comments
 (0)