diff --git a/Cargo.lock b/Cargo.lock index 1ddd42e3..ea5f1182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -11,6 +21,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + [[package]] name = "anyhow" version = "1.0.83" @@ -242,12 +258,24 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + [[package]] name = "cc" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41" +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -264,6 +292,16 @@ dependencies = [ "inout", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -332,12 +370,15 @@ dependencies = [ "anyhow", "async-std", "futures", + "libloading", "nitrokey", "once_cell", + "robusta_jni", "serde", "serde_json", "test-case", "tracing", + "tracing-android", "tracing-appender", "tracing-subscriber", "tss-esapi", @@ -345,6 +386,41 @@ dependencies = [ "yubikey", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.107", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.107", +] + [[package]] name = "der" version = "0.7.9" @@ -543,6 +619,12 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "futures" version = "0.3.30" @@ -746,6 +828,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "inout" version = "0.1.3" @@ -781,6 +869,26 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "js-sys" version = "0.3.69" @@ -814,6 +922,16 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + [[package]] name = "libm" version = "0.2.8" @@ -1027,6 +1145,12 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -1211,6 +1335,29 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.65" @@ -1307,6 +1454,33 @@ dependencies = [ "subtle", ] +[[package]] +name = "robusta-codegen" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb512b451472948a204452dfad582bdc48d69caacdd3b1b4571d5e3f11707f3" +dependencies = [ + "Inflector", + "darling", + "proc-macro-error", + "proc-macro2", + "quote", + "rand", + "syn 1.0.107", +] + +[[package]] +name = "robusta_jni" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c080146e0cc733697fe500413871142af91bd879641205c2febbe5f982f304e3" +dependencies = [ + "jni", + "paste", + "robusta-codegen", + "static_assertions", +] + [[package]] name = "rsa" version = "0.9.6" @@ -1370,6 +1544,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "sec1" version = "0.7.3" @@ -1539,6 +1722,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -1700,6 +1895,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-android" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12612be8f868a09c0ceae7113ff26afe79d81a24473a393cb9120ece162e86c0" +dependencies = [ + "android_log-sys", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-appender" version = "0.2.3" @@ -1842,6 +2048,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1946,6 +2162,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 28009807..c582c34e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,10 +3,9 @@ name = "crypto-layer" version = "0.1.0" edition = "2021" license = "MIT" -crate-type = ["staticlib"] [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "lib"] [profile.dev] debug-assertions = true @@ -24,7 +23,7 @@ debug = false strip = "symbols" [features] -android = [] +android = ["robusta_jni", "libloading", "tracing-android"] debug = [] hsm = [] ffi = [] @@ -50,6 +49,9 @@ tracing = { version = "0.1.40", features = ["std", "log"] } tracing-subscriber = "0.3.18" tracing-appender = "0.2.3" yubikey = { version = "0.8.0", optional = true } +robusta_jni = { version = "0.2", optional = true } +libloading = { version = "0.8.3", optional = true} +tracing-android = { version = "0.2.0", optional = true } [dev-dependencies] test-case = "*" diff --git a/src/common/traits/module_provider.rs b/src/common/traits/module_provider.rs index 50cc035b..86e42fe6 100644 --- a/src/common/traits/module_provider.rs +++ b/src/common/traits/module_provider.rs @@ -1,6 +1,6 @@ -use super::{key_handle::KeyHandle, module_provider_config::ProviderConfig}; +use super::key_handle::KeyHandle; use crate::common::error::SecurityModuleError; -use std::fmt::Debug; +use std::{any::Any, fmt::Debug}; /// Defines the interface for a security module provider. /// @@ -26,11 +26,8 @@ pub trait Provider: Send + Sync + KeyHandle + Debug { /// /// A `Result` that, on success, contains `Ok(())`, indicating that the key was created successfully. /// On failure, it returns a `SecurityModuleError`. - fn create_key( - &mut self, - key_id: &str, - config: Box, - ) -> Result<(), SecurityModuleError>; + fn create_key(&mut self, key_id: &str, config: Box) + -> Result<(), SecurityModuleError>; /// Loads an existing cryptographic key identified by `key_id`. /// @@ -46,11 +43,7 @@ pub trait Provider: Send + Sync + KeyHandle + Debug { /// /// A `Result` that, on success, contains `Ok(())`, indicating that the key was loaded successfully. /// On failure, it returns a `SecurityModuleError`. - fn load_key( - &mut self, - key_id: &str, - config: Box, - ) -> Result<(), SecurityModuleError>; + fn load_key(&mut self, key_id: &str, config: Box) -> Result<(), SecurityModuleError>; /// Initializes the security module and returns a handle for further operations. /// diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 38e784e3..fc32a423 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,7 @@ pub mod common; + +#[cfg(feature = "hsm")] pub mod hsm; + +#[cfg(feature = "tpm")] mod tpm; diff --git a/src/tests/tpm/android/mod.rs b/src/tests/tpm/android/mod.rs new file mode 100644 index 00000000..6cb02dd9 --- /dev/null +++ b/src/tests/tpm/android/mod.rs @@ -0,0 +1,644 @@ +use robusta_jni::convert::IntoJavaValue; + +use crate::common::crypto::{algorithms, KeyUsage}; +use crate::common::factory::SecModules; +use crate::common::factory::SecurityModule; +use crate::tpm::android::*; +use crate::tpm::core::instance::AndroidTpmType; +use crate::tpm::core::instance::TpmType; + +#[test] +fn initializ_module_test1() { + assert_eq!(true, true); +} + +#[test] +fn initializ_module_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + None, + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider.initialize_module().unwrap(); +} +/* +#[test] +fn key_creation_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("2320").unwrap(); +} + +#[test] +fn key_load_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("2322").unwrap(); + provider.load_key("2322").unwrap(); +} + +/* +----------------TESTING different KeyBits------------------------ +*/ + +#[test] +fn key_creation_bit_128_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits128); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("23128").unwrap(); +} + +#[test] +fn key_creation_bit_192_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits192); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("23192").unwrap(); +} + +#[test] +fn key_creation_bit_256_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits256); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("23256").unwrap(); +} + +#[test] +fn key_creation_bit_512_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits512); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("23512").unwrap(); +} + +#[test] +fn key_creation_bit_1024_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231024").unwrap(); +} + +#[test] +fn key_creation_bit_2048_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits2048); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("232048").unwrap(); +} + +#[test] +fn key_creation_bit_3072_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits3072); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("233072").unwrap(); +} + +#[test] +fn key_creation_bit_4096_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits4096); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("234096").unwrap(); +} + +#[test] +fn key_creation_bit_8192_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits8192); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("238192").unwrap(); +} + +/* + +---------------------TESTING Hashes------------------- + +*/ + +#[test] +fn key_creation_hash_md2_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Md2; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231025").unwrap(); +} + +#[test] +fn key_creation_hash_md4_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Md4; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231026").unwrap(); +} + +#[test] +fn key_creation_hash_md5_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Md5; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231027").unwrap(); +} + +/* + +------------Testing KeyUsages-------------- + +*/ + +#[test] +fn key_creation_hash_ripemd160_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Ripemd160; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231028").unwrap(); +} + +#[test] +fn key_creation_keyusage_clientauth_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::ClientAuth]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231029").unwrap(); +} + +#[test] +fn key_creation_keyusage_decrypt_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::Decrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231030").unwrap(); +} + +#[test] +fn key_creation_keyusage_createx509_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::CreateX509]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + provider.create_key("231031").unwrap(); +} + +/* + +---------------Sign Data------------------ + +*/ + +#[test] +fn sign_data_1_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"testing"; + + provider.sign_data(data); +} + +#[test] +fn sign_data_2_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"h"; + + provider.sign_data(data); +} + +//How to expect a fail?? +#[test] +fn sign_data_3_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b""; + + provider.sign_data(data); +} + +//How to expect a fail?? +#[test] +fn sign_data_4_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"overflowing"; + + provider.sign_data(data); +} + +//Test different Key Ids => 0? + +#[test] +fn verify_signature_1_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"test"; + + let signature = provider.sign_data(data).unwrap(); + + //Convert Vec to list u8 + + let mut signature_list: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (place, element) in signature_list.iter_mut().zip(signature.into_iter()) { + unsafe { std::ptr::write(place, element) }; + } + let verified = provider + .verify_signature(data, &signature_list) + .unwrap_or_default(); + + assert_eq!(true, verified); +} + +#[test] +fn verify_signature_2_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"testingX"; + + let signature = provider.sign_data(data).unwrap(); + + //Convert Vec to list u8 + + let mut signature_list: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (place, element) in signature_list.iter_mut().zip(signature.into_iter()) { + unsafe { std::ptr::write(place, element) }; + } + let verified = provider + .verify_signature(data, &signature_list) + .unwrap_or_default(); + + assert_eq!(true, verified); +} + +#[test] +fn verify_signature_3_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b"H"; + + let signature = provider.sign_data(data).unwrap(); + + //Convert Vec to list u8 + + let mut signature_list: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (place, element) in signature_list.iter_mut().zip(signature.into_iter()) { + unsafe { std::ptr::write(place, element) }; + } + let verified = provider + .verify_signature(data, &signature_list) + .unwrap_or_default(); + + assert_eq!(true, verified); +} + +#[test] +fn verify_signature_4_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); + + let data = b""; + + let signature = provider.sign_data(data).unwrap(); + + let mut signature_list: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + + for (place, element) in signature_list.iter_mut().zip(signature.into_iter()) { + unsafe { std::ptr::write(place, element) }; + } + let verified = provider + .verify_signature(data, &signature_list) + .unwrap_or_default(); + + assert_eq!(false, verified); +} + +#[test] +fn encrypt_data_1_test() { + let security_module = SecModules::get_instance( + "2323".to_string(), + SecurityModule::Tpm(TpmType::Android(AndroidTpmType::Keystore)), + ); + + let x = security_module.unwrap(); + let mut provider = x.lock().unwrap(); + let key_algorithm = + algorithms::encryption::AsymmetricEncryption::Rsa(algorithms::KeyBits::Bits1024); + let hash = algorithms::hashes::Hash::Sha1; + let key_usages = vec![KeyUsage::SignEncrypt]; + provider + .initialize_module(key_algorithm, None, Some(hash), key_usages) + .unwrap(); +} +*/ diff --git a/src/tests/tpm/mod.rs b/src/tests/tpm/mod.rs index 9e77b87c..ac82731e 100644 --- a/src/tests/tpm/mod.rs +++ b/src/tests/tpm/mod.rs @@ -2,3 +2,5 @@ mod linux; #[cfg(feature = "win")] mod win; +#[cfg(feature = "android")] +mod android; diff --git a/src/tpm/android/android_logger.rs b/src/tpm/android/android_logger.rs new file mode 100644 index 00000000..457db609 --- /dev/null +++ b/src/tpm/android/android_logger.rs @@ -0,0 +1,14 @@ +use tracing_subscriber::{layer::SubscriberExt, Registry}; + +use crate::common::traits::log_config::LogConfig; + +#[derive(Debug)] +pub struct DefaultAndroidLogger; + +impl LogConfig for DefaultAndroidLogger { + fn setup_logging(&self) { + let subscriber = Registry::default().with(tracing_android::layer("RUST").unwrap()); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + } +} diff --git a/src/tpm/android/config.rs b/src/tpm/android/config.rs new file mode 100644 index 00000000..98f7d523 --- /dev/null +++ b/src/tpm/android/config.rs @@ -0,0 +1,44 @@ +use std::any::Any; + +use robusta_jni::jni::JavaVM; + +use crate::common::{ + crypto::{ + algorithms::encryption::{AsymmetricEncryption, BlockCiphers}, + algorithms::hashes::Hash, + KeyUsage, + }, + traits::module_provider_config::ProviderConfig, +}; + +#[derive(Debug, Clone, Copy)] +pub enum EncryptionMode { + Sym(BlockCiphers), + ASym { + algo: AsymmetricEncryption, + digest: Hash, + }, +} + +pub struct AndroidConfig { + pub mode: EncryptionMode, + pub key_usages: Vec, + pub hardware_backed: bool, + pub vm: Option, +} + +impl std::fmt::Debug for AndroidConfig { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AndroidProvider") + .field("mode", &self.mode) + .field("key_usages", &self.key_usages) + .field("hardware_backed", &self.hardware_backed) + .finish() + } +} + +impl ProviderConfig for AndroidConfig { + fn as_any(&self) -> &dyn Any { + self + } +} diff --git a/src/tpm/android/error.rs b/src/tpm/android/error.rs new file mode 100644 index 00000000..92682c07 --- /dev/null +++ b/src/tpm/android/error.rs @@ -0,0 +1,62 @@ +use crate::tpm::core::error::{ToTpmError, TpmError}; + +use super::wrapper; + +#[derive(Debug)] +pub(crate) struct JavaException(String); + +impl std::fmt::Display for JavaException { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Java Exception: {}", self.0) + } +} + +impl std::error::Error for JavaException {} + +/// This allows converting the JNI result into a `TpmError` result. +impl ToTpmError for robusta_jni::jni::errors::Result { + /// Converts the JNI result into a `TpmError` result. + /// If a Java exception was thrown, it retrieves the exception message and puts it into the error. + /// If no exception was thrown, it returns the JNI error as the `TpmError`. + fn err_internal(self) -> Result { + match self { + Ok(v) => Ok(v), + Err(e) => { + // check if a java exception was thrown + let vm = + wrapper::get_java_vm().map_err(|e| TpmError::InternalError(Box::new(e)))?; + let env = vm + .get_env() + .map_err(|e| TpmError::InternalError(Box::new(e)))?; + if env + .exception_check() + .map_err(|e| TpmError::InternalError(Box::new(e)))? + { + // get the exception message and put it into the error + env.exception_describe() + .map_err(|e| TpmError::InternalError(Box::new(e)))?; + let ex = env + .exception_occurred() + .map_err(|e| TpmError::InternalError(Box::new(e)))?; + env.exception_clear() + .map_err(|e| TpmError::InternalError(Box::new(e)))?; + let message = env + .call_method(ex, "getMessage", "()Ljava/lang/String;", &[]) + .and_then(|v| v.l()) + .map_err(|e| TpmError::InternalError(Box::new(e)))?; + + let message = env + .get_string(Into::into(message)) + .map_err(|e| TpmError::InternalError(Box::new(e)))? + .to_str() + .map_err(|e| TpmError::InternalError(Box::new(e)))? + .to_string(); + Err(TpmError::InternalError(Box::new(JavaException(message)))) + } else { + // there was no exception, return the jni error + Err(TpmError::InternalError(Box::new(e))) + } + } + } + } +} diff --git a/src/tpm/android/mod.rs b/src/tpm/android/mod.rs index 07d47505..1a8fb7f9 100644 --- a/src/tpm/android/mod.rs +++ b/src/tpm/android/mod.rs @@ -1 +1,547 @@ +pub mod android_logger; +pub mod config; +pub(crate) mod error; pub mod knox; +pub(crate) mod utils; +pub(crate) mod wrapper; + +use std::any::Any; + +use robusta_jni::jni::objects::JObject; +use tracing::{debug, info, instrument}; +use utils::{ + get_algorithm, get_cipher_mode, get_digest, get_key_size, get_padding, get_signature_algorithm, + get_signature_padding, get_sym_block_mode, +}; + +use crate::common::crypto::KeyUsage; +use crate::common::error::SecurityModuleError; +use crate::common::traits::key_handle::KeyHandle; +use crate::common::{ + crypto::algorithms::encryption::{AsymmetricEncryption, BlockCiphers}, + traits::module_provider::Provider, +}; +use crate::tpm::android::config::AndroidConfig; +use crate::tpm::android::wrapper::key_store::key_store::jni::KeyStore; +use crate::tpm::android::wrapper::key_store::signature::jni::Signature; +use crate::tpm::core::error::ToTpmError; +use crate::tpm::core::error::TpmError; + +const ANDROID_KEYSTORE: &str = "AndroidKeyStore"; + +/// A TPM-based cryptographic provider for managing cryptographic keys and performing +/// cryptographic operations in an Android environment. +/// +/// This provider uses the Android Keystore API to interact +/// with the Trusted Execution Environment (TEE), or the devices Secure Element(Like the Titan M chip in a Google Pixel) +/// for operations like signing, encryption, and decryption. +/// It provides a secure and hardware-backed solution for managing cryptographic keys and performing +/// cryptographic operations on Android. +#[derive(Debug)] +pub(crate) struct AndroidProvider { + key_id: String, + config: Option, +} + +impl AndroidProvider { + /// Constructs a new `AndroidProvider`. + /// + /// # Arguments + /// + /// * `key_id` - A string identifier for the cryptographic key to be managed by this provider. + /// * `config` - Configuration + /// + /// # Returns + /// + /// A new instance of `AndroidProvider` with the specified `key_id`. + #[instrument] + pub fn new(key_id: String) -> Self { + Self { + key_id, + config: None, + } + } + + fn apply_config(&mut self, config: AndroidConfig) -> Result<(), SecurityModuleError> { + // TODO: verify config + self.config = Some(config); + Ok(()) + } +} + +/// Implementation of the `Provider` trait for the Android platform. +/// +/// This struct provides methods for key generation, key loading, and module initialization +/// specific to Android. +impl Provider for AndroidProvider { + /// Generates a key with the parameters specified when the module was initialized. + /// + /// The key is generated using the Android Keystore API and is stored securely in the device's + /// Trusted Execution Environment (TEE) or Secure Element. It first attempts to generate a key + /// withing the devices StrongBox (Secure Element), and if that fails, because it is not available, + /// it falls back to the TEE. We have to do this because the KeyStore does not automatically select + /// the highest security level available. + /// + /// # Java Example + /// + /// ```java + /// KeyPairGenerator kpg = KeyPairGenerator.getInstance( + /// KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore"); + /// kpg.initialize(new KeyGenParameterSpec.Builder( + /// alias, + /// KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) + /// .setDigests(KeyProperties.DIGEST_SHA256, + /// KeyProperties.DIGEST_SHA512) + /// .build()); + /// KeyPair kp = kpg.generateKeyPair(); + /// ``` + /// + /// # Arguments + /// + /// * `key_id` - The identifier for the key. + /// + /// # Returns + /// + /// Returns `Ok(())` if the key generation is successful, otherwise returns an error of type `SecurityModuleError`. + #[instrument] + fn create_key( + &mut self, + key_id: &str, + config: Box, + ) -> Result<(), SecurityModuleError> { + info!("generating key! {}", key_id); + + // load config + let config = *config + .downcast::() + .map_err(|_| SecurityModuleError::InitializationError("Wrong Config".to_owned()))?; + + let env = config + .vm + .as_ref() + .expect("cannot happen, already checked") + .get_env() + .map_err(|_| { + TpmError::InitializationError( + "Could not get java environment, this should never happen".to_owned(), + ) + })?; + + // build up key specs + let mut kps_builder = + wrapper::key_generation::builder::Builder::new(&env, key_id.to_owned(), 1 | 2 | 4 | 8) + .err_internal()?; + + match config.mode { + config::EncryptionMode::Sym(cipher) => { + match cipher { + BlockCiphers::Aes(mode, size) => { + kps_builder = kps_builder + .set_block_modes(&env, vec![get_sym_block_mode(mode)?]) + .err_internal()? + .set_encryption_paddings(&env, vec![get_padding(config.mode)?]) + .err_internal()? + .set_key_size(&env, Into::::into(size) as i32) + .err_internal()?; + } + BlockCiphers::Des => { + kps_builder = kps_builder + .set_block_modes(&env, vec!["CBC".to_owned()]) + .err_internal()? + .set_encryption_paddings(&env, vec![get_padding(config.mode)?]) + .err_internal()?; + } + BlockCiphers::TripleDes(_) + | BlockCiphers::Rc2(_) + | BlockCiphers::Camellia(_, _) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()))? + } + }; + kps_builder = kps_builder + .set_is_strongbox_backed(&env, config.hardware_backed) + .err_internal()?; + + let kps = kps_builder.build(&env).err_internal()?; + + let kg = wrapper::key_generation::key_generator::jni::KeyGenerator::getInstance( + &env, + get_algorithm(config.mode)?, + ANDROID_KEYSTORE.to_owned(), + ) + .err_internal()?; + kg.init(&env, kps.raw.as_obj()).err_internal()?; + + kg.generateKey(&env).err_internal()?; + } + config::EncryptionMode::ASym { algo, digest } => { + match algo { + AsymmetricEncryption::Rsa(_key_bits) => { + kps_builder = kps_builder + .set_digests(&env, vec![get_digest(digest)?]) + .err_internal()? + .set_signature_paddings(&env, vec![get_signature_padding()?]) + .err_internal()? + .set_encryption_paddings(&env, vec![get_padding(config.mode)?]) + .err_internal()? + .set_key_size(&env, get_key_size(algo)? as i32) + .err_internal()?; + } + AsymmetricEncryption::Ecc(_scheme) => { + kps_builder = kps_builder + .set_digests(&env, vec![get_digest(digest)?]) + .err_internal()?; + } + }; + kps_builder = kps_builder + .set_is_strongbox_backed(&env, config.hardware_backed) + .err_internal()?; + + let kps = kps_builder.build(&env).err_internal()?; + + let kpg = wrapper::key_generation::key_pair_generator::jni::KeyPairGenerator::getInstance( + &env, + get_algorithm(config.mode)?, + ANDROID_KEYSTORE.to_owned(), + ) + .err_internal()?; + + kpg.initialize(&env, kps.raw.as_obj()).err_internal()?; + + kpg.generateKeyPair(&env).err_internal()?; + } + } + + debug!("key generated"); + self.apply_config(config)?; + + Ok(()) + } + + /// Loads a key with the specified `key_id`. + /// + /// # Arguments + /// + /// * `key_id` - The identifier for the key. + /// + /// # Returns + /// + /// Returns `Ok(())` if the key loading is successful, otherwise returns an error of type `SecurityModuleError`. + #[instrument] + fn load_key(&mut self, key_id: &str, config: Box) -> Result<(), SecurityModuleError> { + key_id.clone_into(&mut self.key_id); + + // load config + let config = *config + .downcast::() + .map_err(|_| SecurityModuleError::InitializationError("Wrong Config".to_owned()))?; + self.apply_config(config)?; + + Ok(()) + } + + /// Initializes the module with the specified parameters. + /// + /// # Arguments + /// + /// * `key_algorithm` - The asymmetric encryption algorithm to be used. + /// * `sym_algorithm` - The block cipher algorithm to be used (optional). + /// * `hash` - The hash algorithm to be used (optional). + /// * `key_usages` - The list of key usages. + /// + /// # Returns + /// + /// Returns `Ok(())` if the module initialization is successful, otherwise returns an error of type `SecurityModuleError`. + #[instrument] + fn initialize_module(&mut self) -> Result<(), SecurityModuleError> { + Ok(()) + } +} + +/// Implementation of the `KeyHandle` trait for the `AndroidProvider` struct. +/// All of the functions in this KeyHandle are basically re-implementations +/// of the equivalent Java functions in the Android KeyStore API. +impl KeyHandle for AndroidProvider { + /// Signs the given data using the Android KeyStore. + /// + /// # Arguments + /// + /// * `data` - Byte array of data to be signed. + /// + /// # Java Example + /// + /// ```java + /// KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + /// ks.load(null); + /// KeyStore.Entry entry = ks.getEntry(alias, null); + /// if (!(entry instanceof PrivateKeyEntry)) { + /// Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + /// return null; + /// } + /// Signature s = Signature.getInstance("SHA256withECDSA"); + /// s.initSign(((PrivateKeyEntry) entry).getPrivateKey()); + /// s.update(data); + /// byte[] signature = s.sign(); + /// ``` + /// + /// # Returns + /// + /// Returns a `Result` containing the signed data as a `Vec` if successful, or a `SecurityModuleError` if an error occurs. + #[instrument] + fn sign_data(&self, data: &[u8]) -> Result, SecurityModuleError> { + // check that signing is allowed + let config = self + .config + .as_ref() + .ok_or(SecurityModuleError::InitializationError( + "Module is not initialized".to_owned(), + ))?; + + if !config.key_usages.contains(&KeyUsage::SignEncrypt) { + return Err(TpmError::UnsupportedOperation( + "KeyUsage::SignEncrypt was not provided".to_owned(), + ) + .into()); + } + + let env = config + .vm + .as_ref() + .ok_or_else(|| TpmError::InitializationError("Module is not initialized".to_owned()))? + .get_env() + .map_err(|_| { + TpmError::InitializationError( + "Could not get java environment, this should never happen".to_owned(), + ) + })?; + + let key_store = KeyStore::getInstance(&env, ANDROID_KEYSTORE.to_string()).err_internal()?; + key_store.load(&env, None).err_internal()?; + + let private_key = key_store + .getKey(&env, self.key_id.clone(), JObject::null()) + .err_internal()?; + + let signature_algorithm = get_signature_algorithm(config.mode)?; + debug!("Signature Algorithm: {}", signature_algorithm); + + let s = Signature::getInstance(&env, signature_algorithm.to_string()).err_internal()?; + + s.initSign(&env, private_key.raw.as_obj()).err_internal()?; + + let data_bytes = data.to_vec().into_boxed_slice(); + + s.update(&env, data_bytes).err_internal()?; + debug!("Signature Init: {}", s.toString(&env).unwrap()); + + let output = s.sign(&env).err_internal()?; + + Ok(output) + } + + /// Decrypts the given encrypted data using the Android KeyStore. + /// + /// # Arguments + /// + /// * `encrypted_data` - The encrypted data to be decrypted. + /// + /// # Java Example + /// + /// ```java + /// KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); + /// keyStore.load(null); + /// PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEYNAME, null); + /// PublicKey publicKey = keyStore.getCertificate(KEYNAME).getPublicKey(); + /// Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + /// cipher.init(Cipher.DECRYPT_MODE, privateKey); + /// byte[] decrypted = cipher.doFinal(encrypted); + /// ``` + /// + /// # Returns + /// + /// Returns a `Result` containing the decrypted data as a `Vec` if successful, or a `SecurityModuleError` if an error occurs. + #[instrument] + fn decrypt_data(&self, encrypted_data: &[u8]) -> Result, SecurityModuleError> { + info!("decrypting data"); + + let config = self + .config + .as_ref() + .ok_or(SecurityModuleError::InitializationError( + "Module is not initialized".to_owned(), + ))?; + + let env = config + .vm + .as_ref() + .ok_or_else(|| TpmError::InitializationError("Module is not initialized".to_owned()))? + .get_env() + .map_err(|_| { + TpmError::InitializationError( + "Could not get java environment, this should never happen".to_owned(), + ) + })?; + + let cipher_mode = get_cipher_mode(config.mode)?; + + let key_store = KeyStore::getInstance(&env, ANDROID_KEYSTORE.to_owned()).err_internal()?; + key_store.load(&env, None).err_internal()?; + + let key = key_store + .getKey(&env, self.key_id.to_owned(), JObject::null()) + .err_internal()?; + + let cipher = wrapper::key_store::cipher::jni::Cipher::getInstance(&env, cipher_mode) + .err_internal()?; + cipher.init(&env, 2, key.raw.as_obj()).err_internal()?; + + let decrypted = cipher + .doFinal(&env, encrypted_data.to_vec()) + .err_internal()?; + + debug!("decrypted data: {:?}", decrypted); + Ok(decrypted) + } + + /// Encrypts the given data using the Android KeyStore. + /// + /// # Arguments + /// + /// * `data` - The data to be encrypted. + /// + /// # Java Example + /// + /// ```java + /// KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE); + /// keyStore.load(null); + /// PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEYNAME, null); + /// PublicKey publicKey = keyStore.getCertificate(KEYNAME).getPublicKey(); + /// Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + /// byte[] encrypted; + /// cipher.init(Cipher.ENCRYPT_MODE, publicKey); + /// encrypted = cipher.doFinal(text.getBytes()); + /// ``` + /// + /// # Returns + /// + /// Returns a `Result` containing the encrypted data as a `Vec` if successful, or a `SecurityModuleError` if an error occurs. + #[instrument] + fn encrypt_data(&self, data: &[u8]) -> Result, SecurityModuleError> { + info!("encrypting"); + + let config = self + .config + .as_ref() + .ok_or(SecurityModuleError::InitializationError( + "Module is not initialized".to_owned(), + ))?; + + let env = config + .vm + .as_ref() + .ok_or_else(|| TpmError::InitializationError("Module is not initialized".to_owned()))? + .get_env() + .map_err(|_| { + TpmError::InitializationError( + "Could not get java environment, this should never happen".to_owned(), + ) + })?; + + let key_store = KeyStore::getInstance(&env, ANDROID_KEYSTORE.to_owned()).err_internal()?; + key_store.load(&env, None).err_internal()?; + + let key = key_store + .getCertificate(&env, self.key_id.to_owned()) + .err_internal()? + .getPublicKey(&env) + .err_internal()?; + + let public_alg = key.getAlgorithm(&env).unwrap(); + debug!("Public Alg: {}", public_alg); + + let cipher = wrapper::key_store::cipher::jni::Cipher::getInstance( + &env, + get_cipher_mode(config.mode)?, + ) + .err_internal()?; + + cipher.init(&env, 1, key.raw.as_obj()).err_internal()?; + + let encrypted = cipher.doFinal(&env, data.to_vec()).err_internal()?; + + debug!("encrypted: {:?}", encrypted); + Ok(encrypted) + } + + /// Verifies the signature of the given data using the Android KeyStore. + /// + /// # Arguments + /// + /// * `data` - The data whose signature needs to be verified. + /// * `signature` - The signature to be verified. + /// + /// # Java Example + /// + /// ```java + /// KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + /// ks.load(null); + /// KeyStore.Entry entry = ks.getEntry(alias, null); + /// if (!(entry instanceof PrivateKeyEntry)) { + /// Log.w(TAG, "Not an instance of a PrivateKeyEntry"); + /// return false; + /// } + /// Signature s = Signature.getInstance("SHA256withECDSA"); + /// s.initVerify(((PrivateKeyEntry) entry).getCertificate()); + /// s.update(data); + /// boolean valid = s.verify(signature); + /// ``` + /// + /// # Returns + /// + /// Returns a `Result` containing `true` if the signature is valid, `false` otherwise, or a `SecurityModuleError` if an error occurs. + #[instrument] + fn verify_signature(&self, data: &[u8], signature: &[u8]) -> Result { + info!("verifiying"); + + let config = self + .config + .as_ref() + .ok_or(SecurityModuleError::InitializationError( + "Module is not initialized".to_owned(), + ))?; + + let env = config + .vm + .as_ref() + .ok_or_else(|| TpmError::InitializationError("Module is not initialized".to_owned()))? + .get_env() + .map_err(|_| { + TpmError::InitializationError( + "Could not get java environment, this should never happen".to_owned(), + ) + })?; + + let key_store = KeyStore::getInstance(&env, ANDROID_KEYSTORE.to_string()).err_internal()?; + key_store.load(&env, None).err_internal()?; + + let signature_algorithm = get_signature_algorithm(config.mode)?; + debug!("Signature Algorithm: {}", signature_algorithm); + + let s = Signature::getInstance(&env, signature_algorithm.to_string()).err_internal()?; + + let cert = key_store + .getCertificate(&env, self.key_id.clone()) + .err_internal()?; + + s.initVerify(&env, cert).err_internal()?; + debug!("Signature Init: {}", s.toString(&env).unwrap()); + + let data_bytes = data.to_vec().into_boxed_slice(); + s.update(&env, data_bytes).err_internal()?; + + let signature_boxed = signature.to_vec().into_boxed_slice(); + let output = s.verify(&env, signature_boxed).err_internal()?; + debug!("Signature verified: {:?}", output); + + Ok(output) + } +} diff --git a/src/tpm/android/utils.rs b/src/tpm/android/utils.rs new file mode 100644 index 00000000..96abf8f1 --- /dev/null +++ b/src/tpm/android/utils.rs @@ -0,0 +1,196 @@ +use crate::{ + common::{ + crypto::algorithms::{ + encryption::{AsymmetricEncryption, BlockCiphers, SymmetricMode}, + hashes::{Hash, Sha2Bits}, + }, + error::SecurityModuleError, + }, + tpm::core::error::TpmError, +}; + +use super::config::EncryptionMode; + +pub fn get_algorithm(enc: EncryptionMode) -> Result { + Ok(match enc { + EncryptionMode::Sym(algo) => match algo { + BlockCiphers::Aes(_, _) => "AES", + BlockCiphers::TripleDes(_) => "DESede", + BlockCiphers::Des | BlockCiphers::Rc2(_) | BlockCiphers::Camellia(_, _) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()))? + } + }, + EncryptionMode::ASym { algo, digest: _ } => match algo { + AsymmetricEncryption::Rsa(_) => "RSA", + AsymmetricEncryption::Ecc(_) => "EC", + }, + } + .to_owned()) +} + +pub fn get_cipher_mode(e_mode: EncryptionMode) -> Result { + match e_mode { + EncryptionMode::Sym(cipher) => match cipher { + BlockCiphers::Aes(mode, _) => Ok(format!( + "AES/{}/{}", + get_sym_block_mode(mode)?, + get_padding(e_mode)? + )), + BlockCiphers::TripleDes(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + BlockCiphers::Des => Ok("DES".to_owned()), + BlockCiphers::Rc2(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + BlockCiphers::Camellia(_, _) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + }, + EncryptionMode::ASym { algo, digest: _ } => match algo { + AsymmetricEncryption::Rsa(_) => Ok(format!("RSA/ECB/{}", get_padding(e_mode)?)), + AsymmetricEncryption::Ecc(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + }, + } +} + +pub fn get_sym_block_mode(mode: SymmetricMode) -> Result { + Ok(match mode { + SymmetricMode::Gcm => "GCM", + SymmetricMode::Ecb => "ECB", + SymmetricMode::Cbc => "CBC", + SymmetricMode::Ctr => "CTR", + SymmetricMode::Cfb | SymmetricMode::Ofb | SymmetricMode::Ccm => { + Err(TpmError::UnsupportedOperation( + "Only GCM, ECB, CBC and CTR as blockmodes supported".to_owned(), + ))? + } + } + .to_owned()) +} + +pub fn get_padding(mode: EncryptionMode) -> Result { + Ok(match mode { + EncryptionMode::Sym(BlockCiphers::Aes(_, _)) => "PKCS5Padding", + EncryptionMode::ASym { algo: _, digest: _ } => "PKCS1Padding", + _ => "PKCS1Padding", + } + .to_owned()) +} + +pub fn get_signature_padding() -> Result { + Ok("PKCS1".to_owned()) +} + +pub fn get_digest(digest: Hash) -> Result { + match digest { + Hash::Sha1 => Ok("SHA-1".to_owned()), + Hash::Sha2(size) => match size { + Sha2Bits::Sha224 => Ok("SHA-224".to_owned()), + Sha2Bits::Sha256 => Ok("SHA-256".to_owned()), + Sha2Bits::Sha384 => Ok("SHA-384".to_owned()), + Sha2Bits::Sha512 => Ok("SHA-512".to_owned()), + Sha2Bits::Sha512_224 | Sha2Bits::Sha512_256 => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + }, + Hash::Md5 => Ok("MD5".to_owned()), + Hash::Sha3(_) | Hash::Md2 | Hash::Md4 | Hash::Ripemd160 => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + } +} + +pub fn get_hash_name(hash: Hash) -> Result { + match hash { + Hash::Sha1 => Ok("SHA1".to_owned()), + Hash::Sha2(size) => match size { + Sha2Bits::Sha224 => Ok("SHA224".to_owned()), + Sha2Bits::Sha256 => Ok("SHA256".to_owned()), + Sha2Bits::Sha384 => Ok("SHA384".to_owned()), + Sha2Bits::Sha512 => Ok("SHA512".to_owned()), + Sha2Bits::Sha512_224 | Sha2Bits::Sha512_256 => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + }, + Hash::Md5 => Ok("MD5".to_owned()), + Hash::Sha3(_) | Hash::Md2 | Hash::Md4 | Hash::Ripemd160 => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + } +} + +pub fn get_key_size(algo: AsymmetricEncryption) -> Result { + match algo { + AsymmetricEncryption::Rsa(size) => Ok(Into::::into(size)), + AsymmetricEncryption::Ecc(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + } +} + +pub fn get_curve(algo: AsymmetricEncryption) -> Result { + match algo { + AsymmetricEncryption::Rsa(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + AsymmetricEncryption::Ecc(scheme) => { + let curve = match scheme { + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::EcDsa(v) => v, + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::EcDh(v) => v, + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::EcDaa(v) => v, + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::Sm2(v) => v, + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::EcSchnorr(v) => { + v + } + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::EcMqv(v) => v, + crate::common::crypto::algorithms::encryption::EccSchemeAlgorithm::Null => { + return Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + }; + Ok(match curve { + crate::common::crypto::algorithms::encryption::EccCurves::P256 => "secp256r1", + crate::common::crypto::algorithms::encryption::EccCurves::P384 => "secp384r1", + crate::common::crypto::algorithms::encryption::EccCurves::P521 => "secp521r1", + crate::common::crypto::algorithms::encryption::EccCurves::Secp256k1 => "secp256k1", + crate::common::crypto::algorithms::encryption::EccCurves::BrainpoolP256r1 => { + "brainpoolP256r1" + } + crate::common::crypto::algorithms::encryption::EccCurves::BrainpoolP384r1 => { + "brainpoolP384r1" + } + crate::common::crypto::algorithms::encryption::EccCurves::BrainpoolP512r1 => { + "brainpoolP512r1" + } + crate::common::crypto::algorithms::encryption::EccCurves::BrainpoolP638 => { + return Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + crate::common::crypto::algorithms::encryption::EccCurves::Curve25519 => "X25519", + crate::common::crypto::algorithms::encryption::EccCurves::Curve448 => "X448", + crate::common::crypto::algorithms::encryption::EccCurves::Frp256v1 => { + return Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + } + .to_owned()) + } + } +} + +pub fn get_signature_algorithm(mode: EncryptionMode) -> Result { + match mode { + EncryptionMode::Sym(_) => { + Err(TpmError::UnsupportedOperation("not supported".to_owned()).into()) + } + EncryptionMode::ASym { algo, digest } => { + let part1 = match algo { + AsymmetricEncryption::Rsa(_) => "RSA", + AsymmetricEncryption::Ecc(_) => "ECDSA", + }; + let part2 = get_hash_name(digest)?; + + Ok(format!("{part2}with{part1}")) + } + } +} diff --git a/src/tpm/android/wrapper/key_generation/builder.rs b/src/tpm/android/wrapper/key_generation/builder.rs new file mode 100644 index 00000000..2d799fe2 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/builder.rs @@ -0,0 +1,275 @@ +use crate::tpm::android::wrapper::key_generation::key_gen_parameter_spec::jni::KeyGenParameterSpec; + +use robusta_jni::jni::errors::Result as JniResult; +use robusta_jni::jni::objects::{AutoLocal, JObject, JValue}; +use robusta_jni::jni::sys::jsize; +use robusta_jni::jni::JNIEnv; + +/// Builder for creating `KeyGenParameterSpec` objects. +/// This class is an inner class of `KeyGenParameterSpec`. For that reason, it could not +/// be implemented using the help of `robusta_jni`. `robusta_jni` does not support inner classes. +pub struct Builder<'env: 'borrow, 'borrow> { + raw: AutoLocal<'env, 'borrow>, +} + +impl<'env: 'borrow, 'borrow> Builder<'env, 'borrow> { + /// Creates a new `Builder` instance. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `keystore_alias` - The alias for the keystore. + /// * `purposes` - The purposes for which the key can be used. + /// + /// # Returns + /// + /// A `JniResult` containing the new `Builder` instance. + pub fn new( + env: &'borrow JNIEnv<'env>, + keystore_alias: String, + purposes: i32, + ) -> JniResult { + let class = env.find_class("android/security/keystore/KeyGenParameterSpec$Builder")?; + let jstring_keystore_alias = env.new_string(keystore_alias)?; + let args = [Into::into(jstring_keystore_alias), JValue::from(purposes)]; + let obj = env.new_object(class, "(Ljava/lang/String;I)V", &args)?; + Ok(Self { + raw: AutoLocal::new(env, Into::::into(obj)), + }) + } + + /// Sets the digests for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `digests` - The digests to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_digests( + mut self, + env: &'borrow JNIEnv<'env>, + digests: Vec, + ) -> JniResult { + let string_class = env.find_class("java/lang/String")?; + let digest_array = + env.new_object_array(digests.len() as jsize, string_class, JObject::null())?; + for (i, digest) in digests.iter().enumerate() { + let jstring_digest = env.new_string(digest)?; + env.set_object_array_element(digest_array, i as jsize, jstring_digest)?; + } + + let result = env.call_method( + self.raw.as_obj(), + "setDigests", + "([Ljava/lang/String;)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[digest_array.into()], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets the encryption paddings for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `paddings` - The encryption paddings to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_encryption_paddings( + mut self, + env: &'borrow JNIEnv<'env>, + paddings: Vec, + ) -> JniResult { + let string_class = env.find_class("java/lang/String")?; + let padding_array = + env.new_object_array(paddings.len() as jsize, string_class, JObject::null())?; + for (i, padding) in paddings.iter().enumerate() { + let jstring_padding = env.new_string(padding)?; + env.set_object_array_element(padding_array, i as jsize, jstring_padding)?; + } + + let result = env.call_method( + self.raw.as_obj(), + "setEncryptionPaddings", + "([Ljava/lang/String;)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[padding_array.into()], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets the signature paddings for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `paddings` - The signature paddings to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_signature_paddings( + mut self, + env: &'borrow JNIEnv<'env>, + paddings: Vec, + ) -> JniResult { + let string_class = env.find_class("java/lang/String")?; + let padding_array = + env.new_object_array(paddings.len() as jsize, string_class, JObject::null())?; + for (i, padding) in paddings.iter().enumerate() { + let jstring_padding = env.new_string(padding)?; + env.set_object_array_element(padding_array, i as jsize, jstring_padding)?; + } + + let result = env.call_method( + self.raw.as_obj(), + "setSignaturePaddings", + "([Ljava/lang/String;)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[padding_array.into()], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets the block modes for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `block_modes` - The block modes to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_block_modes( + mut self, + env: &'borrow JNIEnv<'env>, + block_modes: Vec, + ) -> JniResult { + let string_class = env.find_class("java/lang/String")?; + let block_mode_array = + env.new_object_array(block_modes.len() as jsize, string_class, JObject::null())?; + for (i, block_mode) in block_modes.iter().enumerate() { + let jstring_block_mode = env.new_string(block_mode)?; + env.set_object_array_element(block_mode_array, i as jsize, jstring_block_mode)?; + } + + let result = env.call_method( + self.raw.as_obj(), + "setBlockModes", + "([Ljava/lang/String;)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[block_mode_array.into()], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets the key size for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `key_size` - The key size to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_key_size(mut self, env: &'borrow JNIEnv<'env>, key_size: i32) -> JniResult { + let result = env.call_method( + self.raw.as_obj(), + "setKeySize", + "(I)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[JValue::Int(key_size)], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets the algorithm parameter specification for the key. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `spec` - The algorithm parameter specification to set. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_algorithm_parameter_spec( + mut self, + env: &'borrow JNIEnv<'env>, + spec: JObject, + ) -> JniResult { + let result = env.call_method( + self.raw.as_obj(), + "setAlgorithmParameterSpec", + "(Ljavax/crypto/spec/AlgorithmParameterSpec;)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[JValue::Object(spec)], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Sets whether the key is backed by a strongbox. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// * `is_strongbox_backed` - Whether the key is strongbox backed. + /// + /// # Returns + /// + /// A `JniResult` containing the updated `Builder` instance. + pub fn set_is_strongbox_backed( + mut self, + env: &'borrow JNIEnv<'env>, + is_strongbox_backed: bool, + ) -> JniResult { + let result = env.call_method( + self.raw.as_obj(), + "setIsStrongBoxBacked", + "(Z)Landroid/security/keystore/KeyGenParameterSpec$Builder;", + &[JValue::Bool(is_strongbox_backed.into())], + )?; + self.raw = AutoLocal::new(env, result.l()?); + Ok(self) + } + + /// Builds the `KeyGenParameterSpec` object. + /// + /// # Arguments + /// + /// * `self` - The `Builder` instance. + /// * `env` - The JNI environment. + /// + /// # Returns + /// + /// A `JniResult` containing the built `KeyGenParameterSpec` object. + pub fn build( + self, + env: &'borrow JNIEnv<'env>, + ) -> JniResult> { + let result = env.call_method( + self.raw.as_obj(), + "build", + "()Landroid/security/keystore/KeyGenParameterSpec;", + &[], + )?; + Ok(KeyGenParameterSpec { + raw: AutoLocal::new(env, result.l()?), + }) + } +} diff --git a/src/tpm/android/wrapper/key_generation/key.rs b/src/tpm/android/wrapper/key_generation/key.rs new file mode 100644 index 00000000..cefbe246 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/key.rs @@ -0,0 +1,57 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for key generation in Android. +pub mod jni { + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{errors::Result as JniResult, objects::AutoLocal, JNIEnv}, + }; + + /// Represents a key in Java's `java.security` package. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct Key<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(javax.crypto)] + pub struct SecretKey<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + /// Represents a public key in Java's `java.security` package. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct PublicKey<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> PublicKey<'env, 'borrow> { + /// Converts the public key to its string representation. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + + /// Retrieves the algorithm used by the public key. + pub extern "java" fn getAlgorithm(&self, _env: &JNIEnv) -> JniResult {} + } + + /// Represents a private key in Java's `java.security` package. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct PrivateKey<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> PrivateKey<'env, 'borrow> { + /// Converts the private key to its string representation. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + + /// Retrieves the algorithm used by the private key. + pub extern "java" fn getAlgorithm(&self, _env: &JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_generation/key_gen_parameter_spec.rs b/src/tpm/android/wrapper/key_generation/key_gen_parameter_spec.rs new file mode 100644 index 00000000..1bf683a4 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/key_gen_parameter_spec.rs @@ -0,0 +1,44 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the KeyGenParameterSpec struct/class. +pub mod jni { + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{errors::Result as JniResult, objects::AutoLocal, JNIEnv}, + }; + + /// Represents the KeyGenParameterSpec struct in the android.security.keystore package. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(android.security.keystore)] + pub struct KeyGenParameterSpec<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> KeyGenParameterSpec<'env, 'borrow> { + /// Retrieves the supported digest algorithms for the key generation. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// + /// # Returns + /// + /// A Result containing a vector of strings representing the supported digest algorithms, + /// or an error if the JNI call fails. + pub extern "java" fn getDigests(&self, env: &JNIEnv) -> JniResult> {} + + /// Checks if the key generation is backed by a StrongBox. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// + /// # Returns + /// + /// A Result containing a boolean value indicating whether the key generation is backed by a StrongBox, + /// or an error if the JNI call fails. + pub extern "java" fn isStrongBoxBacked(&self, env: &JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_generation/key_generator.rs b/src/tpm/android/wrapper/key_generation/key_generator.rs new file mode 100644 index 00000000..f200c7b8 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/key_generator.rs @@ -0,0 +1,39 @@ +use robusta_jni::bridge; + +#[bridge] +pub mod jni { + use crate::tpm::android::wrapper::key_generation::key::jni::SecretKey; + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{ + errors::Result as JniResult, + objects::{AutoLocal, JObject}, + JNIEnv, + }, + }; + + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(javax.crypto)] + pub struct KeyGenerator<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> KeyGenerator<'env, 'borrow> { + pub extern "java" fn getInstance( + env: &'borrow JNIEnv<'env>, + algorithm: String, + provider: String, + ) -> JniResult { + } + + pub extern "java" fn init( + &self, + env: &JNIEnv, + #[input_type("Ljava/security/spec/AlgorithmParameterSpec;")] params: JObject, + ) -> JniResult<()> { + } + + pub extern "java" fn generateKey(&self, _env: &'borrow JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_generation/key_pair.rs b/src/tpm/android/wrapper/key_generation/key_pair.rs new file mode 100644 index 00000000..b8934478 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/key_pair.rs @@ -0,0 +1,30 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the `KeyPair` struct used in Android TPM key generation. +pub mod jni { + use crate::tpm::android::wrapper::key_generation::key::jni::{PrivateKey, PublicKey}; + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{errors::Result as JniResult, objects::AutoLocal, JNIEnv}, + }; + + /// Represents a Java `KeyPair` object. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct KeyPair<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> KeyPair<'env, 'borrow> { + /// Returns a string representation of the `KeyPair` object. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + + /// Returns the public key associated with the `KeyPair` object. + pub extern "java" fn getPublic(&self, _env: &'borrow JNIEnv) -> JniResult {} + + /// Returns the private key associated with the `KeyPair` object. + pub extern "java" fn getPrivate(&self, _env: &'borrow JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_generation/key_pair_generator.rs b/src/tpm/android/wrapper/key_generation/key_pair_generator.rs new file mode 100644 index 00000000..67671a14 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/key_pair_generator.rs @@ -0,0 +1,104 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the `KeyPairGenerator` class in the Android TPM wrapper. +pub mod jni { + use crate::tpm::android::wrapper::key_generation::key_pair::jni::KeyPair; + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{ + errors::Result as JniResult, + objects::{AutoLocal, JObject}, + JNIEnv, + }, + }; + + /// Represents a Java `KeyPairGenerator` object. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct KeyPairGenerator<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> KeyPairGenerator<'env, 'borrow> { + /// Returns an instance of `KeyPairGenerator` for the specified algorithm and provider. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `algorithm` - The name of the algorithm. + /// * `provider` - The name of the provider. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the `KeyPairGenerator` instance. + pub extern "java" fn getInstance( + env: &'borrow JNIEnv<'env>, + algorithm: String, + provider: String, + ) -> JniResult { + } + + /// Returns a string representation of the `KeyPairGenerator` object. + /// + /// # Arguments + /// + /// * `_env` - The JNI environment. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the string representation. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + + /// Returns the algorithm name associated with the `KeyPairGenerator` object. + /// + /// # Arguments + /// + /// * `_env` - The JNI environment. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the algorithm name. + pub extern "java" fn getAlgorithm(&self, _env: &JNIEnv) -> JniResult {} + + /// Initializes the `KeyPairGenerator` object with the specified algorithm parameters. + /// + /// Initializes the key pair generator using the specified parameter set and the SecureRandom + /// implementation of the highest-priority installed provider as the source of randomness. + /// (If none of the installed providers supply an implementation of SecureRandom, a system-provided source of randomness is used.) + /// + /// Could not be implemented using `robusta_jni` because the params parameter is the class + /// AlgorithmParameterSpec. AlgorithmParameterSpec is an interface and we need to pass an object + /// of type KeyGenParameterSpec. This causes the signatures to not match, meaning the jni call fails. + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `params` - The algorithm parameter specification. + /// + /// # Returns + /// + /// Returns a `JniResult` indicating success or failure. + pub extern "java" fn initialize( + &self, + env: &JNIEnv, + #[input_type("Ljava/security/spec/AlgorithmParameterSpec;")] params: JObject, + ) -> JniResult<()> { + } + + /// Generates a key pair using the `KeyPairGenerator` object. + /// + /// If this KeyPairGenerator has not been initialized explicitly, provider-specific defaults + /// will be used for the size and other (algorithm-specific) values of the generated keys. + /// This will generate a new key pair every time it is called. + /// + /// # Arguments + /// + /// * `_env` - The JNI environment. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the generated `KeyPair`. + pub extern "java" fn generateKeyPair(&self, _env: &'borrow JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_generation/mod.rs b/src/tpm/android/wrapper/key_generation/mod.rs new file mode 100644 index 00000000..a190cf00 --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/mod.rs @@ -0,0 +1,9 @@ +#![allow(clippy::needless_borrow)] + +pub mod builder; +pub mod key; +pub mod key_gen_parameter_spec; +pub mod key_generator; +pub mod key_pair; +pub mod key_pair_generator; +pub mod secure_random; diff --git a/src/tpm/android/wrapper/key_generation/secure_random.rs b/src/tpm/android/wrapper/key_generation/secure_random.rs new file mode 100644 index 00000000..8a762a7b --- /dev/null +++ b/src/tpm/android/wrapper/key_generation/secure_random.rs @@ -0,0 +1,30 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the `SecureRandom` class in the `java.security` package. +/// This might not even be necessary. The `SecureRandom` is automatically generated by `KeyPairGenerator.initialize` +pub mod jni { + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::errors::Result as JniResult, + jni::objects::AutoLocal, + jni::JNIEnv, + }; + + /// Represents the `SecureRandom` class in Java. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct SecureRandom<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> SecureRandom<'env, 'borrow> { + /// Constructs a new `SecureRandom` instance. + #[constructor] + pub extern "java" fn new(env: &'borrow JNIEnv<'env>) -> JniResult {} + + /// Returns the algorithm name of the `SecureRandom` instance. + pub extern "java" fn getAlgorithm(&self, env: &JNIEnv<'env>) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_store/cipher.rs b/src/tpm/android/wrapper/key_store/cipher.rs new file mode 100644 index 00000000..a3cfb939 --- /dev/null +++ b/src/tpm/android/wrapper/key_store/cipher.rs @@ -0,0 +1,86 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the Cipher class in the javax.crypto package. +pub mod jni { + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{ + errors::Result as JniResult, + objects::{AutoLocal, JObject, JValue}, + sys::jbyteArray, + JNIEnv, + }, + }; + + /// Represents a Cipher object in Java. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(javax.crypto)] + pub struct Cipher<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> Cipher<'env, 'borrow> { + /// Creates a new instance of the Cipher class. + /// + /// # Arguments + /// + /// * `env` - The JNIEnv object. + /// * `transformation` - the name of the transformation, e.g., DES/CBC/PKCS5Padding. See the Cipher section in the Java Cryptography Architecture Standard Algorithm Name Documentation for information about standard transformation names. + /// + /// # Returns + /// + /// Returns a Cipher object that implements the specified transformation. + pub extern "java" fn getInstance( + env: &'borrow JNIEnv<'env>, + transformation: String, + ) -> JniResult { + } + + /// Initializes the Cipher object with the specified operation mode and key. + /// + /// # Arguments + /// + /// * `env` - The JNIEnv object. + /// * `opmode` - The operation mode. + /// * `key` - The key object. + /// + /// # Returns + /// + /// Returns a JniResult indicating success or failure. + pub extern "java" fn init( + &self, + env: &'borrow JNIEnv<'env>, + opmode: i32, + #[input_type("Ljava/security/Key;")] key: JObject, + ) -> JniResult<()> { + } + + /// Performs the final operation of the Cipher, processing any remaining data. + /// + /// # Arguments + /// + /// * `env` - The JNIEnv object. + /// * `input` - The input data. + /// + /// # Returns + /// + /// Returns a JniResult containing the output data. + pub fn doFinal(&self, env: &JNIEnv, input: Vec) -> JniResult> { + let input_array = env.byte_array_from_slice(&input)?; + + let output = env.call_method( + self.raw.as_obj(), + "doFinal", + "([B)[B", + &[JValue::from(input_array)], + )?; + + let output_array = output.l()?.into_inner() as jbyteArray; + let output_vec = env.convert_byte_array(output_array).unwrap(); + + Ok(output_vec) + } + } +} diff --git a/src/tpm/android/wrapper/key_store/key_store.rs b/src/tpm/android/wrapper/key_store/key_store.rs new file mode 100644 index 00000000..3291df36 --- /dev/null +++ b/src/tpm/android/wrapper/key_store/key_store.rs @@ -0,0 +1,131 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the KeyStore functionality in Android. +pub mod jni { + use crate::tpm::android::wrapper::key_generation::key::jni::{Key, PublicKey}; + use robusta_jni::{ + convert::{IntoJavaValue, Signature, TryFromJavaValue, TryIntoJavaValue}, + jni::{ + errors::Result as JniResult, + objects::{AutoLocal, JObject}, + JNIEnv, + }, + }; + + /// Represents a KeyStore object in Java. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct KeyStore<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> KeyStore<'env, 'borrow> { + /// Retrieves an instance of the KeyStore class. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `type1` - The type of the KeyStore. In java the paramenter is just 'type', but we have to use 'type1' because 'type' is a reserved keyword in Rust. + /// + /// # Returns + /// + /// Returns a keystore object of the specified type. + pub extern "java" fn getInstance( + env: &'borrow JNIEnv<'env>, + type1: String, + ) -> JniResult { + } + + /// Retrieves a certificate from the KeyStore. + /// + /// Returns the certificate associated with the given alias. + /// If the given alias name identifies an entry created by a call to setCertificateEntry, + /// or created by a call to setEntry with a TrustedCertificateEntry, then the trusted certificate + /// contained in that entry is returned. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `alias` - The alias name. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the Certificate instance. + pub extern "java" fn getCertificate( + &self, + env: &'borrow JNIEnv<'env>, + alias: String, + ) -> JniResult { + } + + /// Retrieves a key from the KeyStore. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `alias` - The alias of the key. + /// * `password` - The password for the key. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the Key instance. + pub extern "java" fn getKey( + &self, + env: &'borrow JNIEnv<'env>, + alias: String, + #[input_type("[C")] password: JObject, + ) -> JniResult { + } + + /// Loads the KeyStore. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `param` - An optional parameter for loading the KeyStore. + /// + /// # Returns + /// + /// Returns a `JniResult` indicating the success or failure of the operation. + pub fn load(&self, env: &JNIEnv, param: Option) -> JniResult<()> { + let param_obj = param.unwrap_or(JObject::null()); + env.call_method( + self.raw.as_obj(), + "load", + "(Ljava/security/KeyStore$LoadStoreParameter;)V", + &[Into::into(param_obj)], + )?; + Ok(()) + } + } + + /// Represents a Certificate object in Java. + #[derive(Signature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security.cert)] + pub struct Certificate<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> Certificate<'env, 'borrow> { + /// Retrieves the public key from the Certificate. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// + /// # Returns + /// + /// Returns a `JniResult` containing the PublicKey instance. + pub extern "java" fn getPublicKey( + &self, + env: &'borrow JNIEnv<'env>, + ) -> JniResult> { + } + + /// toString Java method of the Certificate class. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/key_store/mod.rs b/src/tpm/android/wrapper/key_store/mod.rs new file mode 100644 index 00000000..75a40b47 --- /dev/null +++ b/src/tpm/android/wrapper/key_store/mod.rs @@ -0,0 +1,5 @@ +#![allow(clippy::needless_borrow)] + +pub mod cipher; +pub mod key_store; +pub mod signature; diff --git a/src/tpm/android/wrapper/key_store/signature.rs b/src/tpm/android/wrapper/key_store/signature.rs new file mode 100644 index 00000000..fc49b3ea --- /dev/null +++ b/src/tpm/android/wrapper/key_store/signature.rs @@ -0,0 +1,133 @@ +use robusta_jni::bridge; + +#[bridge] +/// This module contains the JNI bindings for the Signature class in the Java security package. +pub mod jni { + use crate::tpm::android::wrapper::key_store::key_store::jni::Certificate; + use robusta_jni::{ + convert::{IntoJavaValue, Signature as JavaSignature, TryFromJavaValue, TryIntoJavaValue}, + jni::{ + errors::Result as JniResult, + objects::{AutoLocal, JObject, JValue}, + JNIEnv, + }, + }; + + /// Represents a Signature object in Java. + #[derive(JavaSignature, TryIntoJavaValue, IntoJavaValue, TryFromJavaValue)] + #[package(java.security)] + pub struct Signature<'env: 'borrow, 'borrow> { + #[instance] + pub raw: AutoLocal<'env, 'borrow>, + } + + impl<'env: 'borrow, 'borrow> Signature<'env, 'borrow> { + /// Creates a new instance of the Signature class with the specified algorithm. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `algorithm` - The algorithm to use for the Signature instance. + /// + /// # Returns + /// + /// Returns a Result containing the Signature instance if successful, or an error if it fails. + pub extern "java" fn getInstance( + env: &'borrow JNIEnv<'env>, + algorithm: String, + ) -> JniResult { + } + + /// Signs the data using the Signature instance. + /// + /// Could not be implemented using `robusta_jni` because the Java method returns a byte array, + /// and byte arrays are not supported as a return value by `robusta_jni`. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// + /// # Returns + /// + /// Returns a Result containing the signed data as a Vec if successful, or an error if it fails. + pub fn sign(&self, env: &JNIEnv) -> JniResult> { + let result = env.call_method(self.raw.as_obj(), "sign", "()[B", &[])?; + + let byte_array = result.l()?.into_inner(); + let output = env.convert_byte_array(byte_array)?; + + Ok(output) + } + + /// Initializes the Signature instance for signing with the specified private key. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `privateKey` - The private key to use for signing. + /// + /// # Returns + /// + /// Returns a Result indicating success or failure. + pub extern "java" fn initSign( + &self, + env: &JNIEnv, + #[input_type("Ljava/security/PrivateKey;")] privateKey: JObject, + ) -> JniResult<()> { + } + + /// Initializes the Signature instance for verification with the specified certificate. + /// + /// Could not be implemented using `robusta_jni` because for some reason it doesn't + /// recognize the `Certificate` signature correctly. + /// + /// # Arguments + /// + /// * `env` - The JNI environment. + /// * `certificate` - The certificate to use for verification. + /// + /// # Returns + /// + /// Returns a Result indicating success or failure. + pub fn initVerify(&self, env: &JNIEnv, certificate: Certificate) -> JniResult<()> { + let certificate_obj = certificate.raw.as_obj(); + + env.call_method( + self.raw.as_obj(), + "initVerify", + "(Ljava/security/cert/Certificate;)V", + &[JValue::from(certificate_obj)], + )?; + + Ok(()) + } + + /// Verifies the signature against the specified data. + /// + /// # Arguments + /// + /// * `_env` - The JNI environment. + /// * `signature` - The signature to verify. + /// + /// # Returns + /// + /// Returns a Result indicating whether the signature is valid or not. + pub extern "java" fn verify(&self, _env: &JNIEnv, signature: Box<[u8]>) -> JniResult { + } + + /// Updates the Signature instance with additional data to be signed or verified. + /// + /// # Arguments + /// + /// * `_env` - The JNI environment. + /// * `data` - The data to update the Signature instance with. + /// + /// # Returns + /// + /// Returns a Result indicating success or failure. + pub extern "java" fn update(&self, _env: &JNIEnv, data: Box<[u8]>) -> JniResult<()> {} + + /// toString Java method of the Signature class. + pub extern "java" fn toString(&self, _env: &JNIEnv) -> JniResult {} + } +} diff --git a/src/tpm/android/wrapper/mod.rs b/src/tpm/android/wrapper/mod.rs new file mode 100644 index 00000000..9f90f251 --- /dev/null +++ b/src/tpm/android/wrapper/mod.rs @@ -0,0 +1,61 @@ +use robusta_jni::jni::{ + self, + sys::{jint, jsize}, + JavaVM, +}; + +use crate::tpm::core::error::TpmError; + +pub(crate) mod key_generation; +pub(crate) mod key_store; + +/// This function gets the current Java VM running for the Android app. +/// Every Android app can have only 1 JVM running, so we can't just create a new one. +/// Normally it would be possible to just call the "JNI_GetCreatedJavaVMs" C function, but we can't link against it for some reason +/// so we have to load the symbol manually using the libloading crate. +pub(super) fn get_java_vm() -> Result { + // using jni_sys::JNI_GetCreatedJavaVMs crashes, bc the symbol is not loaded into the process for some reason + // instead we use libloading to load the symbol ourselves + pub type JniGetCreatedJavaVms = unsafe extern "system" fn( + vmBuf: *mut *mut jni::sys::JavaVM, + bufLen: jsize, + nVMs: *mut jsize, + ) -> jint; + pub const JNI_GET_JAVA_VMS_NAME: &[u8] = b"JNI_GetCreatedJavaVMs"; + + let lib = libloading::os::unix::Library::this(); + // let lib = unsafe { libloading::os::unix::Library::new("libart.so") } + // .map_err(|e| TpmError::InitializationError(format!("could not find libart.so: {e}")))?; + + let get_created_java_vms: JniGetCreatedJavaVms = unsafe { + *lib.get(JNI_GET_JAVA_VMS_NAME).map_err(|e| { + TpmError::InitializationError(format!("function JNI_GET_JAVA_VMS_NAME not loaded: {e}")) + })? + }; + + // now that we have the function, we can call it + let mut buffer = [std::ptr::null_mut::(); 1]; + let buffer_ptr = buffer.as_mut_ptr(); + let mut found_vms = 0; + let found_vm_ptr = &mut found_vms as *mut i32; + let res = unsafe { get_created_java_vms(buffer_ptr, 1, found_vm_ptr) }; + + if res != jni::sys::JNI_OK { + return Err(TpmError::InitializationError( + "Unable to get existing JVMs".to_owned(), + )); + } + + if found_vms == 0 { + return Err(TpmError::InitializationError( + "No running JVMs found".to_owned(), + )); + } + + let jvm = unsafe { + JavaVM::from_raw(buffer[0]).map_err(|e| TpmError::InitializationError(e.to_string()))? + }; + jvm.attach_current_thread() + .map_err(|e| TpmError::InitializationError(e.to_string()))?; + Ok(jvm) +} diff --git a/src/tpm/core/error.rs b/src/tpm/core/error.rs index ee6bdffb..a706ad4f 100644 --- a/src/tpm/core/error.rs +++ b/src/tpm/core/error.rs @@ -19,6 +19,8 @@ pub enum TpmError { InitializationError(String), /// Error indicating that an attempted operation is unsupported, containing a description. UnsupportedOperation(String), + /// Error indicating that an internal error occured, possibly caused by ffi bindings + InternalError(Box), } impl fmt::Display for TpmError { @@ -39,10 +41,18 @@ impl TpmError { TpmError::Win(err) => format!("Windows error: {}", err), TpmError::InitializationError(msg) => format!("Initialization error: {}", msg), TpmError::UnsupportedOperation(msg) => format!("Unsupported operation: {}", msg), + TpmError::InternalError(e) => format!("Internal error: {}", e), } } } +/// A trait to allow ergonomic conversions to TpmError +pub trait ToTpmError { + /// Wrap any error in TpmError::InternalError + /// the wrapped error can be accessed througth the error trait + fn err_internal(self) -> Result; +} + /// Enables `TpmError` to be treated as a trait object for any error (`dyn std::error::Error`). /// /// This implementation allows for compatibility with Rust's standard error handling mechanisms, diff --git a/src/tpm/core/instance.rs b/src/tpm/core/instance.rs index 631bc857..6f49b4e3 100644 --- a/src/tpm/core/instance.rs +++ b/src/tpm/core/instance.rs @@ -37,6 +37,8 @@ pub enum TpmType { #[derive(Eq, Hash, PartialEq, Clone, Debug)] #[cfg(feature = "android")] pub enum AndroidTpmType { + /// Android Provider using the Android Keystore API + Keystore, /// Represents the Samsung Knox security platform with TPM functionalities. Knox, } @@ -123,7 +125,12 @@ impl TpmInstance { Arc::new(Mutex::new(instance)) } #[cfg(feature = "android")] - TpmType::Android(_tpm_type) => todo!(), + TpmType::Android(tpm_type) => match tpm_type { + AndroidTpmType::Keystore => Arc::new(Mutex::new( + crate::tpm::android::AndroidProvider::new(key_id), + )), + AndroidTpmType::Knox => todo!(), + }, TpmType::None => todo!(), } }