Skip to content

Commit

Permalink
rvps: change interface to get all reference values
Browse files Browse the repository at this point in the history
Previously we expected the caller of the RVPS to provide
a name for the reference value that they wanted.
In the AS we were flattening the TCB claims to get this name.
Ultimately, the names of the TCB claims do not map directly onto
the names of the required reference values.

This changes the interface to have the RVPS determine which
reference values to send. At the moment, it simply sends all of them.

This allows the reference values that are used to mostly be set within
the policy itself, which is probably a good idea.

In the future, the RVPS should be improved to include a context
abtraction that allows groups of reference values to be provided to the
AS.

Signed-off-by: Tobin Feldman-Fitzthum <[email protected]>
  • Loading branch information
fitzthum committed Oct 4, 2024
1 parent 171c133 commit 4c8cdfe
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 80 deletions.
18 changes: 5 additions & 13 deletions attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,16 +207,9 @@ impl AttestationService {
runtime_data_claims,
tee,
)?;

debug!("tcb_claims: {:#?}", tcb_claims);

// TODO: now that claims aren't flattened, this will not work.
// reference values do not map onto claim names anyway.
// change RVPS to provide all reference values for a given
// context.
let reference_data_map = self
.get_reference_data(tcb_claims.keys())
.await
let reference_data_map = self.rvps.get_digests().await
.map_err(|e| anyhow!("Generate reference data failed: {:?}", e))?;
debug!("reference_data_map: {:#?}", reference_data_map);

Expand All @@ -241,13 +234,12 @@ impl AttestationService {
Ok(attestation_results_token)
}

async fn get_reference_data<'a, I>(&self, tcb_claims: I) -> Result<HashMap<String, Vec<String>>>
where
I: Iterator<Item = &'a String>,
{
async fn get_reference_data<'a, I>(&self) -> Result<HashMap<String, Vec<String>>> {
let mut data = HashMap::new();
let reference_values = self.rvps.get_digests().await?;
for

for key in tcb_claims {
let reference_value = self.rvps.get_digests(key).await?;
if !reference_value.is_empty() {
debug!("Successfully get reference values of {key} from RVPS.");
}
Expand Down
4 changes: 1 addition & 3 deletions protos/reference.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ syntax = "proto3";

package reference;

message ReferenceValueQueryRequest {
string name = 1;
}
message ReferenceValueQueryRequest {}

message ReferenceValueQueryResponse {
string reference_value_results = 1;
Expand Down
12 changes: 3 additions & 9 deletions rvps/src/bin/rvps-tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@ async fn register(addr: &str, provenance_path: &str) -> Result<()> {
Ok(())
}

async fn query(addr: &str, name: &str) -> Result<()> {
async fn query(addr: &str) -> Result<()> {
let mut client = ReferenceValueProviderServiceClient::connect(addr.to_string()).await?;
let req = tonic::Request::new(ReferenceValueQueryRequest {
name: name.to_string(),
});
let req = tonic::Request::new(ReferenceValueQueryRequest {});

let rvs = client
.query_reference_value(req)
Expand Down Expand Up @@ -77,10 +75,6 @@ struct QueryArgs {
/// The address of target RVPS
#[arg(short, long, default_value = DEFAULT_ADDR)]
addr: String,

/// The name to query reference value
#[arg(short, long)]
name: String,
}

#[tokio::main]
Expand All @@ -100,6 +94,6 @@ async fn main() -> Result<()> {

match cli {
Cli::Register(para) => register(&para.addr, &para.path).await,
Cli::Query(para) => query(&para.addr, &para.name).await,
Cli::Query(para) => query(&para.addr).await,
}
}
13 changes: 4 additions & 9 deletions rvps/src/bin/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,16 @@ impl RVPSServer {
impl ReferenceValueProviderService for RVPSServer {
async fn query_reference_value(
&self,
request: Request<ReferenceValueQueryRequest>,
_request: Request<ReferenceValueQueryRequest>,
) -> Result<Response<ReferenceValueQueryResponse>, Status> {
let request = request.into_inner();

info!("query {}", request.name);

let rvs = self
.rvps
.lock()
.await
.get_digests(&request.name)
.get_digests()
.await
.map_err(|e| Status::aborted(format!("Query reference value: {e}")))?
.map(|rvs| rvs.hash_values)
.unwrap_or_default();
.map_err(|e| Status::aborted(format!("Query reference value: {e}")))?;

let reference_value_results = serde_json::to_string(&rvs)
.map_err(|e| Status::aborted(format!("Serde reference value: {e}")))?;
info!("Reference values: {}", reference_value_results);
Expand Down
9 changes: 5 additions & 4 deletions rvps/src/extractors/extractor_modules/sample/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub struct SampleExtractor;
const DEFAULT_ALG: &str = "sha384";

/// The reference value will be expired in the default time (months)
const DEFAULT_EXPIRED_TIME: u32 = 12;
const MONTHS_BEFORE_EXPIRATION: u32 = 12;

impl Extractor for SampleExtractor {
fn verify_and_extract(&self, provenance_base64: &str) -> Result<Vec<ReferenceValue>> {
Expand All @@ -54,12 +54,13 @@ impl Extractor for SampleExtractor {

let time = Utc::now()
.with_nanosecond(0)
.and_then(|t| t.checked_add_months(Months::new(DEFAULT_EXPIRED_TIME)));
.and_then(|t| t.checked_add_months(Months::new(MONTHS_BEFORE_EXPIRATION)));

match time {
Some(expired) => Some(ReferenceValue {
Some(expiration) => Some(ReferenceValue {
version: REFERENCE_VALUE_VERSION.into(),
name: name.to_string(),
expired,
expiration,
hash_value: rvs,
}),
None => {
Expand Down
43 changes: 19 additions & 24 deletions rvps/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
//

use anyhow::{bail, Context, Result};
use chrono::{DateTime, Utc};
use log::{info, warn};
use std::time::SystemTime;
use std::collections::HashMap;

use crate::{store::StoreType, Config};

use super::{
extractors::{Extractors, ExtractorsImpl},
pre_processor::{PreProcessor, PreProcessorAPI},
Message, Store, TrustedDigest, MESSAGE_VERSION,
Message, Store, MESSAGE_VERSION,
};

/// The core of the RVPS, s.t. componants except communication componants.
Expand Down Expand Up @@ -71,28 +70,24 @@ impl Core {
Ok(())
}

pub async fn get_digests(&self, name: &str) -> Result<Option<TrustedDigest>> {
let rv = self.store.get(name).await?;
match rv {
None => Ok(None),
Some(rv) => {
let now: DateTime<Utc> = DateTime::from(SystemTime::now());
if now > *rv.expired() {
warn!("Reference value of {} is expired.", name);
return Ok(None);
}

let hash_values = rv
.hash_values()
.iter()
.map(|pair| pair.value().to_owned())
.collect();

Ok(Some(TrustedDigest {
name: name.to_owned(),
hash_values,
}))
pub async fn get_digests(&self) -> Result<HashMap<String, Vec<String>>> {
let mut rv_map = HashMap::new();
let reference_values = self.store.get_values().await?;

for rv in reference_values {
if rv.expired() {
warn!("Reference value of {} is expired.", rv.name());
continue;
}

let hash_values = rv
.hash_values()
.iter()
.map(|pair| pair.value().to_owned())
.collect();

rv_map.insert(rv.name().to_string(), hash_values);
}
Ok(rv_map)
}
}
31 changes: 18 additions & 13 deletions rvps/src/reference_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use anyhow::{anyhow, Result};
use chrono::{DateTime, NaiveDateTime, Timelike, Utc};
use serde::{Deserialize, Deserializer, Serialize};
use std::time::SystemTime;

/// Default version of ReferenceValue
pub const REFERENCE_VALUE_VERSION: &str = "0.1.0";
Expand Down Expand Up @@ -53,7 +54,7 @@ fn primitive_date_time_from_str<'de, D: Deserializer<'de>>(
/// Here, ReferenceValue is stored inside RVPS. Its format MAY be modified.
/// * `version`: version of the reference value format.
/// * `name`: name of the artifact related to this reference value.
/// * `expired`: expired time for this reference value.
/// * `expiration`: Time after which refrence valid is invalid
/// * `hash_value`: A set of key-value pairs, each indicates a hash
/// algorithm and its relative hash value for the artifact.
/// The actual struct deliver from RVPS to AS is
Expand All @@ -65,7 +66,7 @@ pub struct ReferenceValue {
pub version: String,
pub name: String,
#[serde(deserialize_with = "primitive_date_time_from_str")]
pub expired: DateTime<Utc>,
pub expiration: DateTime<Utc>,
#[serde(rename = "hash-value")]
pub hash_value: Vec<HashValuePair>,
}
Expand All @@ -76,15 +77,15 @@ fn default_version() -> String {
}

impl ReferenceValue {
/// Create a new `ReferenceValue`, the `expired`
/// Create a new `ReferenceValue`, the `expiration`
/// field's nanosecond will be set to 0. This avoid
/// a rare bug that when the nanosecond of the time
/// is not 0, the test case will fail.
pub fn new() -> Result<Self> {
Ok(ReferenceValue {
version: REFERENCE_VALUE_VERSION.into(),
name: String::new(),
expired: Utc::now()
expiration: Utc::now()
.with_nanosecond(0)
.ok_or_else(|| anyhow!("set nanosecond failed."))?,
hash_value: Vec::new(),
Expand All @@ -103,14 +104,18 @@ impl ReferenceValue {
}

/// Set expired time of the ReferenceValue.
pub fn set_expired(mut self, expired: DateTime<Utc>) -> Self {
self.expired = expired.with_nanosecond(0).expect("Set nanosecond failed.");
pub fn set_expiration(mut self, expiration: DateTime<Utc>) -> Self {
self.expiration = expiration
.with_nanosecond(0)
.expect("Set nanosecond failed.");
self
}

/// Get expired of the ReferenceValue.
pub fn expired(&self) -> &DateTime<Utc> {
&self.expired
/// Check whether reference value is expired
pub fn expired(&self) -> bool {
let now: DateTime<Utc> = DateTime::from(SystemTime::now());

now > self.expiration
}

/// Set hash value of the ReferenceValue.
Expand Down Expand Up @@ -162,13 +167,13 @@ mod test {
.expect("create ReferenceValue failed.")
.set_version("1.0.0")
.set_name("artifact")
.set_expired(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())
.set_expiration(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())
.add_hash_value("sha512".into(), "123".into());

assert_eq!(rv.version(), "1.0.0");

let rv_json = json!({
"expired": "1970-01-01T00:00:00Z",
"expiration": "1970-01-01T00:00:00Z",
"name": "artifact",
"version": "1.0.0",
"hash-value": [{
Expand All @@ -187,12 +192,12 @@ mod test {
.expect("create ReferenceValue failed.")
.set_version("1.0.0")
.set_name("artifact")
.set_expired(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())
.set_expiration(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())
.add_hash_value("sha512".into(), "123".into());

assert_eq!(rv.version(), "1.0.0");
let rv_json = r#"{
"expired": "1970-01-01T00:00:00Z",
"expiration": "1970-01-01T00:00:00Z",
"name": "artifact",
"version": "1.0.0",
"hash-value": [{
Expand Down
10 changes: 10 additions & 0 deletions rvps/src/store/local_fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ impl Store for LocalFs {
None => Ok(None),
}
}

async fn get_values(&self) -> Result<Vec<ReferenceValue>> {
let mut values = Vec::new();

for (_k,v) in self.engine.iter().flatten() {
values.push(serde_json::from_slice(&v)?);
}

Ok(values)
}
}

#[cfg(test)]
Expand Down
7 changes: 7 additions & 0 deletions rvps/src/store/local_json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,11 @@ impl Store for LocalJson {
let rv = rvs.into_iter().find(|rv| rv.name == name);
Ok(rv)
}

async fn get_values(&self) -> Result<Vec<ReferenceValue>> {
let _ = self.lock.read().await;
let file = tokio::fs::read(&self.file_path).await?;
let rvs: Vec<ReferenceValue> = serde_json::from_slice(&file)?;
Ok(rvs)
}
}
10 changes: 5 additions & 5 deletions rvps/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ impl StoreType {
}

/// Interface of a `Store`.
/// We only provide a simple instance here which implements
/// Store. In more scenarios, RV should be stored in persistent
/// storage, like database, file and so on. All of the mentioned
/// forms will have the same interface as following.
/// Reference value storage facilities should implement this trait.
#[async_trait]
pub trait Store {
/// Store a reference value. If the given `name` exists,
/// return the previous `Some<ReferenceValue>`, otherwise return `None`
async fn set(&self, name: String, rv: ReferenceValue) -> Result<Option<ReferenceValue>>;

// Retrieve a reference value
// Retrieve reference value by name
async fn get(&self, name: &str) -> Result<Option<ReferenceValue>>;

// Retrieve reference values
async fn get_values(&self) -> Result<Vec<ReferenceValue>>;
}

0 comments on commit 4c8cdfe

Please sign in to comment.