Skip to content

Commit

Permalink
Refactor text reference values matching (#4965)
Browse files Browse the repository at this point in the history
Allow literal string comparison and  make regex optional
  • Loading branch information
k-naliuka authored Mar 27, 2024
1 parent 121a6b0 commit 579e92c
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 72 deletions.
17 changes: 9 additions & 8 deletions oak_attestation_integration_tests/tests/verifier_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ use oak_attestation::dice::evidence_to_proto;
use oak_attestation_verification::verifier::{to_attestation_results, verify, verify_dice_chain};
use oak_proto_rust::oak::attestation::v1::{
attestation_results::Status, binary_reference_value, endorsements,
kernel_binary_reference_value, reference_values, regex_reference_value,
kernel_binary_reference_value, reference_values, text_reference_value,
ApplicationLayerReferenceValues, BinaryReferenceValue, Endorsements, InsecureReferenceValues,
KernelBinaryReferenceValue, KernelLayerReferenceValues, OakRestrictedKernelEndorsements,
OakRestrictedKernelReferenceValues, ReferenceValues, RegexReferenceValue,
RootLayerEndorsements, RootLayerReferenceValues, SkipVerification,
OakRestrictedKernelReferenceValues, ReferenceValues, RootLayerEndorsements,
RootLayerReferenceValues, SkipVerification, TextReferenceValue,
};
use oak_restricted_kernel_sdk::attestation::EvidenceProvider;

Expand Down Expand Up @@ -78,11 +78,12 @@ fn verify_mock_evidence() {
SkipVerification {},
)),
}),
kernel_image: Some(skip.clone()),
kernel_setup_data: Some(skip.clone()),
kernel_cmd_line: Some(skip.clone()),
kernel_cmd_line_regex: Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Skip(SkipVerification {})),
kernel_image: None,
kernel_setup_data: None,
kernel_cmd_line: None,
kernel_cmd_line_regex: None,
kernel_cmd_line_text: Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::Skip(SkipVerification {})),
}),
init_ram_fs: Some(skip.clone()),
memory_map: Some(skip.clone()),
Expand Down
30 changes: 30 additions & 0 deletions oak_attestation_verification/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,36 @@ rust_library(
compile_data = [
"//oak_attestation_verification/data:ask_milan.pem",
],
deps = [
"//oak_dice",
"//oak_proto_rust",
"//oak_sev_snp_attestation_report",
"@oak_crates_index//:anyhow",
"@oak_crates_index//:base64",
"@oak_crates_index//:coset",
"@oak_crates_index//:ecdsa",
"@oak_crates_index//:getrandom",
"@oak_crates_index//:hex",
"@oak_crates_index//:p256",
"@oak_crates_index//:p384",
"@oak_crates_index//:prost",
"@oak_crates_index//:rsa",
"@oak_crates_index//:serde",
"@oak_crates_index//:serde_json",
"@oak_crates_index//:sha2",
"@oak_crates_index//:time",
"@oak_crates_index//:x509-cert",
"@oak_crates_index//:zerocopy",
],
)

rust_library(
name = "oak_attestation_verification_with_regex",
srcs = glob(["src/**"]),
compile_data = [
"//oak_attestation_verification/data:ask_milan.pem",
],
crate_features = ["regex"],
deps = [
"//oak_dice",
"//oak_proto_rust",
Expand Down
2 changes: 1 addition & 1 deletion oak_attestation_verification/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ p384 = { version = "0.13.0", default-features = false, features = [
prost = { workspace = true, default-features = false, features = [
"prost-derive",
] }
regex = { version = "*", default-features = false }
regex = { version = "*", default-features = false, optional = true }
rsa = { version = "0.9.6", default-features = false }
serde = { version = "*", default-features = false, features = ["derive"] }
serde_json = { version = "*", default-features = false, features = ["alloc"] }
Expand Down
72 changes: 47 additions & 25 deletions oak_attestation_verification/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ use oak_proto_rust::oak::{
attestation::v1::{
attestation_results::Status, binary_reference_value, endorsements,
extracted_evidence::EvidenceValues, kernel_binary_reference_value, reference_values,
regex_reference_value, root_layer_data::Report, AmdAttestationReport,
AmdSevReferenceValues, ApplicationKeys, ApplicationLayerData, ApplicationLayerEndorsements,
root_layer_data::Report, text_reference_value, AmdAttestationReport, AmdSevReferenceValues,
ApplicationKeys, ApplicationLayerData, ApplicationLayerEndorsements,
ApplicationLayerReferenceValues, AttestationResults, BinaryReferenceValue, CbData,
CbEndorsements, CbReferenceValues, ContainerLayerData, ContainerLayerEndorsements,
ContainerLayerReferenceValues, Endorsements, Evidence, ExtractedEvidence,
Expand All @@ -43,14 +43,15 @@ use oak_proto_rust::oak::{
KernelLayerEndorsements, KernelLayerReferenceValues, OakContainersData,
OakContainersEndorsements, OakContainersReferenceValues, OakRestrictedKernelData,
OakRestrictedKernelEndorsements, OakRestrictedKernelReferenceValues, ReferenceValues,
RegexReferenceValue, RootLayerData, RootLayerEndorsements, RootLayerEvidence,
RootLayerReferenceValues, SystemLayerData, SystemLayerEndorsements,
SystemLayerReferenceValues, TcbVersion, TeePlatform, TransparentReleaseEndorsement,
RootLayerData, RootLayerEndorsements, RootLayerEvidence, RootLayerReferenceValues,
SystemLayerData, SystemLayerEndorsements, SystemLayerReferenceValues, TcbVersion,
TeePlatform, TextReferenceValue, TransparentReleaseEndorsement,
},
HexDigest, RawDigest,
};
use oak_sev_snp_attestation_report::AttestationReport;
use prost::Message;
#[cfg(feature = "regex")]
use regex::Regex;
use x509_cert::{
der::{Decode, DecodePem},
Expand Down Expand Up @@ -518,27 +519,27 @@ fn verify_kernel_layer(
.context("kernel failed verification")?;

if let Some(kernel_raw_cmd_line) = values.kernel_raw_cmd_line.as_ref() {
verify_regex(
verify_text(
kernel_raw_cmd_line.as_str(),
reference_values
.kernel_cmd_line_regex
.kernel_cmd_line_text
.as_ref()
.context("no kernel command line regex reference values")?,
.context("no kernel command line text reference values")?,
)
.context("kernel command line failed verification")?;
} else {
// Support missing kernel_cmd_line_regex but only if the corresponding reference
// Support missing kernel_raw_cmd_line but only if the corresponding reference
// value is set to skip. This is a temporary workaround until all clients are
// migrated.
anyhow::ensure!(
matches!(
reference_values
.kernel_cmd_line_regex
.kernel_cmd_line_text
.as_ref()
.expect("no kernel command line regex reference values")
.expect("no kernel command line text reference values")
.r#type
.as_ref(),
Some(regex_reference_value::Type::Skip(_))
Some(text_reference_value::Type::Skip(_))
),
"No kernel_raw_cmd_line provided"
)
Expand Down Expand Up @@ -760,25 +761,46 @@ fn verify_hex_digests(actual: &HexDigest, expected: &HexDigest) -> anyhow::Resul
}
}

fn verify_regex(actual: &str, expected: &RegexReferenceValue) -> anyhow::Result<()> {
fn verify_text(actual: &str, expected: &TextReferenceValue) -> anyhow::Result<()> {
match expected.r#type.as_ref() {
Some(regex_reference_value::Type::Skip(_)) => Ok(()),
Some(regex_reference_value::Type::Regex(regex)) => {
let re = Regex::new(regex.value.as_str()).map_err(|msg| {
anyhow::anyhow!("Couldn't parse regex in the reference value: {msg}")
})?;
if re.is_match(actual) {
Ok(())
} else {
anyhow::bail!(format!(
"kernel cmd line doesn't match the reference value: {actual}"
))
Some(text_reference_value::Type::Skip(_)) => Ok(()),
Some(text_reference_value::Type::Regex(regex)) => verify_regex(actual, regex),
Some(text_reference_value::Type::StringLiterals(string_literals)) => {
anyhow::ensure!(!string_literals.value.is_empty());
for sl in string_literals.value.iter() {
if sl == actual {
return Ok(());
}
}
Err(anyhow::anyhow!(format!(
"value doesn't match the reference value string literal: {actual}"
)))
}
None => Err(anyhow::anyhow!("missing skip or value in the regex reference value")),
None => Err(anyhow::anyhow!("missing skip or value in the text reference value")),
}
}

#[cfg(feature = "regex")]
fn verify_regex(
actual: &str,
regex: &oak_proto_rust::oak::attestation::v1::Regex,
) -> anyhow::Result<()> {
let re = Regex::new(regex.value.as_str())
.map_err(|msg| anyhow::anyhow!("couldn't parse regex in the reference value: {msg}"))?;
Ok(anyhow::ensure!(
re.is_match(actual),
format!("value doesn't match the reference value regex: {actual}")
))
}

#[cfg(not(feature = "regex"))]
fn verify_regex(
_actual: &str,
_regex: &oak_proto_rust::oak::attestation::v1::Regex,
) -> anyhow::Result<()> {
Err(anyhow::anyhow!("verification of regex values not supported"))
}

struct ApplicationKeyValues {
encryption_public_key: Vec<u8>,
signing_public_key: Vec<u8>,
Expand Down
79 changes: 58 additions & 21 deletions oak_attestation_verification/tests/verifier_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ use oak_attestation_verification::{
use oak_proto_rust::oak::{
attestation::v1::{
attestation_results::Status, binary_reference_value, extracted_evidence::EvidenceValues,
kernel_binary_reference_value, reference_values, regex_reference_value,
root_layer_data::Report, AmdSevReferenceValues, ApplicationLayerEndorsements,
kernel_binary_reference_value, reference_values, root_layer_data::Report,
text_reference_value, AmdSevReferenceValues, ApplicationLayerEndorsements,
ApplicationLayerReferenceValues, BinaryReferenceValue, ContainerLayerEndorsements,
ContainerLayerReferenceValues, Digests, EndorsementReferenceValue, Endorsements, Evidence,
InsecureReferenceValues, KernelBinaryReferenceValue, KernelLayerEndorsements,
KernelLayerReferenceValues, OakContainersEndorsements, OakContainersReferenceValues,
OakRestrictedKernelEndorsements, OakRestrictedKernelReferenceValues, ReferenceValues,
Regex, RegexReferenceValue, RootLayerEndorsements, RootLayerReferenceValues,
SkipVerification, SystemLayerEndorsements, SystemLayerReferenceValues, TcbVersion,
Regex, RootLayerEndorsements, RootLayerReferenceValues, SkipVerification, StringLiterals,
SystemLayerEndorsements, SystemLayerReferenceValues, TcbVersion, TextReferenceValue,
TransparentReleaseEndorsement,
},
RawDigest,
Expand Down Expand Up @@ -167,11 +167,16 @@ fn create_containers_reference_values() -> ReferenceValues {
kernel: Some(KernelBinaryReferenceValue {
r#type: Some(kernel_binary_reference_value::Type::Skip(SkipVerification {})),
}),
kernel_image: Some(skip.clone()),
kernel_setup_data: Some(skip.clone()),
kernel_cmd_line: Some(skip.clone()),
kernel_cmd_line_regex: Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Regex(Regex { value: String::from("^.*$") })),
kernel_setup_data: None,
kernel_image: None,
kernel_cmd_line: None,
kernel_cmd_line_regex: None,
kernel_cmd_line_text: Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::StringLiterals(StringLiterals {
value: vec![String::from(
"console=ttyS0 panic=-1 earlycon=uart,io,0x3F8 brd.rd_nr=1 brd.rd_size=3072000 brd.max_part=1 ip=10.0.2.15:::255.255.255.0::eth0:off net.ifnames=0 quiet",
)],
})),
}),
init_ram_fs: Some(skip.clone()),
memory_map: Some(skip.clone()),
Expand Down Expand Up @@ -210,11 +215,14 @@ fn create_rk_reference_values() -> ReferenceValues {
kernel: Some(KernelBinaryReferenceValue {
r#type: Some(kernel_binary_reference_value::Type::Skip(SkipVerification {})),
}),
kernel_image: Some(skip.clone()),
kernel_setup_data: Some(skip.clone()),
kernel_cmd_line: Some(skip.clone()),
kernel_cmd_line_regex: Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Regex(Regex { value: String::from("^.*$") })),
kernel_setup_data: None,
kernel_image: None,
kernel_cmd_line: None,
kernel_cmd_line_regex: None,
kernel_cmd_line_text: Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::StringLiterals(StringLiterals {
value: vec![String::from("console=ttyS0")],
})),
}),
init_ram_fs: Some(skip.clone()),
memory_map: Some(skip.clone()),
Expand Down Expand Up @@ -498,8 +506,8 @@ fn verify_fails_with_non_matching_command_line_reference_value_set() {
let mut reference_values = create_rk_reference_values();
match reference_values.r#type.as_mut() {
Some(reference_values::Type::OakRestrictedKernel(rfs)) => {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_regex = Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Regex(Regex {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_text = Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::Regex(Regex {
value: String::from("this will fail"),
})),
});
Expand All @@ -519,14 +527,43 @@ fn verify_fails_with_non_matching_command_line_reference_value_set() {
}

#[test]
fn verify_succeeds_with_matching_command_line_reference_value_set() {
#[cfg(not(feature = "regex"))]
fn verify_fails_with_matching_command_line_reference_value_regex_set_and_regex_disabled() {
let evidence = create_rk_evidence();
let endorsements = create_rk_endorsements();
let mut reference_values = create_rk_reference_values();
match reference_values.r#type.as_mut() {
Some(reference_values::Type::OakRestrictedKernel(rfs)) => {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_text = Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::Regex(Regex {
value: String::from("^console=[a-zA-Z0-9]+$"),
})),
});
}
Some(_) => {}
None => {}
};

let r = verify(NOW_UTC_MILLIS, &evidence, &endorsements, &reference_values);
let p = to_attestation_results(&r);

eprintln!("======================================");
eprintln!("code={} reason={}", p.status as i32, p.reason);
eprintln!("======================================");
assert!(r.is_err());
assert!(p.status() == Status::GenericFailure);
}

#[test]
#[cfg(feature = "regex")]
fn verify_succeeds_with_matching_command_line_reference_value_regex_set_and_regex_enabled() {
let evidence = create_rk_evidence();
let endorsements = create_rk_endorsements();
let mut reference_values = create_rk_reference_values();
match reference_values.r#type.as_mut() {
Some(reference_values::Type::OakRestrictedKernel(rfs)) => {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_regex = Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Regex(Regex {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_text = Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::Regex(Regex {
value: String::from("^console=[a-zA-Z0-9]+$"),
})),
});
Expand Down Expand Up @@ -568,8 +605,8 @@ fn verify_succeeds_with_skip_command_line_reference_value_set_and_obsolete_evide
let mut reference_values = create_rk_reference_values();
match reference_values.r#type.as_mut() {
Some(reference_values::Type::OakRestrictedKernel(rfs)) => {
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_regex = Some(RegexReferenceValue {
r#type: Some(regex_reference_value::Type::Skip(SkipVerification {})),
rfs.kernel_layer.as_mut().unwrap().kernel_cmd_line_text = Some(TextReferenceValue {
r#type: Some(text_reference_value::Type::Skip(SkipVerification {})),
});
}
Some(_) => {}
Expand Down
1 change: 0 additions & 1 deletion oak_ml_transparency/runner/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 579e92c

Please sign in to comment.