isomdl 0.2.0

ISO mDL implementation in Rust
Documentation
//! This module contains the definitions for the device request functionality.
use crate::cose::MaybeTagged;
use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec, Tag24};
use coset::CoseSign1;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

pub type ItemsRequestBytes = Tag24<ItemsRequest>;
pub type ItemsRequestBytesAll = Vec<ItemsRequestBytes>;
pub type DeviceRequestInfoBytes = Tag24<DeviceRequestInfo>;
pub type DocType = String;
pub type NameSpace = String;
pub type IntentToRetain = bool;
pub type DataElementIdentifier = String;
pub type DataElements = NonEmptyMap<DataElementIdentifier, IntentToRetain>;
pub type Namespaces = NonEmptyMap<NameSpace, DataElements>;
pub type ReaderAuth = MaybeTagged<CoseSign1>;
pub type DocumentSet = NonEmptyVec<DocRequestID>;
pub type DocRequestID = u64;
pub type PurposeHints = NonEmptyMap<PurposeControllerId, PurposeCode>;
pub type PurposeControllerId = String;
pub type PurposeCode = i64;

/// Represents a device request.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceRequest {
    /// The version of the device request.
    pub version: String,

    /// A non-empty vector of document requests.
    pub doc_requests: NonEmptyVec<DocRequest>,

    /// An optional non-empty vector of reader authentication.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reader_auth_all: Option<NonEmptyVec<MaybeTagged<CoseSign1>>>,

    /// An optional device request information.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub device_request_info: Option<Tag24<DeviceRequestInfo>>,
}

/// Represents a document request.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DocRequest {
    /// The items request for the document.
    pub items_request: ItemsRequestBytes,

    /// The reader authentication, if provided.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reader_auth: Option<ReaderAuth>,
}

/// Represents a request for items.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ItemsRequest {
    /// The type of document.
    pub doc_type: DocType,

    /// The namespaces associated with the request.
    #[serde(rename = "nameSpaces")]
    pub namespaces: Namespaces,

    /// Additional information for the request.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub request_info: Option<BTreeMap<String, ciborium::Value>>,
}

/// Represents a use case.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UseCase {
    /// Whether this use case is required.
    pub mandatory: bool,

    /// Document requests that must comply with this use case.
    pub document_sets: NonEmptyVec<DocumentSet>,

    /// Option hints to help the user distinguish between use cases.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub purpose_hints: Option<PurposeHints>,
}

/// Represents additional information on the set of documents requested in the mdoc request.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeviceRequestInfo {
    /// A non-empty vector of use cases.
    pub use_cases: NonEmptyVec<UseCase>,
}

impl DeviceRequest {
    pub const VERSION: &'static str = "1.0";
}

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

    #[test]
    fn items_request() {
        const HEX: &str = "D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4";
        let bytes: Vec<u8> = hex::decode(HEX).unwrap();
        let req: Tag24<ItemsRequest> = crate::cbor::from_slice(&bytes).unwrap();
        let roundtripped = crate::cbor::to_vec(&req).unwrap();
        assert_eq!(bytes, roundtripped);
    }

    #[test]
    fn doc_request() {
        const HEX: &str = "A16C6974656D7352657175657374D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4";
        let bytes: Vec<u8> = hex::decode(HEX).unwrap();
        let req: DocRequest = crate::cbor::from_slice(&bytes).unwrap();
        let roundtripped = crate::cbor::to_vec(&req).unwrap();
        assert_eq!(bytes, roundtripped);
    }

    #[test]
    fn device_request() {
        const HEX: &str = "A26776657273696F6E63312E306B646F63526571756573747381A16C6974656D7352657175657374D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4";
        let bytes: Vec<u8> = hex::decode(HEX).unwrap();
        let req: DeviceRequest = crate::cbor::from_slice(&bytes).unwrap();
        let roundtripped = crate::cbor::to_vec(&req).unwrap();
        assert_eq!(bytes, roundtripped);
    }

    #[test]
    fn device_request_v11() {
        const HEX: &str = "a46776657273696f6e63312e316b646f63526571756573747381a16c6974656d7352657175657374d818586ea267646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6a6e616d65537061636573a1716f72672e69736f2e31383031332e352e31a468706f727472616974f56a676976656e5f6e616d65f56b6167655f6f7665725f3231f46b66616d696c795f6e616d65f56d72656164657241757468416c6c818443a10126a1182159017d30820179308201200209009dbe1a6d4dd57b70300a06082a8648ce3d0403023045311a3018060355040a0c1154657374204f7267616e697a6174696f6e3127302506035504030c1e5465737420526571756573742041757468656e7469636174696f6e204341301e170d3235303532373230343830305a170d3335303532353230343830305a3045311a3018060355040a0c1154657374204f7267616e697a6174696f6e3127302506035504030c1e5465737420526571756573742041757468656e7469636174696f6e2043413059301306072a8648ce3d020106082a8648ce3d030107034200048b7d4c6a005b6bb6e588bf0b3a6dab6d71354aa894975fbe904345505874c5d2b1dc31b1b976247de01776223fa565770608d14c45d3e0ad920502a09f8b8da3300a06082a8648ce3d0403020347003044022047c3dae3eebfc6f1e2e66ed22dc900d1099d8d585413379a9bf48792998ce00102207e47ee580a3b0d02d26148be0186f1cc92ec852e93ac0622c99cec98ed5fd7a6f65840a4285751d90bb408115e99aafd11872d7a5f6b3494610de68b99d70d8c3bdd1f72ab0fe0c8e483bf1a63c047922a41c4758b7befb5aef0eb24bf08ea0223da857164657669636552657175657374496e666fd8185827a168757365436173657381a2696d616e6461746f7279f56c646f63756d656e7453657473818100";
        let bytes: Vec<u8> = hex::decode(HEX).unwrap();
        let req: DeviceRequest = crate::cbor::from_slice(&bytes).unwrap();
        let roundtripped = crate::cbor::to_vec(&req).unwrap();
        assert_eq!(bytes, roundtripped);
    }
}