use std::{collections::HashMap, str::FromStr};
use bh_jws_utils::{Es256Signer, Es256Verifier, SignerWithChain};
use bh_status_list::{StatusClaim, UriBuf};
use bhmdoc::{
models::{
data_retrieval::device_retrieval::issuer_auth::ValidityInfo,
mdl::{MDLMandatory, MDL},
DeviceRequest, DocRequest, FullDate,
},
Device, DeviceKey, Issuer, Verifier,
};
const INTERMEDIARY_KEY: &str = include_str!("certs/intermediary.key");
const INTERMEDIARY_CERT: &str = include_str!("certs/intermediary.crt");
const ROOT_CERT: &str = include_str!("certs/root.crt");
const MDL_DOCUMENT_TYPE: &str = "org.iso.18013.5.1.mDL";
const MDL_NAMESPACE: &str = "org.iso.18013.5.1";
fn main() {
let x5chain_builder =
bhx5chain::Builder::new(INTERMEDIARY_KEY, INTERMEDIARY_CERT, ROOT_CERT).unwrap();
let issuer_signer = Es256Signer::generate("issuer".to_owned()).unwrap();
let cert_chain = x5chain_builder
.generate_x5chain(&issuer_signer.public_key_pem().unwrap(), None)
.unwrap();
let issuer_signer = SignerWithChain::new(issuer_signer, cert_chain).unwrap();
let device_signer = Es256Signer::generate("example_kid".to_string()).unwrap();
let mdl_mandatory = MDLMandatory {
family_name: "Doe".to_owned(),
given_name: "John".to_owned(),
birth_date: "1980-01-02".parse().unwrap(),
issue_date: FullDate::from_str("2024-01-01").unwrap().into(),
expiry_date: FullDate::from_str("2029-01-01").unwrap().into(),
issuing_authority: "MUP".to_owned(),
issuing_country: "RH".to_owned(),
document_number: "1234".to_owned(),
portrait: vec![1u8, 2, 3].into(),
driving_privileges: 7,
un_distinguishing_sign: "sign".to_owned(),
};
let mdl = MDL::new(mdl_mandatory);
let device_jwk = device_signer.public_jwk().unwrap();
let device_key = DeviceKey::from_jwk(&device_jwk).unwrap();
let current_time = 100;
let status_list_uri: UriBuf = "https://issuer.com/status-list".parse().unwrap();
let status_list_idx = 532;
let issued_document = Issuer
.issue_mdl(
mdl,
device_key,
&issuer_signer,
&mut rand::thread_rng(),
ValidityInfo::new(
current_time.try_into().unwrap(),
current_time.try_into().unwrap(),
(current_time + 365 * 24 * 60 * 60).try_into().unwrap(),
None,
)
.unwrap(),
Some(StatusClaim::new(status_list_uri.clone(), status_list_idx)),
)
.unwrap();
let serialized_issued_document = issued_document.serialize_issuer_signed().unwrap();
let current_time = current_time + 10;
let device = Device::verify_issued(
&serialized_issued_document,
MDL_DOCUMENT_TYPE.into(),
current_time,
|_alg| Some(&Es256Verifier),
)
.unwrap();
let _ = device.validity_info().expect("Validity info should exist");
let doc_request = DocRequest::builder(MDL_DOCUMENT_TYPE.into())
.add_name_space(
MDL_NAMESPACE.into(),
HashMap::from([
("family_name".into(), false.into()),
("given_name".into(), false.into()),
("expiry_date".into(), false.into()),
]),
)
.build();
let device_request = DeviceRequest::new(vec![doc_request]);
let client_id = "example_client_id".to_owned();
let response_uri = "http://example_response_uri".to_owned();
let verifier = Verifier::new(
client_id.clone(),
response_uri.clone(),
&mut rand::thread_rng(),
);
let current_time = current_time + 10;
let device_response = device
.present(
current_time,
&device_request,
&client_id,
&response_uri,
verifier.nonce(),
None,
&device_signer,
)
.unwrap();
let current_time = current_time + 10;
let verified_claims = verifier
.verify(
device_response,
current_time,
None,
None,
|_alg| Some(&Es256Verifier),
)
.unwrap();
assert_eq!(verified_claims.len(), 1);
let claims = &verified_claims[0].claims.0;
let credential_status_pointer = &verified_claims[0].status;
let mdl_namespace_claims = claims.get(&MDL_NAMESPACE.into()).unwrap();
assert_eq!(mdl_namespace_claims.len(), 3);
assert!(mdl_namespace_claims.contains_key(&"family_name".into()));
assert!(mdl_namespace_claims.contains_key(&"given_name".into()));
assert!(mdl_namespace_claims.contains_key(&"expiry_date".into()));
assert_eq!(
credential_status_pointer,
&Some(StatusClaim::new(status_list_uri, status_list_idx))
);
println!("Claim: {:?}", claims);
}