From 9baac26b5bc5f1871fd3cc7fe7eb610f5a665189 Mon Sep 17 00:00:00 2001 From: hermann Date: Tue, 2 Dec 2025 17:07:09 +0100 Subject: [PATCH] feat: add rust bindings --- README.md | 8 + rust-bindings/README.md | 32 +++ rust-bindings/examples/.gitignore | 17 ++ rust-bindings/examples/Cargo.toml | 33 +++ rust-bindings/examples/src/main.rs | 58 ++++ rust-bindings/libuta_rust/.gitignore | 17 ++ rust-bindings/libuta_rust/Cargo.toml | 36 +++ rust-bindings/libuta_rust/README.md | 128 +++++++++ rust-bindings/libuta_rust/build.rs | 42 +++ rust-bindings/libuta_rust/src/bindings.h | 21 ++ rust-bindings/libuta_rust/src/bindings.rs | 28 ++ rust-bindings/libuta_rust/src/lib.rs | 333 ++++++++++++++++++++++ 12 files changed, 753 insertions(+) create mode 100644 rust-bindings/README.md create mode 100644 rust-bindings/examples/.gitignore create mode 100644 rust-bindings/examples/Cargo.toml create mode 100644 rust-bindings/examples/src/main.rs create mode 100644 rust-bindings/libuta_rust/.gitignore create mode 100644 rust-bindings/libuta_rust/Cargo.toml create mode 100644 rust-bindings/libuta_rust/README.md create mode 100644 rust-bindings/libuta_rust/build.rs create mode 100644 rust-bindings/libuta_rust/src/bindings.h create mode 100644 rust-bindings/libuta_rust/src/bindings.rs create mode 100644 rust-bindings/libuta_rust/src/lib.rs diff --git a/README.md b/README.md index 5b1f14f..2833a19 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ simulator with a uniform interface. * [Thread safety](#thread-safety) * [UTA Key Hierarchy](#uta-key-hierarchy) * [TPM TCG and TPM IBM](#tpm-tcg-and-tpm-ibm) + * [Rust Bindings](#rust-bindings) * [Coding Standard](#coding-standard) * [Versioning](#versioning) @@ -502,6 +503,13 @@ primary HMAC key in the endorsement hierarchy using the 8 Byte string +----------------------+ +-------------------------------+ ``` +## Rust Bindings + +This library includes an interface for the [Rust programming +language](https://rust-lang.org/), located in the rust-bindings subdirectory. +For detailed instructions and usage information, please refer to the dedicated +[README file](rust-bindings/README.md). + ## Coding Standard The UTA library code is oriented to the Barr Group's Embedded C Coding Standard. diff --git a/rust-bindings/README.md b/rust-bindings/README.md new file mode 100644 index 0000000..c1bb7c2 --- /dev/null +++ b/rust-bindings/README.md @@ -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 diff --git a/rust-bindings/examples/.gitignore b/rust-bindings/examples/.gitignore new file mode 100644 index 0000000..67f2093 --- /dev/null +++ b/rust-bindings/examples/.gitignore @@ -0,0 +1,17 @@ +# Unified Trust Anchor API Rust Wrapper +# +# Copyright (c) Siemens Mobility GmbH, 2025 +# +# Authors: +# Christian P. Feist +# Hermann Seuschek +# +# 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 diff --git a/rust-bindings/examples/Cargo.toml b/rust-bindings/examples/Cargo.toml new file mode 100644 index 0000000..dade213 --- /dev/null +++ b/rust-bindings/examples/Cargo.toml @@ -0,0 +1,33 @@ +# Unified Trust Anchor API Rust Wrapper +# +# Copyright (c) Siemens Mobility GmbH, 2025 +# +# Authors: +# Christian P. Feist +# Hermann Seuschek +# +# 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 ", "Hermann Seuschek "] +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 diff --git a/rust-bindings/examples/src/main.rs b/rust-bindings/examples/src/main.rs new file mode 100644 index 0000000..30f8d39 --- /dev/null +++ b/rust-bindings/examples/src/main.rs @@ -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 +* Hermann Seuschek +* +* 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()) + } +} diff --git a/rust-bindings/libuta_rust/.gitignore b/rust-bindings/libuta_rust/.gitignore new file mode 100644 index 0000000..67f2093 --- /dev/null +++ b/rust-bindings/libuta_rust/.gitignore @@ -0,0 +1,17 @@ +# Unified Trust Anchor API Rust Wrapper +# +# Copyright (c) Siemens Mobility GmbH, 2025 +# +# Authors: +# Christian P. Feist +# Hermann Seuschek +# +# 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 diff --git a/rust-bindings/libuta_rust/Cargo.toml b/rust-bindings/libuta_rust/Cargo.toml new file mode 100644 index 0000000..4179e33 --- /dev/null +++ b/rust-bindings/libuta_rust/Cargo.toml @@ -0,0 +1,36 @@ +# Unified Trust Anchor API Rust Wrapper +# +# Copyright (c) Siemens Mobility GmbH, 2025 +# +# Authors: +# Christian P. Feist +# Hermann Seuschek +# +# 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 ", "Hermann Seuschek "] +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 diff --git a/rust-bindings/libuta_rust/README.md b/rust-bindings/libuta_rust/README.md new file mode 100644 index 0000000..0b2e4e3 --- /dev/null +++ b/rust-bindings/libuta_rust/README.md @@ -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` 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. + +The file structure of crate `libuta_rust` is as follows: + +``` +├── Cargo.toml +├── build.rs +├── README.md +└── src + ├── lib.rs + ├── bindings.h + └── bindings.rc +``` + +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 +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. diff --git a/rust-bindings/libuta_rust/build.rs b/rust-bindings/libuta_rust/build.rs new file mode 100644 index 0000000..cdc5671 --- /dev/null +++ b/rust-bindings/libuta_rust/build.rs @@ -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 +* @author Hermann Seuschek +* +* @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"); +} diff --git a/rust-bindings/libuta_rust/src/bindings.h b/rust-bindings/libuta_rust/src/bindings.h new file mode 100644 index 0000000..651f727 --- /dev/null +++ b/rust-bindings/libuta_rust/src/bindings.h @@ -0,0 +1,21 @@ +/* Unified Trust Anchor API Rust Wrapper +* +* Includes uta.h from include path +* +* Copyright (c) Siemens Mobility GmbH, 2025 +* +* Authors: +* Christian P. Feist +* Hermann Seuschek +* +* 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 +*/ + +/* 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 diff --git a/rust-bindings/libuta_rust/src/bindings.rs b/rust-bindings/libuta_rust/src/bindings.rs new file mode 100644 index 0000000..e7440bf --- /dev/null +++ b/rust-bindings/libuta_rust/src/bindings.rs @@ -0,0 +1,28 @@ +/* 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, 2025 +* +* Authors: +* Christian P. Feist +* Hermann Seuschek +* +* This work is licensed under the terms of the Apache Software License +* 2.0. See the COPYING file in the top-level directory. +* +* 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")); + diff --git a/rust-bindings/libuta_rust/src/lib.rs b/rust-bindings/libuta_rust/src/lib.rs new file mode 100644 index 0000000..bd7775f --- /dev/null +++ b/rust-bindings/libuta_rust/src/lib.rs @@ -0,0 +1,333 @@ +/* Unified Trust Anchor API Rust Wrapper +* +* This code wraps the low-level C-bindings to make it more accessible +* for Rust +* +* Copyright (c) Siemens Mobility GmbH, 2025 +* +* Authors: +* Christian P. Feist +* Hermann Seuschek +* +* 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 +*/ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +mod bindings; + +pub use crate::api::UtaApiV1; + +pub mod api { + // Note: Here we only use the necessary symbols from the low-level wrapper + use crate::bindings::{UTA_SUCCESS, + UTA_INVALID_KEY_LENGTH, + UTA_INVALID_DV_LENGTH, + UTA_INVALID_KEY_SLOT, + uta_version_t, + uta_context_v1_t, + uta_api_v1_t, + uta_rc, + uta_init_v1 }; + use std::result::Result; + use std::error::Error; + use std::fmt; + + const UUID_SZ: usize = 16; + + pub type UtaVersion = uta_version_t; + + #[derive(Debug, PartialEq, Copy, Clone)] + pub enum UtaRc { + SUCCESS, + INVALID_KEY_LENGTH, + INVALID_DV_LENGTH, + INVALID_KEY_SLOT, + TA_ERROR + } + + fn encode_uta_rc(rc: uta_rc) -> UtaRc { + match rc { + UTA_SUCCESS => UtaRc::SUCCESS, + UTA_INVALID_KEY_LENGTH => UtaRc::INVALID_KEY_LENGTH, + UTA_INVALID_DV_LENGTH => UtaRc::INVALID_DV_LENGTH, + UTA_INVALID_KEY_SLOT => UtaRc::INVALID_KEY_SLOT, + _ => UtaRc::TA_ERROR + } + } + + #[derive(Debug)] + pub struct UtaError { + rc: UtaRc + } + + impl UtaError { + fn new(err_rc: uta_rc) -> UtaError { + UtaError {rc: encode_uta_rc(err_rc)} + } + + pub fn get_rc(&self) -> UtaRc { + self.rc + } + } + + impl fmt::Display for UtaError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.rc) + } + } + + impl Error for UtaError { + fn description(&self) -> &str { + "An UTA error occurred, got error code {:?}" + } + } + + pub struct UtaApiV1 { + api: uta_api_v1_t, + context: Vec, + } + + impl UtaApiV1 { + // Constructor: Here we translate the libuta methods + // and the libuta context to an appropriate Rust struct/object. + pub fn new() -> Result { + let mut api = uta_api_v1_t { + context_v1_size: None, + len_key_max: None, + open: None, + close: None, + derive_key: None, + get_device_uuid: None, + get_random: None, + get_version: None, + self_test: None + }; + let rc = unsafe { uta_init_v1(&mut api as *mut uta_api_v1_t) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + + let context_size = unsafe { (api.context_v1_size.unwrap())() }; + Ok(UtaApiV1{api, context: vec![0u8; context_size]}) + } + + pub fn derive_key(&mut self, len_key: usize, dv: &[u8], key_slot: u8) -> Result, UtaError> { + let context_ptr = self.context.as_mut_ptr() as *const uta_context_v1_t; + let mut rc = unsafe { (self.api.open.unwrap())(context_ptr) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + + let mut key: Vec = vec![0; len_key]; + rc = unsafe { (self.api.derive_key.unwrap())(context_ptr, (key).as_mut_ptr(), + len_key, dv.as_ptr(), dv.len(), key_slot) }; + unsafe { (self.api.close.unwrap())(context_ptr) }; + + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + Ok(key) + } + + pub fn get_random(&mut self, len_random: u32) -> Result, UtaError> { + let context_ptr = self.context.as_mut_ptr() as *const uta_context_v1_t; + let mut rc = unsafe { (self.api.open.unwrap())(context_ptr) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + let mut random: Vec = vec![0;len_random as usize]; + rc = unsafe { (self.api.get_random.unwrap())(context_ptr, + (random).as_mut_ptr(), random.len()) }; + unsafe { (self.api.close.unwrap())(context_ptr) }; + + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + Ok(random) + } + + pub fn get_device_uuid(&mut self) -> Result, UtaError> { + let context_ptr = self.context.as_mut_ptr() as *const uta_context_v1_t; + let mut rc = unsafe { (self.api.open.unwrap())(context_ptr) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + + let mut uuid = vec![0u8; UUID_SZ]; + rc = unsafe { (self.api.get_device_uuid.unwrap())(context_ptr, uuid.as_mut_ptr()) }; + unsafe { (self.api.close.unwrap())(context_ptr) }; + + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + Ok(uuid) + } + + pub fn self_test(&mut self) -> Result<(), UtaError> { + let context_ptr = self.context.as_mut_ptr() as *const uta_context_v1_t; + let mut rc = unsafe { (self.api.open.unwrap())(context_ptr) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + rc = unsafe { (self.api.self_test.unwrap())(context_ptr) }; + unsafe { (self.api.close.unwrap())(context_ptr) }; + + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + Ok(()) + + } + + pub fn get_version(&mut self) -> Result { + let context_ptr = self.context.as_mut_ptr() as *const uta_context_v1_t; + let mut rc = unsafe { (self.api.open.unwrap())(context_ptr) }; + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + let mut version = UtaVersion { + uta_type: 0, + major: 0, + minor: 0, + patch: 0, + }; + rc = unsafe { (self.api.get_version.unwrap())(context_ptr, &mut version as *mut uta_version_t) }; + unsafe { (self.api.close.unwrap())(context_ptr) }; + + if rc != UTA_SUCCESS { + return Err(UtaError::new(rc)); + } + Ok(version) + } + } +} + +#[cfg(test)] +mod tests { + use super::api::*; + use std::fs::File; + use std::io::{Read}; + + #[test] + fn get_key_ok() { + let mut uta = UtaApiV1::new(); + match uta { + Ok(ref mut api) => { + let dv = vec![1u8; 8]; + let ref_key = vec![ 141, 243, 3, 60, + 242, 217, 255, 175, + 133, 63, 236, 185, + 124, 72, 113, 96, + 25, 85, 33, 157, + 11, 96, 53, 225, + 189, 46, 160, 242, + 172, 53, 62, 102 ]; + let res = api.derive_key(32, &dv, 0); + + match res { + Ok(res_key) => assert_eq!(res_key, ref_key), + Err(e) => panic!("Error in get_key, returned {:?}", e) + } + }, + Err(e) => panic!("Error getting UTA API, returned {:?}", e) + } + } + + #[test] + fn get_random_ok() { + let mut uta = UtaApiV1::new(); + match uta { + Ok(ref mut api) => { + let res_rnd = api.get_random(32); + match res_rnd { + Ok(rnd) => assert_ne!(rnd, vec![0u8, 32]), + Err(e) => panic!("Error in get_random, returned {:?}", e) + } + }, + Err(e) => panic!("Error getting UTA API, returned {:?}", e) + } + } + + #[test] + fn get_device_uuid_ok() { + let mut uta = UtaApiV1::new(); + match uta { + Ok(ref mut api) => { + // The UTA_SIM implementation retrieves the device UUID from /etc/machine-id. + // Accordingly, this test reads the same file and compares the resulting value. + let mut file = match File::open("/etc/machine-id") { + Ok(f) => f, + Err(e) => panic!("Error opening /etc/machine-id: {}", e) + }; + + let mut machine_id = String::new(); + if let Err(e) = file.read_to_string(&mut machine_id) { + panic!("Error reading /etc/machine-id: {}", e) + } + machine_id = machine_id.trim().to_string(); + + if machine_id.len() != 32 { + panic!("Invalid machine-id length: expected 32 hex characters, got {}", machine_id.len()); + } + + let mut machine_id_vec: Vec = Vec::with_capacity(16); + for i in 0..16 { + let byte_str = &machine_id[i * 2..i * 2 + 2]; + match u8::from_str_radix(byte_str, 16) { + Ok(b) => machine_id_vec.push(b), + Err(e) => panic!("Failed to parse hex at position {}: {}", i, e) + } + } + + let res_uuid = api.get_device_uuid(); + match res_uuid { + Ok(uuid) => assert_eq!(uuid, machine_id_vec), + Err(e) => panic!("Error in get_device_uuid, returned {:?}", e) + } + }, + Err(e) => panic!("Error getting UTA API, returned {:?}", e) + } + } + + #[test] + fn self_test_ok() { + let mut uta = UtaApiV1::new(); + match uta { + Ok(ref mut api) => api.self_test().expect("Error in get_self_test"), + Err(e) => panic!("Error getting UTA API, returned {:?}", e) + } + } + + #[test] + fn get_version_ok() { + let mut uta = UtaApiV1::new(); + match uta { + Ok(ref mut api) => { + let ref_version = UtaVersion { + uta_type: 0, + major: 1, + minor: 2, + patch: 0, + }; + let res_ver = api.get_version(); + + match res_ver { + Ok(ver) => { + assert_eq!(ver.uta_type, ref_version.uta_type); + assert_eq!(ver.major, ref_version.major); + assert_eq!(ver.minor, ref_version.minor); + assert_eq!(ver.patch, ref_version.patch); + }, + Err(e) => panic!("Error in get_version, returned {:?}", e) + } + }, + Err(e) => panic!("Error getting UTA API, returned {:?}", e) + } + } +}