use std::collections::HashMap;
use bh_status_list::StatusClaim;
use rand::Rng;
use crate::{
models::{
data_retrieval::{
common::DocType,
device_retrieval::{issuer_auth::ValidityInfo, response::IssuerSigned},
Claims,
},
issue::IssuedDocument,
mdl::{MDL, MDL_DOCUMENT_TYPE, MDL_NAMESPACE},
},
DeviceKey, Result,
};
pub struct Issuer;
impl Issuer {
#[allow(clippy::too_many_arguments)]
pub fn issue<Signer: bh_jws_utils::Signer + bh_jws_utils::HasX5Chain, R: Rng + ?Sized>(
&self,
doc_type: DocType,
name_spaces: Claims,
device_key: DeviceKey,
signer: &Signer,
rng: &mut R,
validity_info: ValidityInfo,
status: Option<StatusClaim>,
) -> Result<IssuedDocument> {
let issuer_signed = IssuerSigned::new(
doc_type.clone(),
name_spaces,
device_key,
signer,
rng,
validity_info,
status,
)?;
Ok(IssuedDocument::new(doc_type, issuer_signed))
}
pub fn issue_mdl<Signer: bh_jws_utils::Signer + bh_jws_utils::HasX5Chain, R: Rng + ?Sized>(
&self,
mdl: MDL,
device_key: DeviceKey,
signer: &Signer,
rng: &mut R,
validity_info: ValidityInfo,
status: Option<StatusClaim>,
) -> Result<IssuedDocument> {
let mut name_spaces = HashMap::new();
name_spaces.insert(MDL_NAMESPACE.into(), mdl.into());
self.issue(
MDL_DOCUMENT_TYPE.into(),
Claims(name_spaces),
device_key,
signer,
rng,
validity_info,
status,
)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr as _;
use ciborium::into_writer;
use rand::thread_rng;
use super::*;
use crate::{
models::{
data_retrieval::common::{DataElementIdentifier, DataElementValue},
mdl::MDLMandatory,
FullDate,
},
utils::test::validity_info,
};
#[test]
fn test_issue() {
let mut rng = thread_rng();
let issuer_signer = crate::utils::test::issuer_signer();
let (_, device_key) = crate::utils::test::dummy_device_key();
let claims = Claims(HashMap::from([(
MDL_NAMESPACE.into(),
HashMap::<DataElementIdentifier, DataElementValue>::from([(
"name".into(),
"John".into(),
)]),
)]));
let issued = Issuer
.issue(
MDL_DOCUMENT_TYPE.into(),
claims,
device_key,
&issuer_signer,
&mut rng,
validity_info(100),
None,
)
.unwrap();
let mut encoded = Vec::new();
into_writer(&issued, &mut encoded).unwrap();
let encoded_hex = hex::encode(&encoded);
println!("{}", encoded_hex);
}
#[test]
fn test_issue_mdl() {
let mut rng = thread_rng();
let issuer_signer = crate::utils::test::issuer_signer();
let (_, device_key) = crate::utils::test::dummy_device_key();
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 issued = Issuer
.issue_mdl(
mdl,
device_key,
&issuer_signer,
&mut rng,
validity_info(100),
None,
)
.unwrap();
let mut encoded = Vec::new();
into_writer(&issued, &mut encoded).unwrap();
let encoded_hex = hex::encode(&encoded);
println!("{}", encoded_hex);
}
#[test]
fn test_issue_with_status() {
let mut rng = thread_rng();
let issuer_signer = crate::utils::test::issuer_signer();
let (_, device_key) = crate::utils::test::dummy_device_key();
let claims = Claims(HashMap::from([(
MDL_NAMESPACE.into(),
HashMap::<DataElementIdentifier, DataElementValue>::from([(
"name".into(),
"John".into(),
)]),
)]));
let issued = Issuer
.issue(
MDL_DOCUMENT_TYPE.into(),
claims,
device_key,
&issuer_signer,
&mut rng,
validity_info(100),
Some(StatusClaim::new(
"https://example.com/status-list".parse().unwrap(),
3,
)),
)
.unwrap();
let issuer_signed = issued.serialize_issuer_signed().unwrap();
let issuer_signed = IssuerSigned::from_base64_url(&issuer_signed).unwrap();
let status = issuer_signed.status().unwrap().unwrap();
assert_eq!(status.uri(), "https://example.com/status-list");
assert_eq!(status.idx(), 3);
}
}