Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions libuta_rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Unified Trust Anchor API Rust Wrapper
#
# Copyright (c) Siemens Mobility GmbH, 2026
#
# Authors:
# Christian P. Feist <christian.feist@siemens.com>
# Hermann Seuschek <hermann.seuschek@siemens.com>
#
# This work is licensed under the terms of the Apache Software License
# 2.0. See the COPYING file in the top-level directory.
#
# SPDX-FileCopyrightText: Copyright 2026 Siemens
# SPDX-License-Identifier: Apache-2.0
#
/target
**/*.rs.bk
Cargo.lock
39 changes: 39 additions & 0 deletions libuta_rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Unified Trust Anchor API Rust Wrapper
#
# Copyright (c) Siemens Mobility GmbH, 2026
#
# Authors:
# Christian P. Feist <christian.feist@siemens.com>
# Hermann Seuschek <hermann.seuschek@siemens.com>
#
# This work is licensed under the terms of the Apache Software License
# 2.0. See the COPYING file in the top-level directory.
#
# SPDX-FileCopyrightText: Copyright 2026 Siemens
# SPDX-License-Identifier: Apache-2.0
#
[package]
name = "libuta_rust"
license = "Apache-2.0"
description = "Rust wrapper for the Unified Trust Anchor API (libuta)"
repository = "https://github.com/siemens/libuta"
readme = "README.md"
version = "1.2.0"
authors = ["Christian P. Feist <christian.feist@siemens.com>", "Hermann Seuschek <hermann.seuschek@siemens.com>"]
categories = ["api-bindings", "cryptography"]
keywords = ["tpm", "trust-anchor", "libuta", "hardware-security", "cryptography"]
edition = "2021"
build = "build.rs"
rust-version = "1.85"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
bindgen = "0.71.1"

[lib]
path = "src/lib.rs"

[profile.release]
opt-level = 3
lto = true
137 changes: 137 additions & 0 deletions libuta_rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Rust Bindings for the Unified Trust Anchor API

This crate is part of the Unified Trust Anchor API (libuta) and provides
lightweight Rust bindings for the C implementation of the library. The
low-level C bindings are generated using
[bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html), while
additional Rust code exposes these low-level interfaces through a more
idiomatic Rust API.

## Licensing

This work is licensed under the terms of the Apache License, Version 2.0.
Copyright (c) 2026 Siemens Mobility GmbH.

* SPDX-FileCopyrightText: Copyright 2026 Siemens
* SPDX-License-Identifier: Apache-2.0

## Prerequisites

* **libuta C library** properly installed:
* Header file `uta.h` must be in the compiler's include path (e.g., `/usr/local/include/uta.h`)
* Shared library must be in the system library path (e.g., `/usr/lib` or `/lib`)
* **Note:** On Debian-based systems, `/usr/local/lib` is not in the default search path. Add it with: `export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH`
* **Rust toolchain** installed (on Debian-based systems: `apt install rustc`)
* Tested with Rust `1.85.0 (4d91de4e4 2025-02-17)` or later
* **LLVM** installed (required by bindgen), see [bindgen requirements](https://rust-lang.github.io/rust-bindgen/requirements.html)

## Architecture

.The Rust bindings for libuta use a two-layer architecture. The **lower layer**
(`mod bindings`) provides a direct mapping of the C API to Rust and is primarily
generated using bindgen, exposing all available symbols. The **upper layer** (`mod api`)
builds on top of the lower layer bindings, importing only the necessary components and
presenting them through an idiomatic Rust interface. These high-level bindings
enhance usability by incorporating Rust-style error handling and memory
management. The following diagram illustrates the architecture from the native
libuta library up to its integration in a Rust application:

```
+--------------------+
| application |
| e.g., examples |
| (Rust) |
+--------------------+
| crate: libuta_rust |
| mod api |
| mod bindings |
| (Rust) |
+--------------------+
| libuta |
| (C) |
+--------------------+
```

## Wrapper Library

The directory `libuta_rust` contains the primary wrapper crate designed for use in
Rust applications through an idiomatic Rust interface. Its purpose is to
encapsulate the data structures, types, and functions defined in the low-level
C implementation exposed by the generated bindings. This includes replacing
patterns such as "output parameters" with Rust-native constructs like
`Result<T>` for error handling, and managing context within an instance of the
`UtaApiV1` structure. These design choices significantly reduce the risk of
misuse and provide a safer, more ergonomic API for developers.

Basic unit tests have been implemented to verify the core functionality of the
library. Please note that the expected outputs for certain functions rely on
default keys provided by the software simulation embedded in libuta. When
executed against a real hardware secure element, these keys differ, resulting
in mismatches between actual and expected values. Consequently, some unit tests
may fail under hardware conditions even though the underlying functionality is
correct. Since the keys used by hardware implementations are unknown in
advance, the test code cannot be adapted to produce matching results.

**Note:** An `UtaApiV1` instance must always be declared as mutable because
most methods require `&mut self`. While this may initially seem
counterintuitive, it reflects the fact that the object’s internal state changes
frequently, particularly when managing context, such as acquiring or releasing
locks. This behavior is mandated by the underlying C library interface, and
therefore cannot be avoided.

### File Structure

```
├── Cargo.toml # Crate configuration and dependencies
├── build.rs # Build script (invokes bindgen)
├── README.md # This file
├── examples
│   └── all.rs # Example code
└── src/
├── lib.rs # High-level idiomatic Rust API
├── bindings.h # C header input for bindgen
└── bindings.rs # Imports auto-generated C bindings
```

### Building and Testing

```bash
cargo build
cargo test
```

### Example Code

The example code can be found in the examples directory and serves as a starting point for
integrating `libuta_rust` into your own projects. Build and run the example code as follows:

```
cargo run --example all
```

### Low-Level Bindings

The file bindings.rc imports the low-level Rust bindings for the libuta
library. These bindings are generated using bindgen and supplemented
with additional code to suppress warnings caused by Rust naming conventions.
The bindgen tool is invoked automatically by Cargo during the build process, so
manual execution is not required. The build steps are implemented in build.rs,
which translates the input header file bindings.h (including the main libuta
header) into Rust bindings accessible through src/bindings.rs. Because this
layer is a direct one-to-one mapping of the C API, it is not intended for
direct use in Rust applications. Instead, it serves as the foundation for the
higher-level wrapper located in the lib.rs file.

**Note:** The Rust compiler emits warnings regarding the use of 128-bit integers
because **bindings for this data type are not yet fully stable and may break
compatibility in future releases.** These warnings are explicitly suppressed in
the code. Additional warnings related to naming conventions are also
suppressed, as the original libuta naming is preserved to maintain consistency
with the underlying C API. Since this low-level binding layer is not intended
for direct use in Rust applications, but rather serves as the foundation for
the higher-level wrapper, these warnings do not pose a practical issue.

**Note:** Rust introduces several C-based dependencies through libuta.
Vulnerabilities in any of these linked C libraries could potentially be
exploited, even when accessed via a Rust abstraction layer. At present, these
dependencies cannot be removed because they are integral to libuta.
44 changes: 44 additions & 0 deletions libuta_rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @file build.rs
*
* @brief This code orchestrates the bindgen tool to create Rust-bindings.
*
* @copyright Copyright (c) Siemens Mobility GmbH, 2026
*
* @author Christian P. Feist <christian.feist@siemens.com>
* @author Hermann Seuschek <hermann.seuschek@siemens.com>
*
* @license This work is licensed under the terms of the Apache Software License
* 2.0. See the COPYING file in the top-level directory.
*
* SPDX-FileCopyrightText: Copyright 2026 Siemens
* SPDX-License-Identifier: Apache-2.0
*/
extern crate bindgen;
use std::env;
use std::path::PathBuf;

fn main() {
println!("cargo:rerun-if-changed=src/bindings.h");

// The bindgen::Builder is the main entry point to bindgen
let bindings = bindgen::Builder::default()
// The input header we would like to generate bindings for.
.header("src/bindings.h")
// Invalidate the built crate whenever included header files change.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");

// Write the bindings to file $OUT_DIR/bindings.rs
let out_path = PathBuf::from(
env::var("OUT_DIR").expect("OUT_DIR environment variable not set by cargo")
);
bindings
.write_to_file(out_path.join("bindgen_bindings.rs"))
.expect("Couldn't write bindings!");

// Ensure the crate links against the libuta C library
println!("cargo:rustc-link-lib=uta");
}
58 changes: 58 additions & 0 deletions libuta_rust/examples/all.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* Unified Trust Anchor API Rust Wrapper
*
* Example code that demonstrates the use of the libuta Rust wrapper.
*
* Copyright (c) Siemens Mobility GmbH, 2026
*
* Authors:
* Christian P. Feist <christian.feist@siemens.com>
* Hermann Seuschek <hermann.seuschek@siemens.com>
*
* This work is licensed under the terms of the Apache Software License
* 2.0. See the COPYING file in the top-level directory.
*
* SPDX-FileCopyrightText: Copyright 2026 Siemens
* SPDX-License-Identifier: Apache-2.0
*/
extern crate libuta_rust;
use libuta_rust::UtaApiV1;

fn main() {

match UtaApiV1::new() {
Ok(ref mut uta) => {

println!("Execute uta.get_version() ...");
match uta.get_version() {
Ok(uuid) => println!("Library version: {:?}\n", uuid),
Err(e) => println!("Error calling uta.get_version(), got error code {:?}\n", e.get_rc())
}

println!("Execute uta.self_test() ...");
match uta.self_test() {
Ok(()) => println!("Success!\n"),
Err(e) => println!("Error calling uta.self_test(), got error code {:?}\n", e.get_rc())
}

println!("Execute uta.get_device_uuid() ...");
match uta.get_device_uuid() {
Ok(uuid) => println!("Device UUID bytes: {:?}\n", uuid),
Err(e) => println!("Error calling uta.get_device_uuid(), got error code {:?}\n", e.get_rc())
}

println!("Execute uta.derive_key(32, &dv, 0) ...");
let dv = vec![1u8; 8];
match uta.derive_key(32, &dv, 0) {
Ok(key) => println!("Derived key: {:?}\n", key),
Err(e) => println!("Error calling uta.derive_key(), got error code {:?}\n", e.get_rc())
}

println!("Execute uta.get_random(32) ...");
match uta.get_random(32) {
Ok(random) => println!("Random bytes: {:?}\n", random),
Err(e) => println!("Error calling uta.get_random(), got error code {:?}\n", e.get_rc())
}
},
Err(e) => println!("Error on uta.init(), got error code {:?}", e.get_rc())
}
}
21 changes: 21 additions & 0 deletions libuta_rust/src/bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* Unified Trust Anchor API Rust Wrapper
*
* Includes uta.h from include path
*
* Copyright (c) Siemens Mobility GmbH, 2026
*
* Authors:
* Christian P. Feist <christian.feist@siemens.com>
* Hermann Seuschek <hermann.seuschek@siemens.com>
*
* This work is licensed under the terms of the Apache Software License
* 2.0. See the COPYING file in the top-level directory.
*
* SPDX-FileCopyrightText: Copyright 2026 Siemens
* SPDX-License-Identifier: Apache-2.0
*/

/* For this include, we assume that libuta is properly installed on the system
* and that uta.h can be found in the system's include path.
*/
#include <uta.h>
29 changes: 29 additions & 0 deletions libuta_rust/src/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Unified Trust Anchor API Rust Wrapper
*
* This code includes the low-level C-bindings generated by bindgen and
* suppresses some warnings caused by naming conventions.
*
* Copyright (c) Siemens Mobility GmbH, 2026
*
* Authors:
* Christian P. Feist <christian.feist@siemens.com>
* Hermann Seuschek <hermann.seuschek@siemens.com>
*
* This work is licensed under the terms of the Apache Software License
* 2.0. See the COPYING file in the top-level directory.
*
* SPDX-FileCopyrightText: Copyright 2026 Siemens
* SPDX-License-Identifier: Apache-2.0
*/
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

// NOTE: Handling unsigned 128 bit integers is not stable yet!
// Be careful, the interface could break in future!
#![allow(improper_ctypes)]

// Here we include the libuta bindings generated by bindgen
include!(concat!(env!("OUT_DIR"), "/bindgen_bindings.rs"));

Loading