Skip to content

Indicio-tech/isomdl-uniffi

ISO MDL UniFFI Python Bindings

This repository provides Python bindings for the ISO 18013-5 Mobile Driver's License (mDL) library using UniFFI, allowing interaction with mDL documents from Python applications.

Overview

The ISO MDL UniFFI library provides a Python interface to the Rust-based isomdl library, enabling:

  • Holder functionality: Create presentation sessions for mDL documents
  • Reader functionality: Verify and read mDL documents from holders
  • Document management: Create, manage, and present mobile driver's licenses
  • Cross-platform support: Works on macOS, Linux, and Windows

Architecture

This project uses UniFFI to generate Python bindings from Rust code:

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Python App   │ ←→ │  UniFFI Bindings │ ←→ │   Rust Library  │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                        │
                                                ┌───────▼───────┐
                                                │  isomdl crate │
                                                └───────────────┘

Prerequisites

  • Rust: Latest stable version (install via rustup)
  • Python: 3.9 or later
  • UV: Python package manager (recommended, install via pip install uv)
  • Cross-compilation tools (for building binaries for multiple platforms)

Platform-specific Requirements

macOS

  • Xcode command line tools: xcode-select --install

Linux

  • Build essentials: sudo apt update && sudo apt install build-essential

Windows

  • MinGW-w64 toolchain

Quick Start

1. Clone and Setup

git clone <repository-url>
cd isomdl-uniffi
git checkout python-bindings

2. (Optional) Setup Pre-commit Hooks

For contributors, set up pre-commit hooks to ensure code quality:

# Install pre-commit
pip install pre-commit

# Install git hook scripts
pre-commit install

# (Optional) Run against all files
pre-commit run --all-files

See python/precommit/README.md for more details.

3. CI/CD Pipeline

This project includes comprehensive GitHub Actions workflows:

Pull Request Checks (.github/workflows/pr-check.yml)

  • Quick validation for PRs with essential checks
  • Runs on every PR to main and develop branches
  • Validates Rust formatting, compilation, and tests
  • Builds Python bindings and runs test suite
  • Verifies selective disclosure functionality
  • Checks Python code quality (ruff)

Full CI Pipeline (.github/workflows/ci.yml)

  • Comprehensive testing across multiple platforms and Python versions
  • Matrix builds: Ubuntu, macOS, Windows × Python 3.9-3.12
  • Security auditing with cargo audit
  • Integration tests including selective disclosure validation

Release Pipeline (.github/workflows/release.yml)

  • Automated releases on version tags (v*)
  • Builds Python wheels for multiple platforms
  • Publishes Rust crate to crates.io
  • Creates GitHub releases with binaries

Dependency Management (.github/dependabot.yml)

  • Automated dependency updates for Rust crates
  • Weekly updates for GitHub Actions
  • Properly labeled and organized PRs

All workflows ensure that:

  • ✅ Rust code compiles and passes tests
  • ✅ Python bindings build successfully
  • ✅ Complete test suite passes (including selective disclosure tests)
  • ✅ Code meets formatting and quality standards
  • ✅ Security vulnerabilities are detected early
  • ✅ All commits are properly signed with DCO (Developer Certificate of Origin)

4. Build Python Bindings

Using UV (recommended if you have UV installed):

cd python
uv sync --extra dev
uv run python build.py

Using standard Python:

cd python
python3 build.py

Direct build script:

./python/precommit/build-bindings.sh

This will:

  • Build the Rust library in release mode with UniFFI symbol preservation
  • Generate Python bindings using UniFFI
  • Copy the bindings to the Python package directory

3. Test the Bindings

# Run the comprehensive test suite
./python/test-bindings.py

4. Install and Use (Optional)

# Create virtual environment (optional but recommended)
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install the generated package
cd rust/out/python
pip install -e .

# Test the installation
python -c "import isomdl_uniffi; print('Success!')"

Usage Examples

Basic Holder Example

import isomdl_uniffi as mdl
import uuid

# Create an mDL document from CBOR data
mdoc = mdl.Mdoc.from_cbor(cbor_data, "device_key_alias")

# Start a presentation session
session_uuid = uuid.uuid4()
session = mdl.MdlPresentationSession.new(mdoc, session_uuid)

print(f"QR Code URI: {session.qr_code_uri}")
print(f"BLE Identifier: {session.ble_ident.hex()}")

Basic Reader Example

import isomdl_uniffi as mdl

# Define what data elements to request
requested_items = {
    "org.iso.18013.5.1": {
        "family_name": True,
        "given_name": True,
        "birth_date": True
    }
}

# Establish reader session
session_data = mdl.establish_session(
    uri="mdoc://example-uri",
    requested_items=requested_items,
    trust_anchor_registry=["-----BEGIN CERTIFICATE-----\n..."]
)

print(f"Session UUID: {session_data.uuid}")

API Reference

Core Classes

Mdoc

Represents an ISO 18013-5 mobile document.

Methods:

  • from_cbor(data: bytes, key_alias: str) -> Mdoc: Create from CBOR data
  • to_cbor() -> bytes: Export to CBOR format
  • namespaces() -> list[str]: Get available namespaces
  • elements_for_namespace(namespace: str) -> list[Element]: Get elements in namespace

MdlPresentationSession

Manages the holder's presentation session.

Methods:

  • new(mdoc: Mdoc, uuid: UUID) -> MdlPresentationSession: Create new session
  • qr_code_uri: str: QR code for reader scanning
  • ble_ident: bytes: Bluetooth Low Energy identifier

MDLSessionManager

Handles reader-side session management.

Methods:

  • establish_session(uri: str, requested_items: dict, trust_anchors: list[str]) -> MDLReaderSessionData

Data Structures

Element

class Element:
    identifier: str        # Element name
    value: Optional[str]   # JSON representation of value

Development

Project Structure

isomdl-uniffi/
├── rust/                    # Rust source code
│   ├── src/
│   │   ├── lib.rs          # Main library entry point
│   │   └── mdl/            # MDL-specific modules
│   │       ├── mod.rs      # Module definitions
│   │       ├── holder.rs   # Holder functionality
│   │       ├── reader.rs   # Reader functionality
│   │       ├── mdoc.rs     # Document management
│   │       └── util.rs     # Utility functions
│   ├── Cargo.toml         # Rust dependencies
│   ├── uniffi-bindgen.rs  # UniFFI binding generator
│   └── out/               # Generated bindings (gitignored)
├── kotlin/                 # Kotlin bindings (separate)
└── out/                   # Generated bindings output
    └── python/            # Python package output

Building for Development

  1. Setup development environment:
# Install development dependencies
cargo install uniffi-bindgen
rustup target add x86_64-apple-darwin aarch64-apple-darwin x86_64-unknown-linux-gnu
  1. Build and test:
# Build library
cargo build --release

# Run Rust tests
cargo test

# Generate Python bindings
cargo run --bin uniffi-bindgen generate --library target/release/libisomdl_uniffi.dylib --language python --out-dir out/python

# Test Python bindings
cd out/python
uv venv test-env
source test-env/bin/activate
uv pip install -e .
python -c "import isomdl_uniffi; print('Import successful!')"

Cross-Platform Building

The build scripts build binaries for the current platform only. For production deployments requiring multiple platforms, you can:

  1. Use GitHub Actions or CI/CD to build on different runners
  2. Use cross-compilation tools like cross-rs:
# Install cross-compilation tool
cargo install cross --git https://github.com/cross-rs/cross

# Add target platforms
rustup target add x86_64-apple-darwin aarch64-apple-darwin x86_64-unknown-linux-gnu x86_64-pc-windows-gnu

# Build for specific targets
cross build --release --target x86_64-unknown-linux-gnu --lib
cargo build --release --target x86_64-apple-darwin --lib
# ... etc for other platforms
  1. Create universal macOS binaries using lipo:
# Build both architectures
cargo build --release --target x86_64-apple-darwin --lib
cargo build --release --target aarch64-apple-darwin --lib

# Merge into universal binary
lipo -create \
    target/x86_64-apple-darwin/release/libisomdl_uniffi.dylib \
    target/aarch64-apple-darwin/release/libisomdl_uniffi.dylib \
    -output out/python/libisomdl_uniffi.dylib

Integration with ACA-Py Plugins

This library is designed to be used within ACA-Py (Aries Cloud Agent Python) plugins. Example integration:

# In your ACA-Py plugin
from aries_cloudagent.core.profile import Profile
import isomdl_uniffi as mdl

class MDLHandler:
    def __init__(self, profile: Profile):
        self.profile = profile
    
    async def create_presentation(self, mdoc_data: bytes):
        # Create mDL document
        mdoc = mdl.Mdoc.from_cbor(mdoc_data, "key_alias")
        
        # Start presentation session
        session = mdl.MdlPresentationSession.new(mdoc, uuid.uuid4())
        
        return {
            "qr_code": session.qr_code_uri,
            "ble_ident": session.ble_ident.hex()
        }

Dependencies

Rust Dependencies

  • uniffi: UniFFI framework for generating bindings
  • isomdl: Core ISO 18013-5 implementation
  • serde: Serialization framework
  • uuid: UUID generation and parsing
  • base64: Base64 encoding/decoding
  • p256: ECDSA P-256 cryptography
  • anyhow: Error handling

Python Dependencies

Generated Python package has minimal dependencies, as most functionality is provided by the compiled Rust library.

Contributing

We welcome contributions to this project! For detailed guidelines on how to contribute, including:

  • Development environment setup
  • Code standards and formatting
  • Testing requirements
  • Submission process
  • DCO (Developer Certificate of Origin) requirements

Please see CONTRIBUTING.md for complete contribution guidelines.

License

see the LICENSE file for details.

Third-Party Dependencies

This project incorporates code from the following open-source projects:

Both projects are dual-licensed under Apache 2.0 and MIT licenses.

See THIRD_PARTY_LICENSES.md for complete license information and attributions.

Troubleshooting

Common Issues

  1. Build fails with "target not found"

    • Ensure all required targets are installed: rustup target add <target>
  2. Python import fails

    • Verify the dynamic library is in the correct location
    • Check that Python can find the generated module
  3. Cross-compilation issues

    • Install cross: cargo install cross
    • Use Docker for consistent cross-compilation environment

Getting Help

Directory Structure

isomdl-uniffi/
├── README.md                    # Main documentation
├── rust/                       # Rust source code
│   ├── src/                    # Rust library source
│   ├── Cargo.toml             # Rust dependencies
│   ├── uniffi-bindgen.rs      # UniFFI binding generator
│   └── out/                   # Generated bindings (gitignored)
│       └── python/            # Python bindings output
│           ├── isomdl_uniffi.py        # Generated Python module
│           ├── libisomdl_uniffi.dylib  # Compiled library
│           ├── setup.py               # Package setup
│           └── pyproject.toml         # Package config
└── kotlin/                    # Kotlin bindings (separate)

Roadmap

  • Add comprehensive test suite
  • Improve error handling and error types
  • Add more usage examples
  • Performance optimizations
  • Enhanced documentation
  • CI/CD pipeline for automated builds
  • Cross-platform binary distribution

About

No description, website, or topics provided.

Resources

License

MIT and 4 other licenses found

Licenses found

MIT
LICENSE
Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Unknown
license-template-indicio.txt
Unknown
license-template-with-spruceid.txt

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors 2

  •  
  •