-
Notifications
You must be signed in to change notification settings - Fork 5
feat: add rust bindings #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # Rust Wrapper for the Unified Trust Anchor API | ||
|
|
||
| This project is part of the libuta library and provides a Rust wrapper | ||
| implemented in the crate `libuta_rust`, along with example usage in the | ||
| `examples` crate. | ||
|
|
||
| For detailed information, please refer to the crate’s documentation in the | ||
| corresponding [README.md](libuta_rust/README.md) | ||
|
|
||
| The examples crate is organized as follows and serves as a starting point for | ||
| integrating `libuta_rust` into your own projects: | ||
|
|
||
| ``` | ||
| ├── Cargo.toml | ||
| └── src | ||
| └── main.rs | ||
| ``` | ||
|
|
||
| Build and run the examples as follows: | ||
|
|
||
| ``` | ||
| cargo build | ||
| cargo run | ||
| ``` | ||
|
|
||
| ## Licensing | ||
|
|
||
| This work is licensed under the term of the Apache License, Version 2.0. | ||
| Copyright (c) 2025 Siemens Mobility GmbH. | ||
|
|
||
| * SPDX-FileCopyrightText: Copyright 2025 Siemens | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| 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, 2025 | ||
| # | ||
| # 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 2025 Siemens | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| /target | ||
| **/*.rs.bk | ||
| Cargo.lock |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Unified Trust Anchor API Rust Wrapper | ||
| # | ||
| # Copyright (c) Siemens Mobility GmbH, 2025 | ||
| # | ||
| # 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 2025 Siemens | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| [package] | ||
| name = "examples" | ||
| version = "1.2.0" | ||
| authors = ["Christian P. Feist <christian.feist@siemens.com>", "Hermann Seuschek <hermann.seuschek@siemens.com>"] | ||
| edition = "2018" | ||
|
|
||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
|
||
| [dependencies] | ||
| libuta_rust = {path = "../libuta_rust"} | ||
|
|
||
| [[bin]] | ||
| name = "uta_examples" | ||
| path = "src/main.rs" | ||
|
|
||
| [profile.release] | ||
| opt-level = "z" | ||
| lto = true | ||
| strip = true |
| 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, 2025 | ||
| * | ||
| * 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 2025 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()) | ||
| } | ||
| } |
| 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, 2025 | ||
| # | ||
| # 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 2025 Siemens | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| /target | ||
| **/*.rs.bk | ||
| Cargo.lock |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| # Unified Trust Anchor API Rust Wrapper | ||
| # | ||
| # Copyright (c) Siemens Mobility GmbH, 2025 | ||
| # | ||
| # 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 2025 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>"] | ||
| edition = "2018" | ||
| build = "build.rs" | ||
|
|
||
| # 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 = 0 | ||
| lto = true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| # Rust Wrapper for the Unified Trust Anchor API | ||
|
|
||
| This crate provides a lightweight Rust wrapper around the C implementation of | ||
| the Unified Trust Anchor API (libuta). The underlying 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) 2025 Siemens Mobility GmbH. | ||
|
|
||
| * SPDX-FileCopyrightText: Copyright 2025 Siemens | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| * Proper installation of libuta | ||
| * The header file `uta.h` must be available in the compiler’s include | ||
| path (e.g., `/usr/local/include/uta.h`) | ||
| * The shared library must be accessible in the system library path (e.g., | ||
| `/usr/lib` or `/lib`). **Note:** `/usr/local/lib` is not included in | ||
| the default search path on Debian-based systems. To add it manually, | ||
| use: `export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH` | ||
| * Installation of Rust envionment (On Debian-based systems: `apt install rustc`) | ||
| * Tested with the following Rust toolchain: `rustc 1.85.0 (4d91de4e4 2025-02-17)` | ||
| * LLVM installed (required by bindgen). For details, see [bindgen requirements](https://rust-lang.github.io/rust-bindgen/requirements.html) | ||
|
|
||
| ## Architecture of the Rust bindings | ||
|
|
||
| The Rust bindings for libuta are structured in two layers. 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 bindings, importing only the necessary components and | ||
| presenting them through an idiomatic Rust interface. This high-level wrapper | ||
| enhances 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 (`libuta_rust`) | ||
|
|
||
| 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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a remark: in your use-case the mutable reference to self makes more sense, but there is also a way how to avoid a mutable reference in every method: RWLock. |
||
|
|
||
| The file structure of crate `libuta_rust` is as follows: | ||
|
|
||
| ``` | ||
| ├── Cargo.toml | ||
| ├── build.rs | ||
| ├── README.md | ||
| └── src | ||
| ├── lib.rs | ||
| ├── bindings.h | ||
| └── bindings.rc | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bindings.rs maybe? |
||
| ``` | ||
|
|
||
| Building and testing the library: | ||
|
|
||
| ``` | ||
| cargo build | ||
| cargo test | ||
| ``` | ||
|
|
||
| ### Low-level bindings (mod bindings) | ||
|
|
||
| The file bindings.rc includes 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's great that you explain why you suppress some warnings 👍 |
||
| 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. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /** @file build.rs | ||
| * | ||
| * @brief This code orchestrates the bindgen tool to create Rust-bindings. | ||
| * | ||
| * @copyright Copyright (c) Siemens Mobility GmbH, 2025 | ||
| * | ||
| * @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 2025 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").unwrap()); | ||
| 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"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor thing, you have 2 separate rust projects (two Cargo.toml files) rather than a single project with examples. If you have a single rust project, there is a standard approach to run examples #
cargo run --example <example_name>. But a separate example project is easier to take and integrate by the user.