Crate isomdl

Crate isomdl 

Source
Expand description

ISO/IEC DIS 18013-5 mDL implementation in Rust.

It is intended to be used in creating apps for Devices and Readers that can interact with each other to exchange mDL data.

§Simulated Device and Reader interaction

Here are examples of how to use the library. You can see more in examples folder and read about in the dedicated README.

This example demonstrates a simulated device and reader interaction. The reader requests the age_over_21 element, and the device responds with that value. The flow is something like this:

   +---------------------+                                          +----------------------+
   |                     |                                          |                      |
   |                     |                                          |                      |
   |   Device            |                                          |   Reader             |
   |                     |                                          |                      |
   |                     |                                          |                      |
   +---------+-----------+                                          +----------+-----------+
             |                                                                 |
         Initialize session                                                    |
             |                                                                 |
             |                                                                 |
Create QR code engagement                                                      |
             |                                                                 |
             +-------------+                                                   |
             |             |                                                   |
             |             |                                                   |
             <-------------+                                                   |
             |                                                                 |
             |                        Send QR code                             |
             +----------------------------------------------------------------->
             |                                                                 |
             |                                                                 | Establish session
             |                                                                 +-----------+
             |                                                                 |           |
             |                                                                 |           |
             |                                                                 +-----------+
             |                  Request age_over_21                            |
             <-----------------------------------------------------------------+
             |                                                                 |
             |                                                                 |
             |                                                                 |
             |                 Send age_over_21                                |
             +----------------------------------------------------------------->
             |                                                                 |
             |                                                                 |
             |                                                                 | Process age_over_21
             |                                                                 +-----------+
             |                                                                 |           |
             |                                                                 |           |
             |                                                                 +-----------+
             |                                                                 |
             |                                                                 |
             |                                                                 |
             |                    Session finished                             |
             |                                                                 |
  1. Device initialization and engagement:
    • The device creates a QR code containing DeviceEngagement data, which includes its public key.
    • Internally:
      • The device initializes with the mDL data, private key, and public key.
  2. Reader processing QR code and requesting the necessary fields:
    • The reader processes the QR code and creates a request for the age_over_21 element.
    • Internally:
      • Generates its private and public keys.
      • Initiates a key exchange, and generates the session keys.
      • The request is encrypted with the reader’s session key.
  3. Device accepting request and responding:
    • The device receives the request and creates a response with the age_over_21 element.
    • Internally:
      • Initiates the key exchange, and generates the session keys.
      • Decrypts the request with the reader’s session key.
      • Parse and validate it creating error response if needed.
      • The response is encrypted with the device’s session key.
  4. Reader Processing mDL data:
    • The reader processes the response and prints the value of the age_over_21 element.

§Device perspective

There are several states through which the device goes during the interaction:


                                                 +---------+
                                                 |         |
                                                 |         |
                                                 | User    |
                                                 |         |
                                                 |         |
                                                 +---+-----+
                                                     |
                                                     |
+----------------------------------------------------v--------------------------------------------------------------+
|                                              Device                                                               |
|                                                                                                                   |
|                                         +-------------------+                                                     |
|                                         |                   |                                                     |
|                                         |SessionManagerInit |                                                     |
|                                         |                   |                                                     |
|       +---------------------------------+                   +-------------+                                       |
|       |                                 |                   |             |                                       |
|       |                                 |                   |             |                                       |
|       |                                 +-------------------+             |                                       |
|       |                                                                   |                                       |
|       |                                                              qr_engagement                                |
|       |                                                                   |                                       |
|       |                                                                   |                                       |
|   qr_engagement                                                           |                                       |
|       |                                                                   |                                       |
|       |                                                        +----------v------------+                          |
|       |                                                        |                       |                          |
|       |                                                        | SessionManagerEngaged <-------------+            |
|       |                                             +----------+                       |             |            |
|       |                                             |          |                       |             |            |
|       |                                             |          +-----------------------+             |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                                             |                                                |            |
|       |                               process_session_establishment                                  |            |
|       |                                             |                                                |            |
|       |      +--------------------------------------v----------------------------------+             |            |
|       |      |                                 SessionManager                          |             |            |
|       |      |                                                                         |             |            |
|       |      |                             +--------------------+                      |             |            |
|       |      |                             |                    |                      |             |            |
|       |      |     +-----------------------+ AwaitingRequest    <----------------+     |             |            |
|       |      |     |                       |                    |                |     |             |            |
|       |      |     |           +-----------+                    |                |     |             |            |
|       |      |prepare_response |           +--------------------+                |     |             |            |
|       |      |     |           |                                                 |     |             |            |
|       |      |     |           |                                                 |     |       establish_session  |
|       |      |     |           |                                                 |     |             |            |
|       |      |     |         handle_request                                      |     |             |            |
|       |      |     |           |                                                 |     |             |            |
|       |      |     |           |                                                 |     |             |            |
|       |      |  +--v-----------v--------+                           retrieve_response  |             |            |
|       |      |  |                       +---------                               |     |             |            |
|       |      |  |   Signing             |   get_next_signature_payload           |     |             |            |
|       |      |  |                       <---------                               |     |             |            |
|       |      |  +---------+-------------+                                        |     |             |            |
|       |      |            |                                                      |     |             |            |
|       |      |            |                                                      |     |             |            |
|       |      |            |                                                      |     |             |            |
|       |      |      submit_next_signature                                        |     |             |            |
|       |      |            |                                                      |     |             |            |
|       |      |            |                 +----------------------+             |     |             |            |
|       |      |            |                 |                      |             |     |             |            |
|       |      |            |                 |   ReadyToRespond     |             |     |             |            |
|       |      |            +----------------->                      +-------------+     |             |            |
|       |      |                              |                      |                   |             |            |
|       |      |                              +----------+-----------+                   |             |            |
|       |      |                                         |                               |             |            |
|       |      +-----------------------------------------+-------------------------------+             |            |
|       |                                          handle_response                                     |            |
+-------+------------------------------------------------+---------------------------------------------+------------+
        |                                                |                                             |
        |                                        +-------v-------+                                     |
        |                                        |  Reader       |                                     |
        |                                        |               |                                     |
        |                                        |               |                                     |
        +---------------------------------------->               +-------------------------------------+
                                                 |               |
                                                 |               |
                                                 |               |
                                                 +---------------+

§Reader perspective

From the reader’s perspective, the flow is simpler:

            +--------+
            |        |
            |  User  |
            |        |
            +---+----+
                |
                |
            +---v-----+
            |         |
            | Device  |
  +---------+         |<----------------+
  |         |         |                 |
  |         |         |                 |
  |         +--+--^---+                 |
  |              |  |                   |
  |              |  | establish_session |
  |qr_engagement |  |                   |
  |              |  |                   | new_request
  |              |  |                   |
+-v--------------v--+-------------------+---+
|                  Reader                   |
|                                           |
| +--------------------+   handle_response  |
| |                    +---------------+    |
| | SessionManager     |               |    |
| |                    |<--------------+    |
| +--------------------+                    |
|                                           |
+-------------------------------------------+

The reader is simulated in common module (you can find the complete code in examples directory), here we focus on the code from the device perspective.

§Example

Basic example.

mod common;

use crate::common::{Device, Reader};

#[test]
pub fn simulated_device_and_reader_interaction() {
    let key: p256::ecdsa::SigningKey =
        p256::SecretKey::from_sec1_pem(include_str!("data/sec1.pem"))
            .unwrap()
            .into();

    // Device initialization and engagement
    let (engaged_state, qr_code_uri) = Device::initialise_session().unwrap();

    // Reader processing QR and requesting the necessary fields
    let (mut reader_session_manager, request) =
        Device::establish_reader_session(qr_code_uri).unwrap();

    // Device accepting request
    let (device_session_manager, validated_request) =
        Device::handle_request(engaged_state, request, Default::default()).unwrap();

    // Prepare response with required elements
    let response = Device::create_response(
        device_session_manager,
        &validated_request.items_request,
        &key,
    )
    .unwrap();

    // Reader Processing mDL data
    Reader::reader_handle_device_response(&mut reader_session_manager, response).unwrap();
}

§Example

An example that uses State pattern, Arc and Mutex.

use std::sync::{Arc, Mutex};

use anyhow::{Context, Result};
use isomdl::cbor;
use isomdl::definitions::device_engagement::{CentralClientMode, DeviceRetrievalMethods};
use isomdl::definitions::device_request::{DataElements, Namespaces};
use isomdl::definitions::x509::trust_anchor::TrustAnchorRegistry;
use isomdl::definitions::{self, BleOptions, DeviceRetrievalMethod};
use isomdl::presentation::device::{Documents, RequestedItems};
use isomdl::presentation::{device, reader};
use signature::Signer;
use uuid::Uuid;

use crate::common::{Device, AGE_OVER_21_ELEMENT, DOC_TYPE, NAMESPACE};

mod common;

struct SessionData {
    state: Arc<SessionManagerEngaged>,
    qr_code_uri: String,
}

struct RequestData {
    session_manager: Arc<SessionManager>,
}

struct SessionManager {
    inner: Mutex<device::SessionManager>,
    items_request: RequestedItems,
    key: Arc<p256::ecdsa::SigningKey>,
}

struct SessionManagerEngaged(device::SessionManagerEngaged);

#[test]
pub fn simulated_device_and_reader_interaction() -> Result<()> {
    let key: Arc<p256::ecdsa::SigningKey> =
        Arc::new(p256::SecretKey::from_sec1_pem(include_str!("data/sec1.pem"))?.into());

    // Parse the mDL
    let docs = Device::parse_mdl()?;

    // Device initialization and engagement
    let session_data = initialise_session(docs, Uuid::new_v4())?;

    // Reader processing QR and requesting the necessary fields
    let (mut reader_session_manager, request) = establish_reader_session(session_data.qr_code_uri)?;

    // Device accepting request
    let request_data = handle_request(
        session_data.state,
        &mut reader_session_manager,
        request,
        key.clone(),
    )?;
    if request_data.is_none() {
        anyhow::bail!("there were errors processing request");
    }
    let request_data = request_data.unwrap();

    // Prepare response with required elements
    let response = create_response(request_data.session_manager.clone())?;

    // Reader Processing mDL data
    reader_handle_device_response(&mut reader_session_manager, response)?;

    Ok(())
}

/// Check if there were any errors and sign them if needed, returning the response error.
fn get_errors(session_manager: Arc<SessionManager>) -> Result<Option<Vec<u8>>> {
    sign_pending_and_retrieve_response(session_manager, None)
}

/// Creates a QR code containing `DeviceEngagement` data, which includes its public key.
fn initialise_session(docs: Documents, uuid: Uuid) -> Result<SessionData> {
    let drms = DeviceRetrievalMethods::new(DeviceRetrievalMethod::BLE(BleOptions {
        peripheral_server_mode: None,
        central_client_mode: Some(CentralClientMode { uuid }),
    }));

    let session = device::SessionManagerInit::initialise(docs, Some(drms), None)
        .context("failed to initialize device")?;

    let (engaged_state, qr_code_uri) = session
        .qr_engagement()
        .context("could not generate qr engagement")?;
    Ok(SessionData {
        state: Arc::new(SessionManagerEngaged(engaged_state)),
        qr_code_uri,
    })
}

/// Establishes the reader session from the given QR code and create request for needed elements.
fn establish_reader_session(qr: String) -> Result<(reader::SessionManager, Vec<u8>)> {
    let requested_elements = Namespaces::new(
        NAMESPACE.into(),
        DataElements::new(AGE_OVER_21_ELEMENT.to_string(), false),
    );
    let trust_anchor_registry = TrustAnchorRegistry::default();

    let (reader_sm, session_request, _ble_ident) =
        reader::SessionManager::establish_session(qr, requested_elements, trust_anchor_registry)
            .context("failed to establish reader session")?;
    Ok((reader_sm, session_request))
}

/// The Device handles the request from the reader and creates the `RequestData` context.
fn handle_request(
    state: Arc<SessionManagerEngaged>,
    reader_session_manager: &mut reader::SessionManager,
    request: Vec<u8>,
    key: Arc<p256::ecdsa::SigningKey>,
) -> Result<Option<RequestData>> {
    let (session_manager, validated_response) = {
        let session_establishment: definitions::SessionEstablishment =
            cbor::from_slice(&request).context("could not deserialize request")?;
        state
            .0
            .clone()
            .process_session_establishment(session_establishment, Default::default())
            .context("could not process process session establishment")?
    };
    let session_manager = Arc::new(SessionManager {
        inner: Mutex::new(session_manager),
        items_request: validated_response.items_request.clone(),
        key,
    });
    // Propagate any errors back to the reader
    if let Ok(Some(response)) = get_errors(session_manager.clone()) {
        let validated_response = reader_session_manager.handle_response(&response);
        println!("Reader: {validated_response:?}");
        return Ok(None);
    };

    Ok(Some(RequestData { session_manager }))
}

// Prepare response with required elements.
fn create_response(session_manager: Arc<SessionManager>) -> Result<Vec<u8>> {
    let permitted_items = [(
        DOC_TYPE.to_string(),
        [(NAMESPACE.to_string(), vec![AGE_OVER_21_ELEMENT.to_string()])]
            .into_iter()
            .collect(),
    )]
    .into_iter()
    .collect();
    session_manager
        .inner
        .lock()
        .unwrap()
        .prepare_response(&session_manager.items_request, permitted_items);
    sign_pending_and_retrieve_response(session_manager.clone(), Some(1))?
        .ok_or_else(|| anyhow::anyhow!("cannot prepare response"))
}

fn sign_pending_and_retrieve_response(
    session_manager: Arc<SessionManager>,
    expected_to_sign: Option<usize>,
) -> Result<Option<Vec<u8>>> {
    let mut signed = 0;
    loop {
        let mut guard = session_manager.inner.lock().unwrap();
        if let Some((_, payload)) = guard.get_next_signature_payload() {
            if let Some(expected_to_sign) = expected_to_sign {
                if signed >= expected_to_sign {
                    anyhow::bail!(
                        "expected to sign {} documents, but there are more",
                        expected_to_sign
                    );
                }
            }
            let signature: p256::ecdsa::Signature = session_manager.key.sign(payload);
            guard
                .submit_next_signature(signature.to_vec())
                .context("failed to submit signature")?;
            signed += 1;
        } else {
            break;
        }
    }
    Ok(session_manager.inner.lock().unwrap().retrieve_response())
}

/// Reader Processing mDL data.
fn reader_handle_device_response(
    reader_sm: &mut reader::SessionManager,
    response: Vec<u8>,
) -> Result<()> {
    let validated_response = reader_sm.handle_response(&response);
    println!("Validated Response: {validated_response:?}");
    Ok(())
}

Modules§

cbor
cose
definitions
This module contains the definitions for all components involved in the lib.
issuance
This module contains the implementation of the issuance module.
macros
presentation
This module responsible on handling the interaction between the device and reader.