Smart Byte (1 crate, ~4370 LOC, 82 tests)
smart-byte-oidc4vc OID4VCI 13 (issuer/AS metadata, pre-auth + auth-code
offers, deep-link encode/decode, token endpoint,
c_nonce + nonce endpoint, credential endpoint with
single + batch proofs + deferred + encryption hooks,
notification endpoint). OID4VP 23 (VpAuthRequest/
VpAuthResponse with DCQL + PE 2.0 +
presentation_definition_uri, client_id_scheme
constants, response_mode constants). SIOPv2 (request
+ ID-token with sub_jwk). DCQL (path walker, value
constraints, claim_sets, credential_sets).
Presentation Exchange 2.0 (definition + submission
validation). DPoP (RFC 9449 with htm/htu/iat/ath/
nonce + skew validator). Bitstring Status List 2021
+ IETF Token Status List (1/2/4/8-bit slots,
GZIP+base64url). HAIP + EUDI Wallet ARF profile
evaluators (PID + mDL vct/doctype constants).
This closes the planned ingest run for batches 4-7. The open-standards
monorepo now contains 22 net-new crates from this session across the
three workspaces.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
125 lines
3.7 KiB
Rust
125 lines
3.7 KiB
Rust
//! DCQL serialisation + matching tests.
|
|
|
|
use serde_json::json;
|
|
use smart_byte_oidc4vc::{
|
|
ClaimQuery, CredentialMeta, CredentialQuery, DcqlQuery, PathSegment,
|
|
evaluate_claim, evaluate_credential,
|
|
};
|
|
|
|
fn pid_credential() -> serde_json::Value {
|
|
json!({
|
|
"vct": "https://eu.europa.ec.eudi/pid/1",
|
|
"credentialSubject": {
|
|
"given_name": "Alice",
|
|
"family_name": "Citizen",
|
|
"birth_date": "1990-04-12"
|
|
}
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn query_serialises() {
|
|
let q = DcqlQuery {
|
|
credentials: vec![CredentialQuery {
|
|
id: "pid".into(),
|
|
format: "dc+sd-jwt".into(),
|
|
require: true,
|
|
meta: CredentialMeta {
|
|
vct_values: vec!["https://eu.europa.ec.eudi/pid/1".into()],
|
|
doctype_value: None,
|
|
type_values: vec![],
|
|
},
|
|
claims: vec![
|
|
ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("given_name".into()),
|
|
])
|
|
.with_id("c-gn"),
|
|
ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("family_name".into()),
|
|
])
|
|
.with_id("c-fn"),
|
|
],
|
|
claim_sets: vec![vec!["c-gn".into(), "c-fn".into()]],
|
|
}],
|
|
credential_sets: vec![],
|
|
};
|
|
q.validate().expect("query validates");
|
|
let j = serde_json::to_string(&q).unwrap();
|
|
let back: DcqlQuery = serde_json::from_str(&j).unwrap();
|
|
assert_eq!(back, q);
|
|
}
|
|
|
|
#[test]
|
|
fn evaluates_each_claim_path() {
|
|
let cred = pid_credential();
|
|
let gn = ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("given_name".into()),
|
|
]);
|
|
let v = evaluate_claim(&cred, &gn).expect("given_name resolves");
|
|
assert_eq!(v, &serde_json::Value::from("Alice"));
|
|
}
|
|
|
|
#[test]
|
|
fn rejects_unknown_path() {
|
|
let cred = pid_credential();
|
|
let q = ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("missing".into()),
|
|
]);
|
|
assert!(evaluate_claim(&cred, &q).is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn evaluates_credential_with_claim_set() {
|
|
let cred = pid_credential();
|
|
let q = CredentialQuery {
|
|
id: "pid".into(),
|
|
format: "dc+sd-jwt".into(),
|
|
require: true,
|
|
meta: CredentialMeta::default(),
|
|
claims: vec![
|
|
ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("given_name".into()),
|
|
])
|
|
.with_id("gn"),
|
|
ClaimQuery::path(vec![
|
|
PathSegment::Key("credentialSubject".into()),
|
|
PathSegment::Key("does_not_exist".into()),
|
|
])
|
|
.with_id("missing"),
|
|
],
|
|
claim_sets: vec![vec!["gn".into()]],
|
|
};
|
|
let matched = evaluate_credential(&cred, &q).expect("matches set");
|
|
assert!(matched.contains(&"gn".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_credential_ids_rejected() {
|
|
let q = DcqlQuery {
|
|
credentials: vec![
|
|
CredentialQuery {
|
|
id: "a".into(),
|
|
format: "dc+sd-jwt".into(),
|
|
require: true,
|
|
meta: CredentialMeta::default(),
|
|
claims: vec![],
|
|
claim_sets: vec![],
|
|
},
|
|
CredentialQuery {
|
|
id: "a".into(),
|
|
format: "dc+sd-jwt".into(),
|
|
require: true,
|
|
meta: CredentialMeta::default(),
|
|
claims: vec![],
|
|
claim_sets: vec![],
|
|
},
|
|
],
|
|
credential_sets: vec![],
|
|
};
|
|
assert!(q.validate().is_err());
|
|
}
|