Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ categories = ["data-structures", "encoding"]
base64 = "0.22.1"
ciborium = "0.2.0"
cose-rust = "0.1.2"
cmw = { git = "https://github.com/Xynnn007/rust-cmw.git", rev = "39b92ad20f855db95282b9d49cf7cf422b038888" }
hex = "0.4.3"
jsonwebtoken = { version = "10", features = ["aws_lc_rs"] }
lazy_static = "1.5.0"
Expand Down
58 changes: 31 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
An implementation of EAT Attestation Results token.

This crate provides an implementation of attestation results tokens that conforms to EAT
Attestation Results [draft-fv-rats-ear] specification. This defines a token intended to
Attestation Results [draft-ietf-rats-ear-04] specification. This defines a token intended to
communicate a set of appraisals of attested evidence produced by a verifier. Each appraisal is
based around a set of trust claims defined by Attestation Results for Secure Interactions
(AR4SI) [draft-ietf-rats-ar4si].

The attestation result may be serialized as a signed JSON or CBOR token (using JWT and COSE,
respectively).

[draft-fv-rats-ear]: https://datatracker.ietf.org/doc/draft-fv-rats-ear/
[draft-ietf-rats-ear-04]: https://datatracker.ietf.org/doc/draft-ietf-rats-ear/
[draft-ietf-rats-ar4si]: https://datatracker.ietf.org/doc/draft-ietf-rats-ar4si/

# Examples
Expand Down Expand Up @@ -39,6 +39,8 @@ fn main() {
},
raw_evidence: None,
nonce: None,
status: None,
topology: None,
submods: BTreeMap::from([("test".to_string(), Appraisal::new())]),
extensions: Extensions::new(),
};
Expand All @@ -62,7 +64,7 @@ const VERIF_KEY: &str = r#"
"#;

fn main() {
let signed = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlYXRfcHJvZmlsZSI6InRlc3QiLCJpYXQiOjEsImVhci52ZXJpZmllci1pZCI6eyJkZXZlbG9wZXIiOiJodHRwczovL3ZlcmFpc29uLXByb2plY3Qub3JnIiwiYnVpbGQiOiJ2c3RzIDAuMC4xIn0sInN1Ym1vZHMiOnsidGVzdCI6eyJlYXIuc3RhdHVzIjoibm9uZSJ9fX0.G25v0j0NDQhSOcK3Jtfq5vqVxnoWuWf-Q0DCNkCwpyB03DGr25ZDJ3IDSAHVPZrr6TVMwj8RcGEzQnCrucem4Q";
let signed = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJlYXRfcHJvZmlsZSI6InRlc3QiLCJpYXQiOjEsImVhcl92ZXJpZmllcl9pZCI6eyJkZXZlbG9wZXIiOiJodHRwczovL3ZlcmFpc29uLXByb2plY3Qub3JnIiwiYnVpbGQiOiJ2c3RzIDAuMC4xIn0sInN1Ym1vZHMiOnsidGVzdCI6eyJlYXJfc3RhdHVzIjoibm9uZSJ9fX0.i20_BuOLXWi0CjD-ZzBVZNUv8vygk3rdeUAP6HwvSVXy0Svp1BYU8YH67H3bLAh9OcwTwiC_Rvg1fydSllSUgw";

let token = Ear::from_jwt_jwk(signed, Algorithm::ES256, VERIF_KEY.as_bytes()).unwrap();
println!("EAR profiles: {}", token.profile);
Expand All @@ -75,44 +77,44 @@ EAR supports extension at top level (i.e. within the [`Ear`] struct), and also w
[`Appraisal`]s. An extension is an additional field definition. Extensions can be defined by
registering them with the `extensions` field of the corresponding struct. When registering an
extension, you must provide a string name (used in JSON), an integer key (used in CBOR), and an
[`ExtensionKind`] indicating which [`ExtensionValue`]s are valid.
[`RawValueKind`] indicating which [`RawValue`]s are valid.

## Registering individual extensions

Extensions can be registered directly with the corresponding struct's `extensions` field. Once
they have been registered, their values can be set and queried

```rust
use ear::{Ear, Appraisal, ExtensionKind, ExtensionValue};
use ear::{Ear, Appraisal, RawValueKind, RawValue};

let mut ear = Ear::new();
ear.extensions.register("ext.company-name", -65537, ExtensionKind::String).unwrap();
ear.extensions.register("ext.company-name", -65537, RawValueKind::String).unwrap();

let mut appraisal = Appraisal::new();
// extensions for Ear's and Appraisal's have their own namespaces, so it is
// to use the same key in both.
appraisal.extensions.register("ext.timestamp", -65537, ExtensionKind::Integer).unwrap();
appraisal.extensions.register("ext.timestamp", -65537, RawValueKind::Integer).unwrap();

ear.extensions.set_by_name(
"ext.company-name",
ExtensionValue::String("Acme Inc.".to_string()),
RawValue::String("Acme Inc.".to_string()),
).unwrap();

appraisal.extensions.set_by_key(
-65537,
ExtensionValue::Integer(1723534859),
RawValue::Integer(1723534859),
).unwrap();

ear.submods.insert("road-runner-trap".to_string(), appraisal);

assert_eq!(
ear.extensions.get_by_key(&-65537).unwrap(),
ExtensionValue::String("Acme Inc.".to_string()),
RawValue::String("Acme Inc.".to_string()),
);

assert_eq!(
ear.submods["road-runner-trap"].extensions.get_by_name("ext.timestamp").unwrap(),
ExtensionValue::Integer(1723534859),
RawValue::Integer(1723534859),
);
```

Expand All @@ -126,15 +128,15 @@ Sets of extensions can be associated together within [`Profile`]s. A [`Profile`]
registered, and can then be retrieved by its `id` when creating a new [`Ear`] or [`Appraisal`]

```rust
use ear::{Ear, Appraisal, ExtensionKind, ExtensionValue, Profile, register_profile};
use ear::{Ear, Appraisal, RawValueKind, RawValue, Profile, register_profile};

fn init_profile() {
let mut profile = Profile::new("tag:github.com,2023:veraison/ear#acme-profile");
let mut profile = Profile::new("tag:ietf.org,2026:rats/ear#04");

profile.register_ear_extension(
"ext.company-name", -65537, ExtensionKind::String).unwrap();
"ext.company-name", -65537, RawValueKind::String).unwrap();
profile.register_appraisal_extension(
"ext.timestamp", -65537, ExtensionKind::Integer).unwrap();
"ext.timestamp", -65537, RawValueKind::Integer).unwrap();

register_profile(&profile);
}
Expand All @@ -143,32 +145,32 @@ fn main() {
init_profile();

let mut ear = Ear::new_with_profile(
"tag:github.com,2023:veraison/ear#acme-profile").unwrap();
"tag:ietf.org,2026:rats/ear#04").unwrap();
// these will apply to all submods/appraisals within a profiled EAR
let mut appraisal = Appraisal::new_with_profile(
"tag:github.com,2023:veraison/ear#acme-profile").unwrap();
"tag:ietf.org,2026:rats/ear#04").unwrap();

ear.extensions.set_by_name(
"ext.company-name",
ExtensionValue::String("Acme Inc.".to_string()),
RawValue::String("Acme Inc.".to_string()),
).unwrap();

appraisal.extensions.set_by_key(
-65537,
ExtensionValue::Integer(1723534859),
RawValue::Integer(1723534859),
).unwrap();

ear.submods.insert("road-runner-trap".to_string(), appraisal);

assert_eq!(
ear.extensions.get_by_key(&-65537).unwrap(),
ExtensionValue::String("Acme Inc.".to_string()),
RawValue::String("Acme Inc.".to_string()),
);

assert_eq!(
ear.submods["road-runner-trap"]
.extensions.get_by_name("ext.timestamp").unwrap(),
ExtensionValue::Integer(1723534859),
RawValue::Integer(1723534859),
);
}

Expand All @@ -188,7 +190,7 @@ The following example shows how to include and then verify expiration time ("exp
inside an EAR.

```rust
use ear::{Ear, Algorithm, Appraisal, ExtensionKind, ExtensionValue};
use ear::{Ear, Algorithm, Appraisal, RawValueKind, RawValue};
use std::time::{SystemTime, Duration, UNIX_EPOCH};

const VERIF_KEY: &str = r#"
Expand All @@ -208,17 +210,17 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPp4XZRnRHSMhGg0t
";

let mut ear = Ear::new();
ear.profile = "tag:github.com,2023:veraison/ear#acme-profile".to_string();
ear.profile = "tag:ietf.org,2026:rats/ear#04".to_string();
ear.vid.build = "vsts 0.0.1".to_string();
ear.vid.developer = "https://veraison-project.org".to_string();
ear.submods.insert("road-runner-trap".to_string(), Appraisal::new());
ear.extensions.register("exp", 4, ExtensionKind::Integer).unwrap();
ear.extensions.register("exp", 4, RawValueKind::Integer).unwrap();

// expire 10 days from now
let exp = SystemTime::now().checked_add(Duration::from_secs(60*60*24*10)).unwrap()
.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64;

ear.extensions.set_by_name("exp", ExtensionValue::Integer(exp)).unwrap();
ear.extensions.set_by_name("exp", RawValue::Integer(exp)).unwrap();


let signed = ear
Expand All @@ -228,11 +230,11 @@ let signed = ear
let mut ear2 =
Ear::from_jwt_jwk(signed.as_str(), Algorithm::ES256, VERIF_KEY.as_bytes()).unwrap();

ear2.extensions.register("exp", 4, ExtensionKind::Integer).unwrap();
ear2.extensions.register("exp", 4, RawValueKind::Integer).unwrap();

// Verify the token has not expired.
let exp2 = match ear2.extensions.get_by_name("exp").unwrap() {
ExtensionValue::Integer(v) => Duration::from_secs(v as u64),
RawValue::Integer(v) => Duration::from_secs(v as u64),
_ => panic!(),
};
assert!(SystemTime::now().duration_since(UNIX_EPOCH).unwrap() < exp2);
Expand Down Expand Up @@ -271,6 +273,8 @@ fn main() {
},
raw_evidence: None,
nonce: None,
status: None,
topology: None,
submods: BTreeMap::from([("test".to_string(), Appraisal::new())]),
extensions: Extensions::new(),
};
Expand Down
3 changes: 2 additions & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ allow = [
"OpenSSL",
"Unicode-3.0",
"MPL-2.0",
"BSD-3-Clause"
"BSD-3-Clause",
"BlueOak-1.0.0",
]
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
Expand Down
19 changes: 11 additions & 8 deletions misc/claims.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
{
"ear.raw-evidence": "3q2-7w",
"ear_raw_evidence": [
"application/vnd.evidence",
"3q2-7w"
],
"iat": 1666091373,
"ear.verifier-id": {
"ear_verifier_id": {
"build": "rrtrap-v1.0.0",
"developer": "Acme Inc."
},
"eat_profile": "tag:github.com,2023:veraison/ear",
"eat_profile": "tag:ietf.org,2026:rats/ear#04",
"submods": {
"test": {
"ear.status": "affirming",
"ear.trustworthiness-vector": {
"ear_status": "affirming",
"ear_trustworthiness_vector": {
"instance-identity": 2,
"configuration": 2,
"executables": 3,
Expand All @@ -19,9 +22,9 @@
"storage-opaque": 2,
"sourced-data": 2
},
"ear.appraisal-policy-ids": [
"policy://psa/60a0068d"
"ear_appraisal_policy_ids": [
"https://veraison.example/policy/1/60a0068d"
]
}
}
}
}
Loading
Loading