Skip to content

Commit

Permalink
Merge pull request #14 from veraison/policy
Browse files Browse the repository at this point in the history
feat(keybroker-server): Use Rego to appraise EARs
  • Loading branch information
thomas-fossati authored Sep 26, 2024
2 parents f8bc788 + 9392a64 commit 6f96537
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 11 deletions.
4 changes: 4 additions & 0 deletions rust-keybroker/keybroker-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ clap = { version = "=4.3.24", features = ["derive", "std"] }
keybroker-common = { path = "../keybroker-common" }
veraison-apiclient = { git = "https://github.com/veraison/rust-apiclient.git" }
ear = { git = "https://github.com/veraison/rust-ear.git" }
regorus = "0.2.5"
serde_json = "1.0.128"
anyhow = "1.0.89"
phf = "0.11.2"
44 changes: 44 additions & 0 deletions rust-keybroker/keybroker-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Reference Values

## Arm CCA

The Arm CCA appraisal policy requires the user to provide one or more RIM values corresponding to known and trusted Realm workloads.

The reference values must be provided in a JSON file that conforms to the following CDDL grammar:

```cddl
start = {
"reference-values": [ + b64-rim ]
}
b64-rim = text .b64c rim
rim = bytes .size 32
```

Note that the RIM values are Base64-encoded and that there must be at least one.

### Example

The following contains reference values for three trusted workloads:

```json
{
"reference-values": [
"MRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=",
"q3N/r5ufZZUu+iAg0rlxl2ejC3HMRMUhk4wDUk1DPdY=",
"UvZTSVUJ6IZtdtK0GEa5nueYxDcEJDa2vNHYL6RhQbs="
]
}
```

### Mock mode

In "mock" mode, the keybroker server must be started using the following command line:

```sh
keybroker-server \
--mock-challenge \
--verbose \
--reference-values <(echo '{ "reference-values": [ "MRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=" ] }')
```
23 changes: 23 additions & 0 deletions rust-keybroker/keybroker-server/src/arm-cca.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package arm_cca

default allow := false

allow if {
input.eat_profile == "tag:github.com,2023:veraison/ear"

# platform part
prec := input.submods.CCA_SSD_PLATFORM
prec["ear.status"] == "affirming"

# realm part
rrec := input.submods.CCA_REALM
rrec["ear.status"] == "warning"

rtv := rrec["ear.trustworthiness-vector"]
rtv["instance-identity"] == 2

# check RIM value against known-good-values
rclaims := rrec["ear.veraison.annotated-evidence"]
rim := rclaims["cca-realm-initial-measurement"]
rim in data["reference-values"]
}
12 changes: 12 additions & 0 deletions rust-keybroker/keybroker-server/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ pub enum Error {
/// that are transacted through the API between the client and the server, if the client provides faulty data.
#[error(transparent)]
Base64Decode(#[from] base64::DecodeError),

/// Represents errors from the use of the policy evaluation library.
#[error(transparent)]
Policy(#[from] anyhow::Error),

/// Represents errors from the use of the JSON serialisation and deserialisation library.
#[error(transparent)]
Json(#[from] serde_json::Error),
}

/// Errors happening within the verification process logic.
Expand All @@ -45,6 +53,10 @@ pub enum VerificationErrorKind {
/// It was not possible to find the challenge-response newSession endpoint
#[error("No newChallengeResponseSession endpoint was found on the Veraison server.")]
NoChallengeResponseEndpoint,

/// It was not possible to find an appraisal policy for the evidence type
#[error("No appraisal policy was found for the evidence type.")]
PolicyNotFound,
}

/// Errors happening within the key store.
Expand Down
8 changes: 8 additions & 0 deletions rust-keybroker/keybroker-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use keystore::KeyStore;
mod challenge;
mod error;
mod keystore;
pub mod policy;
mod verifier;

#[post("/key/{keyid}")]
Expand Down Expand Up @@ -90,6 +91,8 @@ async fn submit_evidence(

let verifier_base = data.args.verifier.clone();

let reference_values = data.args.reference_values.clone();

// We are in an async context, but the verifier client is synchronous, so spawn
// it as a blocking task.
let handle = task::spawn_blocking(move || {
Expand All @@ -102,6 +105,7 @@ async fn submit_evidence(
content_type_str,
&challenge.challenge_value,
&evidence_bytes,
&reference_values,
)
});
let result = handle.await.unwrap();
Expand Down Expand Up @@ -167,6 +171,10 @@ struct Args {
/// Set the server verbosity
#[arg(short, long, default_value_t = false)]
verbose: bool,

/// File containing a JSON array with base64-encoded known-good RIM values
#[arg(long, default_value = "reference-values.json")]
reference_values: String,
}

struct ServerState {
Expand Down
84 changes: 84 additions & 0 deletions rust-keybroker/keybroker-server/src/policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

use crate::error::Result;
use phf::{phf_map, Map};
use regorus::{self, Value};

pub static MEDIATYPES_TO_POLICY: Map<&'static str, (&'static str, &'static str)> = phf_map! {
r#"application/eat-collection; profile="http://arm.com/CCA-SSD/1.0.0""# => ( include_str!("arm-cca.rego"), "data.arm_cca.allow" ),
// Other, future mappings
};

// Evaluate an EAR claims-set against the appraisal policy and known-good RIM values
pub(crate) fn rego_eval(
policy: &str,
policy_rule: &str,
reference_values: &str,
ear_claims: &str,
) -> Result<Value> {
// Create engine.
let mut engine = regorus::Engine::new();

engine.set_rego_v1(true);
engine.set_strict_builtin_errors(false);

// Add the appraisal policy
engine.add_policy(String::from("policy.rego"), String::from(policy))?;

// Load the configured known good RIM values
engine.add_data(Value::from_json_file(reference_values)?)?;

// Set the EAR claims-set to be appraised
engine.set_input(Value::from_json_str(ear_claims)?);

let results = engine.eval_rule(policy_rule.to_string())?;

Ok(results)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn rego_eval_ear_default_policy_ok() {
let ear_claims = include_str!("../../../testdata/ear-claims-ok.json");
let reference_values = stringify_testdata_path("rims-matching.json");

let results = rego_eval(
include_str!("arm-cca.rego"),
"data.arm_cca.allow",
&reference_values,
ear_claims,
)
.expect("successful eval");

assert_eq!(results.to_string(), "true");
}

#[test]
fn rego_eval_default_policy_unmatched_rim() {
let ear_claims = include_str!("../../../testdata/ear-claims-ok.json");
let reference_values = stringify_testdata_path("rims-not-matching.json");

let results = rego_eval(
include_str!("arm-cca.rego"),
"data.arm_cca.allow",
&reference_values,
ear_claims,
)
.expect("successful eval");

assert_eq!(results.to_string(), "false");
}

fn stringify_testdata_path(s: &str) -> String {
let mut test_data = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));

test_data.push("../../testdata");
test_data.push(s);

test_data.into_os_string().into_string().unwrap()
}
}
27 changes: 16 additions & 11 deletions rust-keybroker/keybroker-server/src/verifier.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// Copyright 2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

use crate::error::Result;
use ear::{Algorithm, Ear, TrustTier};
use crate::error::{Result, VerificationErrorKind};
use crate::policy;
use ear::{Algorithm, Ear};
use veraison_apiclient::*;

pub fn verify_with_veraison_instance(
verifier_base_url: &str,
media_type: &str,
challenge: &[u8],
evidence: &[u8],
reference_values: &str,
) -> Result<bool> {
// Get the discovery URL from the base URL
let discovery = Discovery::from_base_url(String::from(verifier_base_url))?;
Expand Down Expand Up @@ -60,14 +62,17 @@ pub fn verify_with_veraison_instance(
verification_key_string.as_bytes(),
)?;

// The simplest possible appraisal policy: accept if we have an AFFIRMING or WARNING result
// from every submodule.
// TODO: This policy is rather too "relaxed" - the simplest and strictest policy would be
// to require AFFIRMING from every submodule. We have some integration issues with Veraison
// today that prevent this.
let verified = ear.submods.iter().all(|(_module, appraisal)| {
appraisal.status == TrustTier::Affirming || appraisal.status == TrustTier::Warning
});
let ear_claims = serde_json::to_string(&ear)?;

Ok(verified)
let (policy, policy_rule) = policy::MEDIATYPES_TO_POLICY
.get(media_type)
.ok_or(VerificationErrorKind::PolicyNotFound)?;

// Appraise the received EAR using the embedded policy (see ./policy.rego)
// unless a custom one has been provided on the command line. The default
// policy also wants to match the RIM value reported by the CCA token with
// the known-good RIM values supplied on the command line.
let results = policy::rego_eval(policy, policy_rule, reference_values, &ear_claims)?;

Ok(results.to_string() == "true")
}
36 changes: 36 additions & 0 deletions testdata/ear-claims-ok.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"eat_nonce": "bobW2XzHE7xt1D285JGmtAMRwCeov4WjnaY-nORMEyqKEZ0pb65qaZnpvz5EcbDOASRdiJQkwx6JeTs7HWsVBA==",
"eat_profile": "tag:github.com,2023:veraison/ear",
"submods": {
"CCA_REALM": {
"ear.status": "warning",
"ear.trustworthiness-vector": {
"configuration": 0,
"executables": 33,
"file-system": 0,
"hardware": 0,
"instance-identity": 2,
"runtime-opaque": 0,
"sourced-data": 0,
"storage-opaque": 0
},
"ear.veraison.annotated-evidence": {
"cca-realm-challenge": "bobW2XzHE7xt1D285JGmtAMRwCeov4WjnaY+nORMEyqKEZ0pb65qaZnpvz5EcbDOASRdiJQkwx6JeTs7HWsVBA==",
"cca-realm-extensible-measurements": [
"JNWwopbMBcvYBoxQZ8W9Rzt3Ddpq4IL+O6MKvj+aarE=",
"eI/AkL/GuO2QMVK6hBTnPa9bjHux55rVAqsGmbZZ7RY=",
"2sRqWEFdw6ANenQYUgCOnK5k9S0DufdtdvSzZE/vxBY=",
"MsavxiflVYXAMVU1nzMaDiJfaEDblH3Zbvq4G+JnGTk="
],
"cca-realm-hash-algo-id": "sha-256",
"cca-realm-initial-measurement": "MRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=",
"cca-realm-personalization-value": "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIDEzIGxhenkgZG9ncy5UaGUgcXVpY2sgYnJvd24gZm94IA==",
"cca-realm-public-key": "BHb5iAkb5YXtQYAa7Pq4WFSMYwV+FrDmdhILvQ0vnCngVsXUGgEw65whUXiZ3CMUayjhsGK9PqSzFf0hnxy7Uoy250ykm+Fnc3NPYaHKYQMbK789kY8vlP/EIo5QkZVErg==",
"cca-realm-public-key-hash-algo-id": "sha-256"
}
},
"CCA_SSD_PLATFORM": {
"ear.status": "affirming"
}
}
}
6 changes: 6 additions & 0 deletions testdata/rims-matching.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"reference-values": [
"MRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=",
"q3N/r5ufZZUu+iAg0rlxl2ejC3HMRMUhk4wDUk1DPdY="
]
}
6 changes: 6 additions & 0 deletions testdata/rims-not-matching.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"reference-values": [
"XRMUq3NiA1DPdYg0rlxl2ejC3H/r5ufZZUu+hk4wDUk=",
"X3N/r5ufZZUu+iAg0rlxl2ejC3HMRMUhk4wDUk1DPdY="
]
}

0 comments on commit 6f96537

Please sign in to comment.