diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..b58b603f
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..16ece649
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 00000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..44126d1b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/tech_shield_rust_crypto.iml b/.idea/tech_shield_rust_crypto.iml
new file mode 100644
index 00000000..cf84ae4a
--- /dev/null
+++ b/.idea/tech_shield_rust_crypto.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..10efcb2f
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,16 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug",
+ "program": "${workspaceFolder}/",
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 49004566..a90f3fe8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -137,7 +137,7 @@ dependencies = [
"futures-io",
"futures-lite 2.3.0",
"parking",
- "polling 3.7.1",
+ "polling 3.7.2",
"rustix 0.38.34",
"slab",
"tracing",
@@ -405,7 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
+ "rand_core",
"subtle",
"zeroize",
]
@@ -430,25 +430,31 @@ dependencies = [
"base64 0.22.1",
"ed25519-dalek",
"futures",
+ "hex",
"libloading",
- "nitrokey",
+ "md-5",
"once_cell",
"openssl",
"rand",
"reqwest",
+ "ring",
"robusta_jni",
+ "rsa",
"serde",
"serde_json",
+ "sha2",
"sodiumoxide",
"test-case",
"tokio",
"tracing",
"tracing-android",
"tracing-appender",
+ "tracing-attributes",
"tracing-subscriber",
"tss-esapi",
"windows",
"x25519-dalek",
+ "x509-cert",
"yubikey",
]
@@ -642,7 +648,7 @@ dependencies = [
"hkdf",
"pem-rfc7468",
"pkcs8",
- "rand_core 0.6.4",
+ "rand_core",
"sec1",
"subtle",
"zeroize",
@@ -741,7 +747,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
- "rand_core 0.6.4",
+ "rand_core",
"subtle",
]
@@ -915,17 +921,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "getrandom"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
-]
-
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -934,7 +929,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
]
[[package]]
@@ -962,7 +957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
- "rand_core 0.6.4",
+ "rand_core",
"subtle",
]
@@ -997,6 +992,18 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hkdf"
version = "0.12.4"
@@ -1287,7 +1294,7 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.9",
"libc",
"windows-sys 0.48.0",
]
@@ -1348,7 +1355,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
- "spin",
+ "spin 0.5.2",
]
[[package]]
@@ -1432,11 +1439,21 @@ dependencies = [
"stable_deref_trait",
]
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest",
+]
+
[[package]]
name = "memchr"
-version = "2.7.2"
+version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
@@ -1466,7 +1483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
+ "wasi",
"windows-sys 0.48.0",
]
@@ -1487,27 +1504,6 @@ dependencies = [
"tempfile",
]
-[[package]]
-name = "nitrokey"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddeb2d19d5499ab4740c0131562e8c4b2c13f8954677be4318c1efc944911531"
-dependencies = [
- "lazy_static",
- "libc",
- "nitrokey-sys",
- "rand_core 0.5.1",
-]
-
-[[package]]
-name = "nitrokey-sys"
-version = "3.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d88466a33516e986e87aeb072307356605bb9ac5b13cd95647ee53a6c5d09641"
-dependencies = [
- "cc",
-]
-
[[package]]
name = "nom"
version = "7.1.3"
@@ -1599,7 +1595,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.3.9",
"libc",
]
@@ -1909,13 +1905,13 @@ dependencies = [
[[package]]
name = "polling"
-version = "3.7.1"
+version = "3.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e6a007746f34ed64099e88783b0ae369eaa3da6392868ba262e2af9b8fbaea1"
+checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b"
dependencies = [
"cfg-if",
"concurrent-queue",
- "hermit-abi",
+ "hermit-abi 0.4.0",
"pin-project-lite",
"rustix 0.38.34",
"tracing",
@@ -1992,7 +1988,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
- "rand_core 0.6.4",
+ "rand_core",
]
[[package]]
@@ -2002,16 +1998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core 0.6.4",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
-dependencies = [
- "getrandom 0.1.16",
+ "rand_core",
]
[[package]]
@@ -2020,14 +2007,14 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom 0.2.15",
+ "getrandom",
]
[[package]]
name = "redox_syscall"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
+checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
dependencies = [
"bitflags 2.5.0",
]
@@ -2113,6 +2100,21 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "robusta-codegen"
version = "0.2.2"
@@ -2153,7 +2155,7 @@ dependencies = [
"num-traits",
"pkcs1",
"pkcs8",
- "rand_core 0.6.4",
+ "rand_core",
"sha2",
"signature 2.2.0",
"spki",
@@ -2406,7 +2408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
- "rand_core 0.6.4",
+ "rand_core",
]
[[package]]
@@ -2462,6 +2464,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
[[package]]
name = "spki"
version = "0.7.3"
@@ -2785,7 +2793,6 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
- "log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -2874,7 +2881,7 @@ checksum = "a9ba6594ded739cb539f8ffcd3713f6c21d4525c47314bbc6de15c0cd251aedf"
dependencies = [
"bitfield",
"enumflags2",
- "getrandom 0.2.15",
+ "getrandom",
"hostname-validator",
"log",
"mbox",
@@ -2911,6 +2918,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
[[package]]
name = "url"
version = "2.5.1"
@@ -2940,7 +2953,7 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
dependencies = [
- "getrandom 0.2.15",
+ "getrandom",
]
[[package]]
@@ -2992,12 +3005,6 @@ dependencies = [
"try-lock",
]
-[[package]]
-name = "wasi"
-version = "0.9.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
-
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -3113,9 +3120,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
-version = "0.57.0"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core",
"windows-targets 0.52.5",
@@ -3123,9 +3130,9 @@ dependencies = [
[[package]]
name = "windows-core"
-version = "0.57.0"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6"
dependencies = [
"windows-implement",
"windows-interface",
@@ -3135,9 +3142,9 @@ dependencies = [
[[package]]
name = "windows-implement"
-version = "0.57.0"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
@@ -3146,9 +3153,9 @@ dependencies = [
[[package]]
name = "windows-interface"
-version = "0.57.0"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
@@ -3332,7 +3339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [
"curve25519-dalek",
- "rand_core 0.6.4",
+ "rand_core",
"serde",
"zeroize",
]
@@ -3396,7 +3403,7 @@ dependencies = [
"p384",
"pbkdf2",
"pcsc",
- "rand_core 0.6.4",
+ "rand_core",
"rsa",
"secrecy",
"sha1",
diff --git a/Cargo.toml b/Cargo.toml
index 65a9c7b2..b26ebb4b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,35 +32,46 @@ hcvault = []
core = []
linux = ["tpm", "tss-esapi"]
macos = []
-nitro = ["hsm", "nitrokey"]
std = []
tpm = []
win = ["tpm", "windows"]
yubi = ["hsm", "yubikey"]
[dependencies]
+yubikey = { version = "0.8.0", optional = true, features = ["untested"] }
+sha2 = "0.10.8"
+ring = "0.17.8"
+tracing-attributes = "0.1.15"
anyhow = "*"
async-std = "*"
futures = "*"
-nitrokey = { version = "0.9.0", optional = true }
once_cell = "1.14.0"
-windows = { version = "0.57.0", features = ["Win32_Security_Tpm", "Security_Cryptography_Core", "Win32_Security_Cryptography"], optional = true }
+windows = { version = "0.56.0", features = [
+ "Win32_Security_Tpm",
+ "Security_Cryptography_Core",
+ "Win32_Security_Cryptography",
+], optional = true }
tss-esapi = { version = "7.5.0", optional = true }
serde = { version = "*", features = ["derive"] }
serde_json = "1.0.109"
-tracing = { version = "0.1.40", features = ["std", "log"] }
+tracing = { version = "0.1.40", features = ["attributes"] }
tracing-subscriber = "0.3.18"
tracing-appender = "0.2.3"
-yubikey = { version = "0.8.0", optional = true }
+x509-cert = "0.2.5"
+base64 = "0.22.1"
+hex = "0.4.3"
+rsa = "0.9.6"
+md-5 = "0.10.6"
+openssl = "0.10.64"
reqwest = { version = "0.12.4", features = ["json"] }
tokio = { version = "1", features = ["full"] }
-openssl = "0.10.64"
-base64 = "0.22.1"
ed25519-dalek = "2.1.1"
arrayref = "0.3.7"
sodiumoxide = "0.2.7"
rand = "0.8.5"
-x25519-dalek = { version = "2.0.1", features = ["static_secrets"], optional = true }
+x25519-dalek = { version = "2.0.1", features = [
+ "static_secrets",
+], optional = true }
robusta_jni = { version = "0.2", optional = true }
libloading = { version = "0.8.3", optional = true }
tracing-android = { version = "0.2.0", optional = true }
diff --git a/src/common/factory.rs b/src/common/factory.rs
index 8d119ef3..aeadb77d 100644
--- a/src/common/factory.rs
+++ b/src/common/factory.rs
@@ -17,7 +17,7 @@ type SecurityModuleInstances = Lazy>;
///
/// This enum categorizes security modules into HSM (Hardware Security Module) and
/// TPM (Trusted Platform Module), allowing for a unified interface when working with different types of security modules.
-#[repr(C)]
+//#[repr(C)]
#[derive(Eq, Hash, PartialEq, Clone, Debug)]
pub enum SecurityModule {
#[cfg(feature = "hsm")]
diff --git a/src/common/traits/key_handle.rs b/src/common/traits/key_handle.rs
index c39d3164..04ed7655 100644
--- a/src/common/traits/key_handle.rs
+++ b/src/common/traits/key_handle.rs
@@ -21,6 +21,8 @@ pub enum GenericKeyHandle {
Linux(TssKeyHandle),
#[cfg(feature = "win")]
Windows(NCRYPT_KEY_HANDLE),
+ #[cfg(feature = "yubikey")]
+ YubiKey(KeyHandle),
}
/// Defines a common interface for cryptographic key operations.
diff --git a/src/hsm/mod.rs b/src/hsm/mod.rs
index f56ca7c0..2672d21b 100644
--- a/src/hsm/mod.rs
+++ b/src/hsm/mod.rs
@@ -1,3 +1,92 @@
+/// # High-Level Overview
+///
+/// This module provides configuration structures for hardware security module (HSM) providers.
+/// It includes the `HsmProviderConfig` structure, which encapsulates information about the
+/// cryptographic algorithms and key usages supported by the HSM provider.
+///
+/// ## Module Structure
+///
+/// The module consists of the following components:
+///
+/// - `core`: Contains core functionality for HSM providers.
+/// - `nitrokey`: Provides support for Nitrokey HSM devices.
+/// - `yubikey`: Offers support for YubiKey HSM devices (conditionally compiled with the `yubi` feature).
+///
+/// ## `HsmProviderConfig` Structure
+///
+/// The `HsmProviderConfig` struct defines the configuration parameters for an HSM provider. It contains:
+///
+/// - `key_algorithm`: Specifies the asymmetric encryption algorithm supported by the HSM.
+/// - `key_usage`: Specifies the key usages supported by the HSM.
+///
+/// ## Usage
+///
+/// To use this module, follow these steps:
+///
+/// 1. Import the required modules: `crypto::{algorithms::encryption::AsymmetricEncryption, KeyUsage}`.
+/// 2. Define a new configuration using `HsmProviderConfig::new`.
+/// 3. Pass the configuration to the HSM provider for initialization.
+///
+/// ## Example
+///
+/// ```rust
+/// use crate::common::crypto::{algorithms::encryption::AsymmetricEncryption, KeyUsage};
+/// use crate::common::traits::module_provider_config::ProviderConfig;
+///
+/// // Import the HSM provider configuration module
+/// use crate::hsm::HsmProviderConfig;
+///
+/// // Define the HSM configuration with RSA encryption and key usage for signing and encryption
+/// let config = HsmProviderConfig::new(
+/// AsymmetricEncryption::Rsa(KeyBits::Bits2048),
+/// vec![KeyUsage::SignEncrypt],
+/// );
+///
+/// // Pass the configuration to the HSM provider for initialization
+/// let provider = initialize_hsm_provider(config);
+/// ```
+use crate::common::crypto::algorithms::encryption::AsymmetricEncryption;
+use crate::common::traits::module_provider_config::ProviderConfig;
+
+/// The core functionality for hardware security module (HSM) providers.
pub mod core;
+
+/// Provides support for Nitrokey HSM devices.
pub mod nitrokey;
+
+use std::any::Any;
+
+/// Provides support for YubiKey HSM devices (conditionally compiled with the `yubi` feature).
+#[cfg(feature = "yubi")]
pub mod yubikey;
+
+/// Configuration parameters for an HSM provider.
+#[derive(Debug)]
+pub struct HsmProviderConfig {
+ /// The asymmetric encryption algorithm supported by the HSM.
+ pub(super) key_algorithm: AsymmetricEncryption,
+}
+
+impl ProviderConfig for HsmProviderConfig {
+ /// Returns a reference to the dynamic `Any` trait object.
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+}
+
+impl HsmProviderConfig {
+ /// Creates a new instance of `HsmProviderConfig`.
+ ///
+ /// # Arguments
+ ///
+ /// - `key_algorithm`: The asymmetric encryption algorithm supported by the HSM.
+ /// - `key_usage`: The key usages supported by the HSM.
+ ///
+ /// # Returns
+ ///
+ /// A boxed trait object representing the HSM provider configuration.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new(key_algorithm: AsymmetricEncryption) -> Box {
+ Box::new(Self { key_algorithm })
+ }
+}
diff --git a/src/hsm/yubikey/key_handle.rs b/src/hsm/yubikey/key_handle.rs
new file mode 100644
index 00000000..cf393b85
--- /dev/null
+++ b/src/hsm/yubikey/key_handle.rs
@@ -0,0 +1,350 @@
+use super::YubiKeyProvider;
+use crate::{
+ common::{
+ crypto::algorithms::{
+ encryption::{AsymmetricEncryption, EccCurves, EccSchemeAlgorithm},
+ KeyBits,
+ },
+ error::SecurityModuleError,
+ traits::key_handle::KeyHandle,
+ },
+ hsm::core::error::HsmError,
+};
+
+use ::yubikey::piv;
+use ::yubikey::{
+ piv::{AlgorithmId, SlotId},
+ MgmKey,
+};
+use base64::{engine::general_purpose, Engine};
+use openssl::{
+ ec::EcKey,
+ hash::MessageDigest,
+ pkey::PKey,
+ rsa::{Padding, Rsa},
+ sign::Verifier,
+};
+use rsa::sha2::Digest;
+use sha2::Sha256;
+use tracing::instrument;
+use x509_cert::der::zeroize::Zeroizing;
+
+const BYTES_1024: usize = 128;
+const BYTES_2048: usize = 256;
+/// Provides cryptographic operations for asymmetric keys on a YubiKey,
+/// such as signing, encryption, decryption, and signature verification.
+
+/// Signs data using the cryptographic key on a YubiKey.
+///
+/// This method hashes the input data using SHA-256 and then signs the hash.
+///
+/// # Arguments
+///
+/// * `data` - The data to be signed.
+///
+/// # Returns
+///
+/// A `Result` containing the signature as a `Vec` on success, or a `yubikey::Error` on failure.
+///
+
+impl KeyHandle for YubiKeyProvider {
+ #[instrument]
+ fn sign_data(&self, data: &[u8]) -> Result, SecurityModuleError> {
+ let yubikey = self.yubikey.as_ref().unwrap();
+ let mut yubikey = yubikey.lock().unwrap();
+ let data = data.to_vec();
+ let key_algo = self.key_algo.unwrap();
+
+ // Input gets hashed with SHA-256
+ let mut hasher = Sha256::new();
+ hasher.update(data);
+ let data = hasher.finalize();
+ let mut data: &[u8] = &data;
+
+ //TODO After PIN input implementation in App, insert code for re-authentication
+ let verify = yubikey.verify_pin(self.pin.as_ref());
+ if !verify.is_ok() {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "PIN verification failed".to_string(),
+ )));
+ }
+ let auth = yubikey.authenticate(MgmKey::new(self.management_key.unwrap()).unwrap());
+ if !auth.is_ok() {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Authentication failed".to_string(),
+ )));
+ }
+
+ let signature: Result>, yubikey::Error>;
+ let mut vec_data: Vec = create_digest_info(data).unwrap();
+ let algorithm_id: AlgorithmId;
+
+ match key_algo {
+ AsymmetricEncryption::Rsa(KeyBits::Bits1024) => {
+ algorithm_id = AlgorithmId::Rsa1024;
+ vec_data = apply_pkcs1v15_padding(&vec_data, BYTES_1024);
+ data = &vec_data.as_slice();
+ }
+ AsymmetricEncryption::Rsa(KeyBits::Bits2048) => {
+ algorithm_id = AlgorithmId::Rsa2048;
+ vec_data = apply_pkcs1v15_padding(&vec_data, BYTES_2048);
+ data = vec_data.as_slice();
+ }
+
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P256)) => {
+ algorithm_id = AlgorithmId::EccP256;
+ }
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P384)) => {
+ algorithm_id = AlgorithmId::EccP384;
+ }
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+ signature = piv::sign_data(
+ &mut yubikey,
+ data,
+ algorithm_id,
+ SlotId::Retired(self.slot_id.unwrap()),
+ );
+ match signature {
+ Ok(buffer) => {
+ let signature = general_purpose::STANDARD.encode(&buffer);
+ let signature = general_purpose::STANDARD
+ .decode(signature)
+ .expect("Failed to decode signature");
+ Ok(signature)
+ }
+ Err(err) => Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ err.to_string(),
+ ))),
+ }
+ }
+
+ /// Decrypts data encrypted with the corresponding public key on a YubiKey.
+ /// Only works with PKCS#1 v1.5 padding.
+ /// Utilizes the YubiKey API for decryption.
+ ///
+ /// # Arguments
+ ///
+ /// * `encrypted_data` - The data to be decrypted.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing the decrypted data as a `Vec` on success, or a `yubikey::Error` on failure.
+ #[instrument]
+ fn decrypt_data(&self, encrypted_data: &[u8]) -> Result, SecurityModuleError> {
+ let yubikey = self.yubikey.as_ref().unwrap();
+ let mut yubikey = yubikey.lock().unwrap();
+
+ let decrypted: Result>, &str>;
+ let key_algo = self.key_algo.unwrap();
+
+ match key_algo {
+ AsymmetricEncryption::Rsa(KeyBits::Bits1024) => {
+ decrypted = piv::decrypt_data(
+ &mut yubikey,
+ encrypted_data,
+ piv::AlgorithmId::Rsa1024,
+ piv::SlotId::Retired(self.slot_id.unwrap()),
+ )
+ .map_err(|_| "Failed to decrypt data");
+ }
+ AsymmetricEncryption::Rsa(KeyBits::Bits2048) => {
+ decrypted = piv::decrypt_data(
+ &mut yubikey,
+ encrypted_data,
+ piv::AlgorithmId::Rsa2048,
+ piv::SlotId::Retired(self.slot_id.unwrap()),
+ )
+ .map_err(|_| "Failed to decrypt data");
+ }
+ // The Yubikey do not support decryption with ECC, see:
+ // https://docs.yubico.com/yesdk/users-manual/application-piv/apdu/auth-decrypt.html
+ /*
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P256)) => {}
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P384)) => {}
+ */
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+ fn remove_pkcs1_padding(buffer: &[u8]) -> Result, &'static str> {
+ let mut pos = 2; // Start nach dem ersten Padding-Byte `0x02`
+ if buffer[0] != 0 {
+ return Err("Invalid padding");
+ }
+ // Überspringe alle non-zero Bytes
+ while pos < buffer.len() && buffer[pos] != 0 {
+ pos += 1;
+ }
+ if pos >= buffer.len() {
+ return Err("No data after padding");
+ }
+ // Das erste `0x00` Byte überspringen, um die tatsächlichen Daten zu erhalten
+ Ok(buffer[pos + 1..].to_vec())
+ }
+ match decrypted {
+ Ok(buffer) => match remove_pkcs1_padding(&buffer) {
+ Ok(data) => {
+ return Ok(data);
+ }
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(
+ crate::hsm::core::error::HsmError::DeviceSpecific(err.to_string()),
+ ));
+ }
+ },
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(
+ crate::hsm::core::error::HsmError::DeviceSpecific(err.to_string()),
+ ));
+ }
+ }
+ }
+
+ /// Encrypts data with the cryptographic key on a YubiKey.
+ ///
+ /// Uses the YubiKey API for encryption.
+ ///
+ /// # Arguments
+ ///
+ /// * `data` - The data to be encrypted.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` containing the encrypted data as a `Vec` on success, or a `yubikey::Error` on failure.
+ /// Möglicher Fehler: Müssen Daten vor dem returnen noch in Base64 umgewandelt werden?
+ #[instrument]
+ fn encrypt_data(&self, data: &[u8]) -> Result, SecurityModuleError> {
+ match self.key_algo.unwrap() {
+ AsymmetricEncryption::Rsa(KeyBits::Bits1024)
+ | AsymmetricEncryption::Rsa(KeyBits::Bits2048) => {
+ let rsa = Rsa::public_key_from_pem(self.pkey.trim().as_bytes())
+ .map_err(|_| "failed to create RSA from public key PEM");
+ let mut encrypted_data = vec![0; rsa.clone().unwrap().size() as usize];
+ let _ = rsa
+ .map_err(|_| "")
+ .unwrap()
+ .public_encrypt(data, &mut encrypted_data, Padding::PKCS1)
+ .map_err(|_| "failed to encrypt data");
+ Ok(encrypted_data)
+ }
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+ }
+
+ /// Verifies a signature against the provided data using the YubiKey.
+ ///
+ /// This method hashes the input data using SHA-256 and then verifies the signature.
+ ///
+ /// # Arguments
+ ///
+ /// * `data` - The original data associated with the signature.
+ /// * `signature` - The signature to be verified.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` indicating whether the signature is valid (`true`) or not (`false`),
+ /// or a `SecurityModuleError` on failure.
+ #[instrument]
+ fn verify_signature(&self, data: &[u8], signature: &[u8]) -> Result {
+ match self.key_algo.unwrap() {
+ AsymmetricEncryption::Rsa(KeyBits::Bits1024)
+ | AsymmetricEncryption::Rsa(KeyBits::Bits2048) => {
+ let rsa = Rsa::public_key_from_pem(self.pkey.trim().as_bytes())
+ .expect("failed to create RSA from public key PEM");
+ let key_pkey = PKey::from_rsa(rsa).unwrap();
+
+ let mut verifier = Verifier::new(MessageDigest::sha256(), &key_pkey)
+ .expect("failed to create verifier");
+ verifier
+ .update(data)
+ .map_err(|_| "failed to update verifier")
+ .unwrap();
+ if verifier
+ .verify(signature)
+ .expect("failed to verify signature")
+ {
+ return Result::Ok(true);
+ } else {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Signature verification failed".to_string(),
+ )));
+ }
+ }
+
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P256))
+ | AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P384)) => {
+ let ecc = EcKey::public_key_from_pem(self.pkey.trim().as_bytes())
+ .expect("failed to create ECC from public key PEM");
+ let ecc = PKey::from_ec_key(ecc).expect("failed to create PKey from ECC");
+
+ let mut verifier = Verifier::new(MessageDigest::sha256(), &ecc)
+ .expect("failed to create verifier");
+ verifier
+ .update(data)
+ .map_err(|_| "failed to update verifier")
+ .unwrap();
+ if verifier
+ .verify(signature)
+ .expect("failed to verify signature")
+ {
+ return Result::Ok(true);
+ } else {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Signature verification failed".to_string(),
+ )));
+ }
+ }
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+ }
+}
+
+#[instrument]
+fn create_digest_info(digest: &[u8]) -> Result, Box> {
+ let oid_sha256: [u8; 9] = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]; // OID für SHA-256
+ let mut digest_info = vec![];
+
+ // ASN.1 SEQUENCE Header
+ digest_info.extend_from_slice(&[
+ 0x30, 0x31, // SEQUENCE, Länge 49
+ 0x30, 0x0d, // SEQUENCE, Länge 13
+ 0x06, 0x09, // OID Header
+ ]);
+ digest_info.extend_from_slice(&oid_sha256); // OID SHA-256
+ digest_info.extend_from_slice(&[
+ 0x05, 0x00, // NULL
+ 0x04, 0x20, // OCTET STRING, Länge 32
+ ]);
+ digest_info.extend_from_slice(digest); // SHA-256 Hash-Wert
+
+ Ok(digest_info)
+}
+
+#[instrument]
+fn apply_pkcs1v15_padding(data: &[u8], block_size: usize) -> Vec {
+ let padding_length = block_size - data.len() - 3;
+ let mut padded_data = Vec::with_capacity(block_size);
+ padded_data.push(0x00);
+ padded_data.push(0x01);
+ for _ in 0..padding_length {
+ padded_data.push(0xFF);
+ }
+ padded_data.push(0x00);
+ padded_data.extend_from_slice(data);
+ padded_data
+}
diff --git a/src/hsm/yubikey/mod.rs b/src/hsm/yubikey/mod.rs
index 8b137891..b0980e91 100644
--- a/src/hsm/yubikey/mod.rs
+++ b/src/hsm/yubikey/mod.rs
@@ -1 +1,49 @@
+use crate::common::crypto::algorithms::encryption::AsymmetricEncryption;
+use ::yubikey::{piv::RetiredSlotId, YubiKey};
+use std::sync::{Arc, Mutex};
+use tracing::instrument;
+pub mod key_handle;
+pub mod provider;
+
+/// A YubiKey-based cryptographic provider for managing cryptographic keys and performing
+/// cryptographic operations.
+///
+/// This provider leverages the YubiKey API to interact with a YubiKey device for operations
+/// like signing, encryption, and decryption. It provides a secure and hardware-backed solution
+/// for managing cryptographic keys and performing cryptographic operations.
+
+// #[derive(cloe, Debug)]???
+#[derive(Debug)]
+pub struct YubiKeyProvider {
+ /// A unique identifier for the cryptographic key managed by this provider.
+ pub(super) pkey: String,
+ pub(super) slot_id: Option,
+ pub(super) key_algo: Option,
+ pub(super) yubikey: Option>>,
+ pin: String,
+ management_key: Option<[u8; 24]>,
+}
+
+impl YubiKeyProvider {
+ /// Constructs a new `YubiKeyProvider`.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_id` - A string identifier for the cryptographic key to be managed by this provider.
+ ///
+ /// # Returns
+ ///
+ /// A new instance of `YubiKeyProvider` with the specified `key_id`.
+ #[instrument]
+ pub fn new(key_id: String) -> Self {
+ Self {
+ pkey: String::new(),
+ slot_id: None,
+ key_algo: None,
+ yubikey: None,
+ pin: String::new(),
+ management_key: None,
+ }
+ }
+}
diff --git a/src/hsm/yubikey/provider.rs b/src/hsm/yubikey/provider.rs
new file mode 100644
index 00000000..f3a62d29
--- /dev/null
+++ b/src/hsm/yubikey/provider.rs
@@ -0,0 +1,629 @@
+use super::YubiKeyProvider;
+use crate::common::{
+ crypto::algorithms::{
+ encryption::{AsymmetricEncryption, EccCurves, EccSchemeAlgorithm},
+ KeyBits,
+ },
+ error::SecurityModuleError,
+ traits::module_provider::Provider,
+};
+use crate::hsm::{core::error::HsmError, HsmProviderConfig};
+use ::yubikey::{
+ piv::{self, AlgorithmId, RetiredSlotId, SlotId},
+ Error, YubiKey,
+};
+use base64::{engine::general_purpose, Engine};
+use std::any::Any;
+use std::sync::{Arc, Mutex};
+use tracing::instrument;
+use x509_cert::der::Encode;
+use yubikey::MgmKey;
+
+const SLOTS: [RetiredSlotId; 20] = [
+ RetiredSlotId::R1,
+ RetiredSlotId::R2,
+ RetiredSlotId::R3,
+ RetiredSlotId::R4,
+ RetiredSlotId::R5,
+ RetiredSlotId::R6,
+ RetiredSlotId::R7,
+ RetiredSlotId::R8,
+ RetiredSlotId::R9,
+ RetiredSlotId::R10,
+ RetiredSlotId::R11,
+ RetiredSlotId::R12,
+ RetiredSlotId::R13,
+ RetiredSlotId::R14,
+ RetiredSlotId::R15,
+ RetiredSlotId::R16,
+ RetiredSlotId::R17,
+ RetiredSlotId::R18,
+ RetiredSlotId::R19,
+ RetiredSlotId::R20,
+];
+
+/// IDs/addresses for read/write objects operations;
+/// see https://developers.yubico.com/yubico-piv-tool/Actions/read_write_objects.html
+const SLOTSU32: [u32; 20] = [
+ 0x005f_c10d,
+ 0x005f_c10e,
+ 0x005f_c10f,
+ 0x005f_c110,
+ 0x005f_c111,
+ 0x005f_c112,
+ 0x005f_c113,
+ 0x005f_c114,
+ 0x005f_c115,
+ 0x005f_c116,
+ 0x005f_c117,
+ 0x005f_c118,
+ 0x005f_c119,
+ 0x005f_c11a,
+ 0x005f_c11b,
+ 0x005f_c11c,
+ 0x005f_c11d,
+ 0x005f_c11e,
+ 0x005f_c11f,
+ 0x005f_c120,
+];
+
+/// Implements the `Provider` trait, providing cryptographic operations utilizing a YubiKey.
+///
+/// This implementation interacts with a YubiKey device for key management and cryptographic
+/// operations.
+impl Provider for YubiKeyProvider {
+ /// Creates a new cryptographic key identified by the provider given key_id.
+ ///
+ /// This method creates a persisted cryptographic key using the specified algorithm
+ /// and identifier, making it retrievable for future operations. The key is created
+ /// stored in the YubiKey.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_id` - A string slice that uniquely identifies the key for later usage.
+ /// * `config` - A boxed `ProviderConfig` containing configuration details for key-generating
+ ///
+ /// # Returns
+ ///
+ /// The generated Public Key will be stored in the Yubikey as Object with futher information
+ /// A `Result` that, on success, contains `Ok()`.
+ /// On failure, it returns a `yubikey::Error`.
+ ///
+ /// # Errors
+ /// Stick throws Error, if all Slots are used. We have coded a method to get all stored keys,
+ /// so that the user can see which slots are used.
+ /// We also coded a method, which can remove any stored key from the Yubikey.
+
+ #[instrument]
+ fn create_key(
+ &mut self,
+ key_id: &str,
+ config: Box,
+ ) -> Result<(), SecurityModuleError> {
+ if let Some(hsm_config) = config.downcast_ref::() {
+ self.key_algo = Some(hsm_config.key_algorithm);
+ let key_algo = self.key_algo.clone();
+ let key_algorithm;
+ match key_algo {
+ Some(algo) => key_algorithm = algo,
+ None => {
+ return Err(SecurityModuleError::InitializationError(
+ "No key algorithm found".to_string(),
+ ));
+ }
+ }
+
+ let slot: u32;
+ let slot_id;
+ let algorithm: AlgorithmId;
+
+ if !(self.load_key(key_id, config).is_ok()) {
+ let mut yubikey = self.yubikey.as_ref().unwrap().lock().unwrap();
+ let _ = yubikey.verify_pin(self.pin.as_ref());
+ let _ = yubikey.authenticate(MgmKey::new(self.management_key.unwrap()).unwrap());
+ match get_free_slot(&mut yubikey) {
+ Ok(free) => {
+ slot_id = free;
+ }
+ Err(err) => {
+ return Err(SecurityModuleError::InitializationError(err.to_string()));
+ }
+ }
+ } else {
+ slot_id = self.slot_id.unwrap();
+ }
+
+ fn generate_key(
+ mut yubikey: &mut YubiKey,
+ algorithm: AlgorithmId,
+ slot_id: RetiredSlotId,
+ ) -> Result<(RetiredSlotId, String), SecurityModuleError> {
+ let pkey: String;
+
+ let gen_key = piv::generate(
+ &mut yubikey,
+ SlotId::Retired(slot_id),
+ algorithm,
+ yubikey::PinPolicy::Default,
+ yubikey::TouchPolicy::Default,
+ );
+ match gen_key {
+ Ok(_) => {
+ let gen_key = gen_key.as_ref().unwrap().to_der().unwrap();
+ let gen_key = general_purpose::STANDARD.encode(&gen_key);
+ let gen_key = format!(
+ "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----",
+ gen_key.trim()
+ );
+ pkey = gen_key;
+ }
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ err.to_string(),
+ )))
+ }
+ }
+ Ok((slot_id, pkey))
+ }
+
+ match key_algorithm {
+ AsymmetricEncryption::Rsa(curve) => match curve {
+ KeyBits::Bits1024 => algorithm = AlgorithmId::Rsa1024,
+ KeyBits::Bits2048 => algorithm = AlgorithmId::Rsa2048,
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ },
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(curve)) => match curve {
+ EccCurves::P256 => algorithm = AlgorithmId::EccP256,
+ EccCurves::P384 => algorithm = AlgorithmId::EccP384,
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ },
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+
+ let algo: &str;
+ match key_algorithm {
+ AsymmetricEncryption::Rsa(KeyBits::Bits1024) => algo = "Rsa1024",
+ AsymmetricEncryption::Rsa(KeyBits::Bits2048) => algo = "Rsa2048",
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P256)) => {
+ algo = "EccP256"
+ }
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::P384)) => {
+ algo = "EccP384"
+ }
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key Algorithm not supported".to_string(),
+ )));
+ }
+ }
+
+ let mut yubikey = self.yubikey.as_ref().unwrap().lock().unwrap();
+ let (slot_id, pkey) = generate_key(&mut yubikey, algorithm, slot_id).unwrap();
+ self.slot_id = Some(slot_id);
+ self.pkey = pkey;
+
+ let pkey = self.pkey.clone();
+ slot = get_reference_u32slot(self.slot_id.unwrap());
+
+ let _ = yubikey.verify_pin(self.pin.as_ref());
+ let _ = yubikey.authenticate(MgmKey::new(self.management_key.unwrap()).unwrap());
+
+ match save_key_object(&mut yubikey, key_id, slot, &pkey, algo) {
+ Ok(_) => Ok(()),
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ err.to_string(),
+ )))
+ }
+ }
+ } else {
+ Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Failed to get the Configurations".to_string(),
+ )))
+ }
+ }
+
+ /// Loads an existing cryptographic key identified by `key_id`.
+ ///
+ /// This method attempts to load a persisted cryptographic key by its identifier from the YubiKey.
+ /// If successful, it returns a handle to the key for further
+ /// cryptographic operations.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_id` - A string slice that uniquely identifies the key to be loaded.
+ /// * `config` - A boxed `ProviderConfig` containing configuration details for the key.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` that, on success, contains `Ok(())`, indicating that the key was loaded successfully.
+ /// On failure, it returns a `SecurityModuleError`.
+ #[instrument]
+ fn load_key(&mut self, key_id: &str, config: Box) -> Result<(), SecurityModuleError> {
+ if let Some(hsm_config) = config.downcast_ref::() {
+ self.key_algo = Some(hsm_config.key_algorithm);
+ let mut yubikey = self.yubikey.as_ref().unwrap().lock().unwrap();
+ let mut found = false;
+ for i in 10..20 {
+ let _ = yubikey.verify_pin(self.pin.as_ref());
+ let _ = yubikey.authenticate(MgmKey::new(self.management_key.unwrap()).unwrap());
+ let data = yubikey.fetch_object(SLOTSU32[i]);
+ let mut output: Vec = Vec::new();
+ match data {
+ Ok(data) => {
+ output = data.to_vec();
+ }
+ Err(_) => {}
+ }
+
+ let data = output;
+ match parse_slot_data(&data) {
+ Ok((key_name, _, public_key, algo)) => {
+ let _keyalgo;
+ match self.key_algo {
+ Some(AsymmetricEncryption::Rsa(KeyBits::Bits1024)) => {
+ _keyalgo = "Rsa1024"
+ }
+ Some(AsymmetricEncryption::Rsa(KeyBits::Bits2048)) => {
+ _keyalgo = "Rsa2048"
+ }
+ Some(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P256,
+ ))) => _keyalgo = "EccP256",
+ Some(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P384,
+ ))) => _keyalgo = "EccP384",
+ _ => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Algorithm not supported".to_string(),
+ )))
+ }
+ }
+ if key_name == key_id.to_string() && _keyalgo == algo {
+ self.slot_id = Some(SLOTS[i - 10]);
+ self.pkey = public_key;
+ found = true;
+ break;
+ }
+ }
+ Err(_) => {
+ continue;
+ }
+ }
+ }
+
+ if !found {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Key not found".to_string(),
+ )));
+ } else {
+ Ok(())
+ }
+ } else {
+ Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Failed to get the Configurations".to_string(),
+ )))
+ }
+ }
+
+ /// Initializes the YubiKey module and returns a handle for cryptographic operations.
+ ///
+ /// This method initializes the YubiKey device and sets up the necessary environment
+ /// for cryptographic operations.
+ ///
+ /// # Arguments
+ ///
+ /// * `key_algorithm` - The asymmetric encryption algorithm to be used for the key.
+ /// * `hash` - An optional hash algorithm to be used with the key.
+ ///
+ /// # Returns
+ ///
+ /// A `Result` that, on success, contains `Ok(())`, indicating that the module was initialized successfully.
+ /// On failure, it returns a Yubikey based `Error`.
+ #[instrument]
+ fn initialize_module(&mut self) -> Result<(), SecurityModuleError> {
+ let yubi = YubiKey::open().map_err(|_| Error::NotFound);
+ let mut yubikey: YubiKey;
+ match yubi {
+ Ok(yubi) => {
+ yubikey = yubi;
+ }
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ err.to_string(),
+ )));
+ }
+ }
+ // Hier muesste die Pin Eingabe und die Managementkey Eingabe implementiert werden. Ist aktuell hardcoded.
+ self.pin = "123456".to_string();
+ self.management_key = Some(*MgmKey::default().as_ref());
+
+ let verify = yubikey.verify_pin(self.pin.as_ref());
+ match verify {
+ Ok(_) => {
+ self.yubikey = Some(Arc::new(Mutex::new(yubikey)));
+
+ Ok(())
+ }
+ Err(err) => {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ err.to_string(),
+ )));
+ }
+ }
+ }
+}
+
+/// Saves the key object to the YubiKey device.
+///
+/// This method saves a object to the YubiKey device. The object is stored in a slot and represents
+/// information about the key, such as the key name, slot, key and public key. This information
+/// belongs to a private key which is stored in a other Slot.
+///
+/// # Arguments
+/// * 'key_id' - A string slice that uniquely identifies the key for later usage.
+/// * 'slot_id' - An address where an object will be stored must be given.
+/// * 'pkey' - The public key which is intended to be stored.
+///
+/// # Returns
+///
+/// The saved Object will be stored in the Yubikey on a free Retired slot as Object with futher information
+/// A `Result` that, on success, contains `Ok()`.
+/// On failure, it returns a `yubikey::Error`.
+fn save_key_object(
+ yubikey: &mut YubiKey,
+ key_id: &str,
+ slot_id: u32,
+ pkey: &str,
+ algo: &str,
+) -> Result<(), yubikey::Error> {
+ let key_name = key_id;
+ let slot = slot_id.to_string();
+ let public_key = pkey;
+
+ let total_length = key_name.len() + 1 + slot.len() + 1 + public_key.len() + 1 + algo.len();
+ let mut data = vec![0u8; total_length];
+ let data_slice: &mut [u8] = &mut data;
+
+ let mut offset = 0;
+ data_slice[offset..offset + key_name.len()].copy_from_slice(key_name.as_bytes());
+ offset += key_name.len();
+ data_slice[offset] = 0;
+ offset += 1;
+
+ data_slice[offset..offset + slot.len()].copy_from_slice(slot.as_bytes());
+ offset += slot.len();
+ data_slice[offset] = 0;
+ offset += 1;
+
+ data_slice[offset..offset + public_key.len()].copy_from_slice(public_key.as_bytes());
+ offset += public_key.len();
+ data_slice[offset] = 0;
+ offset += 1;
+
+ data_slice[offset..offset + algo.len()].copy_from_slice(algo.as_bytes());
+
+ let saved = yubikey.save_object(slot_id, data_slice);
+ match saved {
+ Ok(()) => Ok(()),
+ Err(err) => Err(err),
+ }
+}
+
+/// parses the u8 Data to different Key-Information Strings
+///
+/// This method creates a persisted cryptographic key using the specified algorithm
+/// and identifier, making it retrievable for future operations. The key is created
+/// and stored in the YubiKey.
+///
+/// # Arguments
+///
+///* 'data' - This array reference contains important information, it provides: the key name, the slot where it is stored and the public key itself
+///
+/// # Returns
+///
+/// A `Result` that, on success, contains `Ok(key_name, slot, public_key)` where the individual information is given.
+/// On failure, it returns a `Utf8Error`.
+fn parse_slot_data(data: &[u8]) -> Result<(String, String, String, String), SecurityModuleError> {
+ let parts: Vec<&[u8]> = data.split(|&x| x == 0).collect();
+ if !(parts.len() < 4
+ || parts[0].is_empty()
+ || parts[1].is_empty()
+ || parts[2].is_empty()
+ || parts[3].is_empty())
+ {
+ let key_name = std::str::from_utf8(parts[0]).unwrap();
+ let slot = std::str::from_utf8(parts[1]).unwrap();
+ let public_key = std::str::from_utf8(parts[2]).unwrap();
+ let algo = std::str::from_utf8(parts[3]).unwrap();
+
+ Ok((
+ key_name.to_string(),
+ slot.to_string(),
+ public_key.to_string(),
+ algo.to_string(),
+ ))
+ } else {
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "Failed to parse the slot data".to_string(),
+ )));
+ }
+}
+
+/// Gets a free slot for storing a key object.
+///
+/// This method goes through the available slots on the YubiKey and returns the first free slot
+///
+/// # Arguments
+/// The method takes a Yubikey device as an input
+///
+/// # Returns
+///
+/// A `Result` that, on failure, returns the first free slot.
+/// On Success, it returns that no more free slots are available.
+fn get_free_slot(yubikey: &mut YubiKey) -> Result {
+ let mut end = false;
+ let mut slot_id: RetiredSlotId = SLOTS[0];
+ let mut counter = 0;
+ for i in 10..20 {
+ let data = yubikey.fetch_object(SLOTSU32[i]);
+ let mut output: Vec = Vec::new();
+ match data {
+ Ok(data) => {
+ output = data.to_vec();
+ }
+ Err(_) => {}
+ }
+ let data = output;
+ let parsed = parse_slot_data(&data);
+ if !parsed.is_ok() {
+ slot_id = SLOTS[i - 10];
+ end = true;
+ }
+
+ if end {
+ break;
+ }
+ counter += 1;
+ }
+ if counter <= 9 {
+ Ok(slot_id)
+ } else {
+ let _ = list_all_slots(yubikey);
+
+ return Err(SecurityModuleError::Hsm(HsmError::DeviceSpecific(
+ "No more free slots available".to_string(),
+ )));
+ }
+}
+
+/// Converts a `RetiredSlotId` to its corresponding u32 value.
+///
+/// This method converts a `RetiredSlotId` to its corresponding u32 value,
+/// which is required for slot operations on the YubiKey device.
+///
+/// # Arguments
+///
+/// * `slot` - The `RetiredSlotId` to be converted.
+///
+/// # Returns
+///
+/// The corresponding u32 value of the `RetiredSlotId`.
+fn get_reference_u32slot(slot: RetiredSlotId) -> u32 {
+ let mut output: u32 = SLOTSU32[0];
+ for i in 0..20 {
+ if SLOTS[i] == slot {
+ output = SLOTSU32[i + 10];
+ break;
+ } else {
+ continue;
+ }
+ }
+ output
+}
+
+fn list_all_slots(yubikey: &mut YubiKey) -> Result, SecurityModuleError> {
+ let mut output: Vec = Vec::new();
+ for i in 10..20 {
+ let data = yubikey.fetch_object(SLOTSU32[i]);
+ let mut temp_vec: Vec = Vec::new();
+ match data {
+ Ok(data) => {
+ temp_vec = data.to_vec();
+ }
+ Err(_) => {}
+ }
+ let data = temp_vec;
+ match parse_slot_data(&data) {
+ Ok((key_name, slot, pkey, algo)) => {
+ let output_string = format!(
+ "Key Name: {}, Slot: {}, Public-Key: {}, Key-Algorithm: {}\n",
+ key_name, slot, pkey, algo
+ );
+ output.push(output_string);
+ }
+ Err(_) => {}
+ }
+ }
+ Ok(output)
+}
+
+/*
+/// Clears a slot on the YubiKey device.
+/// # Arguments
+/// * `yubikey` - The YubiKey device to clear the slot on.
+/// * `slot` - The slot to clear. If `None`, all slots are cleared.
+/// Needs improvement, as it is problematic to iterate effectively over all slots.
+
+
+fn clear_slot(yubikey: &mut YubiKey, slot: Option) {
+ match slot {
+ Some(address) => {
+ remv(yubikey, address);
+ }
+ None => {
+ //for address in SLOTSU32 {
+ //remv(yubikey, address);
+ }
+ }
+}
+
+/// Removes an object from the YubiKey device.
+/// # Arguments
+/// * `yubikey` - The YubiKey device to remove the object from.
+/// * `address` - The address of the object to remove.
+
+fn remv(yubikey: &mut YubiKey, address: u32) {
+ let mut empty_vec: Vec = Vec::new();
+ let empty_slice: &mut [u8] = &mut empty_vec[..];
+ yubikey.save_object(address, empty_slice);
+}
+*/
+// Halbfertiger Code, kann benutzt werden wenn PIN-Abfrage in App implementiert wird
+/*
+#[instrument]
+fn initialize_module(
+ &mut self,
+ key_algorithm: AsymmetricEncryption,
+ sym_algorithm: Option,
+ hash: Option,
+ input: &str,
+) -> Result {
+ // Opens a connection to the yubikey device
+ loop {
+ let yubikey = YubiKey::open();
+ if yubikey.is_ok() {
+ let verify = device.verify_pin(input);
+ if verify.is_ok() {
+ //successful login
+ return device;
+ } else {
+ let count = device.get_pin_retries().unwrap();
+ // TODO: Implement PUK handling
+ if count == 0 {
+ return yubiKey::Error::PinLocked;
+ /* let puk;
+ let pin_neu;
+ let change_puk = device.unblock_pin(puk.as_ref(), pin_neu.as_ref());
+ if change_puk.is_ok() {
+ return device;
+ */
+ }
+ return yubikey::Errror::WrongPin;
+ }
+ }
+ }
+}
+*/
diff --git a/src/lib.rs b/src/lib.rs
index 898d5a89..91d81ea8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,7 @@
pub mod common;
#[cfg(feature = "ffi")]
pub mod ffi;
-#[cfg(feature = "hsm")]
+//#[cfg(feature = "hsm")]
pub mod hsm;
#[cfg(test)]
mod tests;
diff --git a/src/tests/common/traits/mod.rs b/src/tests/common/traits/mod.rs
index fd907b57..0ff29815 100644
--- a/src/tests/common/traits/mod.rs
+++ b/src/tests/common/traits/mod.rs
@@ -1,68 +1,196 @@
-use tracing::Level;
-use tracing_appender::rolling;
-use tracing_subscriber::FmtSubscriber;
-
-use crate::{
+#[cfg(feature = "hsm")]
+use crate::hsm::core::instance::HsmType;
+#[cfg(feature = "tpm")]
+use crate::tpm::core::instance::TpmType;
+/*use crate::{
common::{
+ crypto::{
+ algorithms::{
+ encryption::{
+ AsymmetricEncryption, BlockCiphers, EccCurves, EccSchemeAlgorithm,
+ SymmetricMode,
+ },
+ hashes::{Hash, Sha2Bits},
+ KeyBits,
+ },
+ KeyUsage,
+ },
factory::SecurityModule,
- traits::{log_config::LogConfig, module_provider::Provider},
},
- tpm::core::instance::TpmType,
- SecModules,
+ tests::common::traits::setup_security_module,
};
-use std::sync::{Arc, Mutex};
-
-pub mod key_handle;
-pub mod module_provider;
-
-#[derive(Debug, Clone, Copy)]
-struct Logger {}
-
-impl Logger {
- fn new_boxed() -> Box {
- Box::new(Self {})
- }
-}
-
-impl LogConfig for Logger {
- fn setup_logging(&self) {
- let file_appender = rolling::daily("./logs", "output.log");
- let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
- let subscriber = FmtSubscriber::builder()
- .with_max_level(Level::TRACE)
- .with_writer(non_blocking)
- .finish();
- tracing::subscriber::set_global_default(subscriber)
- .expect("setting default subscriber failed");
- }
-}
-
-fn setup_security_module(module: SecurityModule) -> Arc> {
- let log = Logger::new_boxed();
- match module {
- #[cfg(feature = "hsm")]
- SecurityModule::Hsm(_hsm_type) => {
- unimplemented!()
- }
- #[cfg(feature = "tpm")]
- SecurityModule::Tpm(tpm_type) => match tpm_type {
- TpmType::Linux => SecModules::get_instance(
- "test_key".to_owned(),
- SecurityModule::Tpm(TpmType::Linux),
- Some(log),
- )
- .unwrap(),
- TpmType::Windows => SecModules::get_instance(
- "test_key".to_owned(),
- SecurityModule::Tpm(TpmType::Windows),
- Some(log),
- )
- .unwrap(),
- TpmType::None => unimplemented!(),
- TpmType::Android(_) => unimplemented!(),
- },
- #[cfg(feature = "nks")]
- SecurityModule::Nks => unimplemented!(),
- // _ => unimplemented!(),
- }
-}
+use test_case::test_matrix;*/
+//
+// // #[test_matrix(
+// // [SecurityModule::Tpm(TpmType::Linux),
+// // SecurityModule::Tpm(TpmType::Windows),
+// // SecurityModule::Hsm(HsmType::NitroKey)]
+// // )]
+// #[test_matrix(
+// [SecurityModule::Nks]
+// )]
+// fn test_sign_and_verify_rsa(module: SecurityModule) {
+// let provider = setup_security_module(module);
+//
+// let config = TpmConfig::new(
+// AsymmetricEncryption::Rsa(KeyBits::Bits4096),
+// BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+// Hash::Sha2(Sha2Bits::Sha256),
+// vec![KeyUsage::SignEncrypt, KeyUsage::ClientAuth],
+// );
+//
+// provider
+// .lock()
+// .unwrap()
+// .initialize_module()
+// .expect("Failed to initialize module");
+// provider
+// .lock()
+// .unwrap()
+// .create_key("test_rsa_key", config)
+// .expect("Failed to create RSA key");
+//
+// let data = b"Hello, World!";
+// let signature = provider
+// .lock()
+// .unwrap()
+// .sign_data(data)
+// .expect("Failed to sign data");
+//
+// assert!(provider
+// .lock()
+// .unwrap()
+// .verify_signature(data, &signature)
+// .unwrap());
+// }
+//
+// // #[test_matrix(
+// // [SecurityModule::Tpm(TpmType::Linux),
+// // SecurityModule::Tpm(TpmType::Windows),
+// // SecurityModule::Hsm(HsmType::NitroKey)]
+// // )]
+// #[test_matrix(
+// [SecurityModule::Nks]
+// )]
+// fn test_sign_and_verify_ecdsa(module: SecurityModule) {
+// let provider = setup_security_module(module);
+//
+// let config = TpmConfig::new(
+// AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(EccCurves::Curve25519)),
+// BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+// Hash::Sha2(Sha2Bits::Sha256),
+// vec![KeyUsage::SignEncrypt, KeyUsage::ClientAuth],
+// );
+//
+// provider
+// .lock()
+// .unwrap()
+// .initialize_module()
+// .expect("Failed to initialize module");
+// provider
+// .lock()
+// .unwrap()
+// .create_key("test_ecdsa_key", config)
+// .expect("Failed to create ECDSA key");
+//
+// let data = b"Hello, World!";
+// let signature = provider
+// .lock()
+// .unwrap()
+// .sign_data(data)
+// .expect("Failed to sign data");
+//
+// assert!(provider
+// .lock()
+// .unwrap()
+// .verify_signature(data, &signature)
+// .unwrap());
+// }
+//
+// // #[test_matrix(
+// // [SecurityModule::Tpm(TpmType::Linux),
+// // SecurityModule::Tpm(TpmType::Windows),
+// // SecurityModule::Hsm(HsmType::NitroKey)]
+// // )]
+// #[test_matrix(
+// [SecurityModule::Nks]
+// )]
+// fn test_encrypt_and_decrypt_rsa(module: SecurityModule) {
+// let provider = setup_security_module(module);
+//
+// let config = TpmConfig::new(
+// AsymmetricEncryption::Rsa(KeyBits::Bits4096),
+// BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+// Hash::Sha2(Sha2Bits::Sha256),
+// vec![KeyUsage::Decrypt, KeyUsage::SignEncrypt],
+// );
+//
+// provider
+// .lock()
+// .unwrap()
+// .initialize_module()
+// .expect("Failed to initialize module");
+// provider
+// .lock()
+// .unwrap()
+// .create_key("test_rsa_key", config)
+// .expect("Failed to create RSA key");
+//
+// let data = b"Hello, World!";
+// let encrypted_data = provider
+// .lock()
+// .unwrap()
+// .encrypt_data(data)
+// .expect("Failed to encrypt data");
+// let decrypted_data = provider
+// .lock()
+// .unwrap()
+// .decrypt_data(&encrypted_data)
+// .expect("Failed to decrypt data");
+//
+// assert_eq!(data, decrypted_data.as_slice());
+// }
+//
+// // #[test_matrix(
+// // [SecurityModule::Tpm(TpmType::Linux),
+// // SecurityModule::Tpm(TpmType::Windows),
+// // SecurityModule::Hsm(HsmType::NitroKey)]
+// // )]
+// #[test_matrix(
+// [SecurityModule::Nks]
+// )]
+// fn test_encrypt_and_decrypt_ecdh(module: SecurityModule) {
+// let provider = setup_security_module(module);
+//
+// let config = TpmConfig::new(
+// AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDh(EccCurves::Curve25519)),
+// BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+// Hash::Sha2(Sha2Bits::Sha256),
+// vec![KeyUsage::SignEncrypt, KeyUsage::Decrypt],
+// );
+//
+// provider
+// .lock()
+// .unwrap()
+// .initialize_module()
+// .expect("Failed to initialize module");
+// provider
+// .lock()
+// .unwrap()
+// .create_key("test_ecdh_key", config)
+// .expect("Failed to create ECDH key");
+//
+// let data = b"Hello, World!";
+// let encrypted_data = provider
+// .lock()
+// .unwrap()
+// .encrypt_data(data)
+// .expect("Failed to encrypt data");
+// let decrypted_data = provider
+// .lock()
+// .unwrap()
+// .decrypt_data(&encrypted_data)
+// .expect("Failed to decrypt data");
+//
+// assert_eq!(data, decrypted_data.as_slice());
+// }
\ No newline at end of file
diff --git a/src/tests/hsm/mod.rs b/src/tests/hsm/mod.rs
index 8b137891..e0f99172 100644
--- a/src/tests/hsm/mod.rs
+++ b/src/tests/hsm/mod.rs
@@ -1 +1 @@
-
+pub mod yubikey;
diff --git a/src/tests/hsm/yubikey/key_handle_tests.rs b/src/tests/hsm/yubikey/key_handle_tests.rs
new file mode 100644
index 00000000..5335e784
--- /dev/null
+++ b/src/tests/hsm/yubikey/key_handle_tests.rs
@@ -0,0 +1,261 @@
+/// # Test Cases for Cryptographic Operations
+///
+/// The purpose of this module is to provide comprehensive tests for cryptographic operations
+/// within the system, specifically focusing on signing and verifying data using RSA and ECC keys.
+/// These tests ensure the integrity and reliability of cryptographic operations, which are crucial
+/// for maintaining the security of the system's data and communications.
+///
+/// ## Test Cases
+///
+/// - `test_sign_and_verify_rsa_1024`: Tests signing and verifying data with a 1024-bit RSA key.
+/// - `test_sign_and_verify_rsa_2048`: Tests signing and verifying data with a 2048-bit RSA key.
+/// - `test_sign_and_verify_ecc_256`: Tests signing and verifying data with a 256-bit ECC key.
+/// - `test_sign_and_verify_ecc_384`: Tests signing and verifying data with a 384-bit ECC key.
+///
+/// ## Test Procedures
+///
+/// Each test case follows a similar procedure:
+///
+/// 1. **Initialization**: Initializes the `YubiKeyProvider` with the necessary parameters, such as
+/// the key ID and configuration.
+/// 2. **Module Initialization**: Initializes the HSM module on the YubiKey device.
+/// 3. **Key Creation**: Creates a new key pair based on the test case requirements.
+/// 4. **Signing**: Signs a predefined data string.
+/// 5. **Verification**: Verifies the signature of the signed data.
+///
+/// ## Test Parameters
+///
+/// Each test case specifies the key type, size, and usage:
+///
+/// - **RSA Keys**: Test cases for RSA keys specify either 1024-bit or 2048-bit key sizes
+/// with key usage for signing and encryption.
+/// - **ECC Keys**: Test cases for ECC keys specify either 256-bit or 384-bit key sizes
+/// with key usage for signing and encryption.
+///
+/// ## Test Assumptions
+///
+/// These test cases assume that a YubiKey device is connected and properly configured for
+/// cryptographic operations. They also assume that the YubiKey device is accessible via
+/// the system's USB interface.
+///
+/// ## Expected Behavior
+///
+/// The expected behavior for each test case is successful key creation, signing, and verification
+/// without encountering any errors. Any failures during these operations are considered test
+/// failures and will be reported accordingly.
+///
+/// Please use **cargo test --features yubi -- --test-threads=1** for successful testing due to parallelization issues.
+#[cfg(test)]
+#[allow(unused_imports)]
+use crate::common::{
+ crypto::{
+ algorithms::{
+ encryption::{AsymmetricEncryption, BlockCiphers, EccCurves, EccSchemeAlgorithm},
+ hashes::Hash,
+ KeyBits,
+ },
+ KeyUsage,
+ },
+ traits::{key_handle::KeyHandle, module_provider::Provider},
+};
+
+// Import YubiKeyProvider and HsmProviderConfig for HSM operations
+use crate::hsm::{yubikey::YubiKeyProvider, HsmProviderConfig};
+// The following tests cover different cryptographic scenarios, ensuring the robustness and
+// compatibility of the system across various configurations and key sizes.
+
+//Test for signing and verifying RSA data with 1024-bit key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_sign_and_verify_rsa_1024() {
+ // Initialization of YubiKeyProvider and configuration of HsmProviderConfig
+ // omitted for brevity; please refer to the individual test implementations
+ let mut provider = YubiKeyProvider::new("test_sv_1024".to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits1024));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_rsa_key_1024", config)
+ .expect("Failed to create RSA key");
+
+ let data = b"Hello, World!";
+ let signature = provider.sign_data(data).expect("Failed to sign data");
+
+ assert!(provider.verify_signature(data, &signature).unwrap());
+}
+
+// Test for signing and verifying RSA data with a 2048-bit key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_sign_and_verify_rsa_2048() {
+ let mut provider = YubiKeyProvider::new("test_sv_2048".to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits2048));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_rsa_key_2048", config)
+ .expect("Failed to create RSA key");
+
+ let data = b"Hello, World!";
+ let signature = provider.sign_data(data).expect("Failed to sign data");
+
+ assert!(provider.verify_signature(data, &signature).unwrap());
+}
+
+// Test for signing and verifying ECC data with a 256-bit key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_sign_and_verify_ecc_256() {
+ let mut provider = YubiKeyProvider::new("test_ecc_256".to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P256,
+ )));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_ecc_key_256", config)
+ .expect("Failed to create ECC key");
+
+ let data = b"Hello, World!";
+ let signature = provider.sign_data(data).expect("Failed to sign data");
+
+ assert!(provider.verify_signature(data, &signature).unwrap());
+}
+
+// Test for signing and verifying ECC data with a 384-bit key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_sign_and_verify_ecc_384() {
+ let mut provider = YubiKeyProvider::new("test_ecc_384".to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P384,
+ )));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_ecc_key_384", config)
+ .expect("Failed to create ECC key");
+
+ let data = b"Hello, World!";
+ let signature = provider.sign_data(data).expect("Failed to sign data");
+
+ assert!(provider.verify_signature(data, &signature).unwrap());
+}
+
+#[cfg(feature = "yubi")]
+#[test]
+fn test_encrypt_and_decrypt_rsa_1024() {
+ let mut provider = YubiKeyProvider::new("test_enc_dec_1024".to_string());
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits1024));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_rsa_key_1024", config)
+ .expect("Failed to create RSA key");
+
+ let data = b"Hello, World!";
+ let encrypted_data = provider.encrypt_data(data).expect("Failed to encrypt data");
+ let decrypted_data = provider
+ .decrypt_data(&encrypted_data)
+ .expect("Failed to decrypt data");
+
+ assert_eq!(data, decrypted_data.as_slice());
+}
+
+#[cfg(feature = "yubi")]
+#[test]
+fn test_encrypt_and_decrypt_rsa_2048() {
+ let mut provider = YubiKeyProvider::new("test_enc_dec_2048".to_string());
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits2048));
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_rsa_key_2048", config)
+ .expect("Failed to create RSA key");
+
+ let data = b"Hello, World!";
+ let encrypted_data = provider.encrypt_data(data).expect("Failed to encrypt data");
+ let decrypted_data = provider
+ .decrypt_data(&encrypted_data)
+ .expect("Failed to decrypt data");
+
+ assert_eq!(data, decrypted_data.as_slice());
+}
+
+/*
+/// The following tests, `test_encrypt_and_decrypt_rsa` and `test_encrypt_and_decrypt_ecdh`,
+/// are currently commented out as they are placeholders for future implementations
+/// involving encryption and decryption operations. These tests will further expand the
+/// test coverage to ensure the complete functionality of the cryptographic operations
+/// provided by the system.
+
+#[test]
+fn test_encrypt_and_decrypt_rsa() {
+ let mut provider = TpmProvider::new("test_rsa_key".to_string());
+
+ let config = TpmConfig::new(
+ AsymmetricEncryption::Rsa(KeyBits::Bits4096),
+ BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+ Hash::Sha2(Sha2Bits::Sha256),
+ vec![KeyUsage::SignEncrypt, KeyUsage::Decrypt],
+ );
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_rsa_key", config)
+ .expect("Failed to create RSA key");
+
+ let data = b"Hello, World!";
+ let encrypted_data = provider.encrypt_data(data).expect("Failed to encrypt data");
+ let decrypted_data = provider
+ .decrypt_data(&encrypted_data)
+ .expect("Failed to decrypt data");
+
+ assert_eq!(data, decrypted_data.as_slice());
+}
+
+#[test]
+fn test_encrypt_and_decrypt_ecdh() {
+ let mut provider = TpmProvider::new("test_ecdh_key".to_string());
+
+ let config = TpmConfig::new(
+ AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDh(EccCurves::Curve25519)),
+ BlockCiphers::Aes(SymmetricMode::Gcm, KeyBits::Bits512),
+ Hash::Sha2(Sha2Bits::Sha256),
+ vec![KeyUsage::SignEncrypt, KeyUsage::Decrypt],
+ );
+
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+ provider
+ .create_key("test_ecdh_key", config)
+ .expect("Failed to create ECDH key");
+
+ let data = b"Hello, World!";
+ let encrypted_data = provider.encrypt_data(data).expect("Failed to encrypt data");
+ let decrypted_data = provider
+ .decrypt_data(&encrypted_data)
+ .expect("Failed to decrypt data");
+
+ assert_eq!(data, decrypted_data.as_slice());
+}
+*/
diff --git a/src/tests/hsm/yubikey/mod.rs b/src/tests/hsm/yubikey/mod.rs
new file mode 100644
index 00000000..9d9955fb
--- /dev/null
+++ b/src/tests/hsm/yubikey/mod.rs
@@ -0,0 +1,2 @@
+mod key_handle_tests;
+mod provider_handle_tests;
\ No newline at end of file
diff --git a/src/tests/hsm/yubikey/provider_handle_tests.rs b/src/tests/hsm/yubikey/provider_handle_tests.rs
new file mode 100644
index 00000000..6ded5846
--- /dev/null
+++ b/src/tests/hsm/yubikey/provider_handle_tests.rs
@@ -0,0 +1,232 @@
+/// # Test Cases for YubiKeyProvider
+///
+/// The `YubiKeyProvider` module facilitates the creation and loading of cryptographic keys
+/// on a YubiKey device. This module contains several test cases to ensure the functionality
+/// of key creation and loading for different key types and sizes.
+///
+/// ## Test Cases
+///
+/// - `test_create_rsa_key_1024`: Tests the creation of a 1024-bit RSA key pair on the YubiKey device.
+/// - `test_create_rsa_key_2048`: Tests the creation of a 2048-bit RSA key pair on the YubiKey device.
+/// - `test_create_ecc_key_256`: Tests the creation of a 256-bit ECC key pair on the YubiKey device.
+/// - `test_create_ecc_key_384`: Tests the creation of a 384-bit ECC key pair on the YubiKey device.
+/// - `test_load_rsa_key`: Tests the loading of an RSA key pair from the YubiKey device.
+/// - `test_load_ecc_key`: Tests the loading of an ECC key pair from the YubiKey device.
+///
+/// ## Test Procedures
+///
+/// Each test case follows a similar procedure:
+///
+/// 1. **Initialization**: Initializes the `YubiKeyProvider` with the necessary parameters, such as
+/// the key ID and configuration.
+/// 2. **Module Initialization**: Initializes the HSM module on the YubiKey device.
+/// 3. **Key Creation/Loading**: Either creates a new key pair or loads an existing key pair
+/// based on the test case requirements.
+///
+/// ## Test Parameters
+///
+/// Each test case specifies the key type, size, and usage:
+///
+/// - **RSA Keys**: Test cases for RSA keys specify either 1024-bit or 2048-bit key sizes
+/// with key usage for signing and encryption.
+/// - **ECC Keys**: Test cases for ECC keys specify either 256-bit or 384-bit key sizes
+/// with key usage for signing and encryption.
+///
+/// ## Test Assumptions
+///
+/// These test cases assume that a YubiKey device is connected and properly configured for
+/// cryptographic operations. They also assume that the YubiKey device is accessible via
+/// the system's USB interface.
+///
+/// ## Expected Behavior
+///
+/// The expected behavior for each test case is successful key creation or loading without
+/// encountering any errors. Any failures during key creation or loading are considered test
+/// failures and will be reported accordingly.
+///
+/// Please use **cargo test --features yubi -- --test-threads=1** for successful testing due to parallelization issues
+#[allow(unused_imports)]
+use crate::common::{
+ crypto::{
+ algorithms::{
+ encryption::{AsymmetricEncryption, BlockCiphers, EccCurves, EccSchemeAlgorithm},
+ hashes::Hash,
+ KeyBits,
+ },
+ KeyUsage,
+ },
+ traits::module_provider::Provider,
+};
+use crate::hsm::yubikey::YubiKeyProvider;
+use crate::hsm::HsmProviderConfig;
+
+// Tests for creating 1024-bit RSA keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_create_rsa_key_1024() {
+ let key_id = "test_rsa_key_1024";
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits1024));
+
+ //initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ //generate RSA-keypair
+ provider
+ .create_key(key_id, config)
+ .expect("Failed to create RSA key");
+}
+
+// Tests for creating 2048-bit RSA keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_create_rsa_key_2048() {
+ let key_id = "test_rsa_key_2048";
+ let mut provider: YubiKeyProvider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits2048));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // generate RSA-keypair
+ provider
+ .create_key(key_id, config)
+ .expect("Failed to create RSA key");
+}
+
+//Tests for creating 256-bit ECC keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_create_ecc_key_256() {
+ let key_id = "test_ecc_key_256";
+
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P256,
+ )));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // generate ECC-keypair
+ provider
+ .create_key(key_id, config)
+ .expect("Failed to create ECC key");
+}
+
+// Tests for creating 384-bit ECC keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_create_ecc_key_384() {
+ let key_id = "test_ecc_key_384";
+
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P384,
+ )));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // generate ECC-keypair
+ provider
+ .create_key(key_id, config)
+ .expect("Failed to create ECC key");
+}
+
+// Test for loading RSA keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_load_rsa_key_1024() {
+ let key_id = "test_rsa_key_1024";
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits1024));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // load RSA-key
+ provider
+ .load_key(&key_id, config)
+ .expect("Failed to load RSA key");
+}
+
+// Test for loading RSA keys
+#[cfg(feature = "yubi")]
+#[test]
+fn test_load_rsa_key_2048() {
+ let key_id = "test_rsa_key_2048";
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Rsa(KeyBits::Bits1024));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // load RSA-key
+ provider
+ .load_key(&key_id, config)
+ .expect("Failed to load RSA key");
+}
+
+// Test to load an ECC key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_load_ecc_key_256() {
+ let key_id = "test_ecc_key_256";
+
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P256,
+ )));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // load ECC-key
+ provider
+ .load_key(key_id, config)
+ .expect("Failed to load ECC key");
+}
+
+// Test to load an ECC key
+#[cfg(feature = "yubi")]
+#[test]
+fn test_load_ecc_key_384() {
+ let key_id = "test_ecc_key_384";
+
+ let mut provider = YubiKeyProvider::new(key_id.to_string());
+
+ let config = HsmProviderConfig::new(AsymmetricEncryption::Ecc(EccSchemeAlgorithm::EcDsa(
+ EccCurves::P256,
+ )));
+
+ // initialize HSM-module
+ provider
+ .initialize_module()
+ .expect("Failed to initialize module");
+
+ // load ECC-key
+ provider
+ .load_key(key_id, config)
+ .expect("Failed to load ECC key");
+}