diff --git a/Cargo.toml b/Cargo.toml index bcc2ddf..d63d153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "effa378c4 [dev-dependencies] hex-literal = "0.3.4" -trussed = { git = "https://github.com/trussed-dev/trussed.git", rev = "effa378c4709a2ec6119673894ed68a18d8e7391", features = ["virt"] } delog = { version = "0.1.6", features = ["std-log"] } test-log = "0.2.11" env_logger = "0.10.0" @@ -28,6 +27,10 @@ rand = "0.8.5" [features] +default = [] +virt = ["std","trussed/virt"] +std = [] + log-all = [] log-none = [] log-info = [] diff --git a/src/crypto_traits.rs b/src/crypto_traits.rs index e81cd07..93fe86b 100644 --- a/src/crypto_traits.rs +++ b/src/crypto_traits.rs @@ -46,17 +46,17 @@ pub trait Rsa2048Pkcs1v15: CryptoClient { /// The resulting [`serialized_key`](trussed::api::reply::SerializeKey::serialized_key) contains a buffer of the parts of the key /// as a postcard-serialized [`RsaPublicParts`](crate::RsaPublicParts): /// ``` + ///# #[cfg(feature = "virt")] + ///# { ///# use trussed_rsa_backend::*; ///# use trussed::{postcard_deserialize,syscall,types::Location::{Volatile,Internal}}; - ///# mod client { - ///# include!("../tests/client/mod.rs"); - ///# } - ///# client::get(|client| { + ///# virt::with_ram_client(|client| { ///# let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; ///# let pk = syscall!(client.derive_rsa2048pkcs_public_key(sk, Volatile)).key; /// let serialized_key = syscall!(client.serialize_rsa2048_key(pk)).serialized_key; /// let public_key: RsaPublicParts = postcard_deserialize(&serialized_key).unwrap(); ///# }) + ///# } ///``` fn serialize_rsa2048_key(&mut self, key: KeyId) -> ClientResult<'_, reply::SerializeKey, Self> { self.serialize_key(Mechanism::Rsa2048Pkcs1v15, key, KeySerialization::RsaParts) @@ -165,17 +165,17 @@ pub trait Rsa3072Pkcs1v15: CryptoClient { /// The resulting [`serialized_key`](trussed::api::reply::SerializeKey::serialized_key) contains a buffer of the parts of the key /// as a postcard-serialized [`RsaPublicParts`](crate::RsaPublicParts): /// ``` + ///# #[cfg(feature = "virt")] + ///# { ///# use trussed_rsa_backend::*; ///# use trussed::{postcard_deserialize,syscall,types::Location::{Volatile,Internal}}; - ///# mod client { - ///# include!("../tests/client/mod.rs"); - ///# } - ///# client::get(|client| { + ///# virt::with_ram_client(|client| { ///# let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; ///# let pk = syscall!(client.derive_rsa3072pkcs_public_key(sk, Volatile)).key; /// let serialized_key = syscall!(client.serialize_rsa3072_key(pk)).serialized_key; /// let public_key: RsaPublicParts = postcard_deserialize(&serialized_key).unwrap(); ///# }) + ///# } ///``` fn serialize_rsa3072_key(&mut self, key: KeyId) -> ClientResult<'_, reply::SerializeKey, Self> { self.serialize_key(Mechanism::Rsa3072Pkcs1v15, key, KeySerialization::RsaParts) @@ -284,17 +284,17 @@ pub trait Rsa4096Pkcs1v15: CryptoClient { /// The resulting [`serialized_key`](trussed::api::reply::SerializeKey::serialized_key) contains a buffer of the parts of the key /// as a postcard-serialized [`RsaPublicParts`](crate::RsaPublicParts): /// ``` + ///# #[cfg(feature = "virt")] + ///# { ///# use trussed_rsa_backend::*; ///# use trussed::{postcard_deserialize,syscall,types::Location::{Volatile,Internal}}; - ///# mod client { - ///# include!("../tests/client/mod.rs"); - ///# } - ///# client::get(|client| { + ///# virt::with_ram_client(|client| { ///# let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; ///# let pk = syscall!(client.derive_rsa4096pkcs_public_key(sk, Volatile)).key; /// let serialized_key = syscall!(client.serialize_rsa4096_key(pk)).serialized_key; /// let public_key: RsaPublicParts = postcard_deserialize(&serialized_key).unwrap(); ///# }) + ///# } ///``` fn serialize_rsa4096_key(&mut self, key: KeyId) -> ClientResult<'_, reply::SerializeKey, Self> { self.serialize_key(Mechanism::Rsa4096Pkcs1v15, key, KeySerialization::RsaParts) diff --git a/src/lib.rs b/src/lib.rs index 26703b8..159f12b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] use heapless_bytes::Bytes; use num_bigint_dig::traits::ModInverse; @@ -21,6 +21,9 @@ use trussed::{ Error, }; +#[cfg(feature = "virt")] +pub mod virt; + #[macro_use] extern crate delog; generate_macros!(); diff --git a/tests/client/mod.rs b/src/virt.rs similarity index 50% rename from tests/client/mod.rs rename to src/virt.rs index e8ef292..224b13f 100644 --- a/tests/client/mod.rs +++ b/src/virt.rs @@ -1,4 +1,5 @@ -use trussed_rsa_backend::SoftwareRsa; +/// Wrapper around [`trussed::virt`][] that provides clients with both the core backend and the [`SoftwareRsa`](crate::SoftwareRsa) backend. +use crate::SoftwareRsa; pub struct Dispatcher; pub enum BackendIds { @@ -18,19 +19,39 @@ impl Dispatch

for Dispatcher { } } +use std::path::PathBuf; use trussed::{ backend::{Backend, BackendId, Dispatch}, - virt::{self, Client, Ram}, + virt::{self, Client, Filesystem, Ram, StoreProvider}, Platform, }; -pub fn get) -> R>(test: F) -> R { - virt::with_platform(Ram::default(), |platform| { +pub fn with_client(store: S, f: F) -> R +where + F: FnOnce(&mut Client) -> R, + S: StoreProvider, +{ + virt::with_platform(store, |platform| { platform.run_client_with_backends( "rsa tests", Dispatcher, &[BackendId::Custom(BackendIds::SoftwareRsa), BackendId::Core], - |mut client| test(&mut client), + |mut client| f(&mut client), ) }) } + +pub fn with_fs_client(internal: P, f: F) -> R +where + F: FnOnce(&mut Client) -> R, + P: Into, +{ + with_client(Filesystem::new(internal), f) +} + +pub fn with_ram_client(f: F) -> R +where + F: FnOnce(&mut Client) -> R, +{ + with_client(Ram::default(), f) +} diff --git a/tests/other.rs b/tests/other.rs index afc1a7c..72d2a1f 100644 --- a/tests/other.rs +++ b/tests/other.rs @@ -1,12 +1,13 @@ +#![cfg(feature = "virt")] + ///! Test that the core backend is still reachable. ///! Tests imported from the trussed repo -mod client; - use trussed::{client::CertificateClient as _, syscall, try_syscall, types::Location::*}; +use trussed_rsa_backend::virt; #[test] fn certificate_client() { - client::get(|client| { + virt::with_ram_client(|client| { let fake_der = &[1u8, 2, 3]; let id = syscall!(client.write_certificate(Volatile, fake_der)).id; diff --git a/tests/rsa2048.rs b/tests/rsa2048.rs index 2062b15..b80b09a 100644 --- a/tests/rsa2048.rs +++ b/tests/rsa2048.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "virt")] + use rsa::sha2::Sha256; use rsa::{Pkcs1v15Encrypt, Pkcs1v15Sign, PublicKeyParts}; use trussed::client::CryptoClient; @@ -16,13 +18,11 @@ use hex_literal::hex; use num_bigint_dig::BigUint; use rsa::{PublicKey, RsaPrivateKey}; -mod client; - // Tests below can be run on a PC using the "virt" feature #[test_log::test] fn rsa2048pkcs_generate_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; // This assumes we don't ever get a key with ID 0 @@ -32,7 +32,7 @@ fn rsa2048pkcs_generate_key() { #[test_log::test] fn rsa2048pkcs_derive_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa2048pkcs_public_key(sk, Volatile)).key; @@ -43,7 +43,7 @@ fn rsa2048pkcs_derive_key() { #[test_log::test] fn rsa2048pkcs_exists_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; let key_exists = syscall!(client.exists(trussed::types::Mechanism::Rsa2048Pkcs1v15, sk)).exists; @@ -54,7 +54,7 @@ fn rsa2048pkcs_exists_key() { #[test_log::test] fn rsa2048pkcs_serialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa2048pkcs_public_key(sk, Volatile)).key; @@ -66,7 +66,7 @@ fn rsa2048pkcs_serialize_key() { #[test_log::test] fn rsa2048_deserialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa2048pkcs_public_key(sk, Volatile)).key; let serialized_key = syscall!(client.serialize_rsa2048_key(pk)).serialized_key; @@ -83,7 +83,7 @@ fn rsa2048_deserialize_key() { #[test_log::test] fn rsa2048pkcs_encrypt_decrypt() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Volatile)).key; let message = [1u8, 2u8, 3u8]; let pk = syscall!(client.derive_rsa2048pkcs_public_key(sk, Volatile)).key; @@ -108,7 +108,7 @@ fn rsa2048pkcs_encrypt_decrypt() { #[test_log::test] fn rsa2048pkcs_sign_verify() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa2048pkcs_private_key(Volatile)).key; let hash_prefix = hex!("3031 300d 0609 608648016503040201 0500 0420"); let message = [1u8, 2u8, 3u8]; @@ -126,7 +126,7 @@ fn rsa2048pkcs_sign_verify() { } #[test_log::test] fn rsa2048pkcs_inject() { - client::get(|client| { + virt::with_ram_client(|client| { let n = hex!("b43f96eee6abf0e71d81244f9adcc049c379f22a40d99e0a921fca08c1a83695f2060eeebc52823e8fa59f61156e42119758c3937c848a69e13a4a3ee23f35bb923a63b7d0cec6092957ff038b58c63339f300fb0d6dfc3d239fb8ef2caafbb40ca98fbd795e6ab5128a6e880b72a0637bfb197ea6697cd045c648d2a55f0f0e181d6bb50e56f297c8da164a3b04fab69e66107a7767e3a2c1df5e655c40db3e76e469e6db71b2d4edd73d48eee894d3c6c8e966bc2153256b014bc63a8f02c59a06b89004903ec4887ac916e2f7c5077b93eef17e914bb07add9dced384946f89d99ba48b28eedcc511ce359d2b2bce8052181f229033b6f2b1a905a55b33bd"); let e = hex!("010001"); let d = hex!("0ac47db4b9ccedb030c00536482f05c1a24ec79ba4921b71d036dbefd7f9bf81079b3b0b21eedfdef2dfd6fc8ab63276308f59e79699a85718e04d8d2220da89e0fb61f79a1eb00fde0b66ad848682188f4ea7f15765099b71645a3cd773436407199dff989f7e4a60d82a303056e1a3efc51949ca9124a6a0746ee73e7fc63b5c9df7e15be95b3f83dbb81a3a95284b52ca584fd058e9dbe74285b85b13688225c72cfc4c636950553aa31670de8dac45abac75e8872ee623f6cb0974c1915600bfc8e5c60e38101ae558ab3400d540b1db36b5eb6d9a0674ddbb814b69258ef15a0a3d07d557856a30af72d5c8ebc26d8cb067be783a5aea564afba4e28181"); diff --git a/tests/rsa3072.rs b/tests/rsa3072.rs index f42f6d6..93e6fb3 100644 --- a/tests/rsa3072.rs +++ b/tests/rsa3072.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "virt")] + use rsa::sha2::Sha384; use rsa::{Pkcs1v15Encrypt, Pkcs1v15Sign, PublicKeyParts}; use trussed::client::CryptoClient; @@ -16,13 +18,11 @@ use hex_literal::hex; use num_bigint_dig::BigUint; use rsa::{PublicKey, RsaPrivateKey}; -mod client; - // Tests below can be run on a PC using the "virt" feature #[test_log::test] fn rsa3072pkcs_generate_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; // This assumes we don't ever get a key with ID 0 @@ -32,7 +32,7 @@ fn rsa3072pkcs_generate_key() { #[test_log::test] fn rsa3072pkcs_derive_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa3072pkcs_public_key(sk, Volatile)).key; @@ -43,7 +43,7 @@ fn rsa3072pkcs_derive_key() { #[test_log::test] fn rsa3072pkcs_exists_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; let key_exists = syscall!(client.exists(trussed::types::Mechanism::Rsa3072Pkcs1v15, sk)).exists; @@ -54,7 +54,7 @@ fn rsa3072pkcs_exists_key() { #[test_log::test] fn rsa3072pkcs_serialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa3072pkcs_public_key(sk, Volatile)).key; @@ -66,7 +66,7 @@ fn rsa3072pkcs_serialize_key() { #[test_log::test] fn rsa3072_deserialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa3072pkcs_public_key(sk, Volatile)).key; let serialized_key = syscall!(client.serialize_rsa3072_key(pk)).serialized_key; @@ -83,7 +83,7 @@ fn rsa3072_deserialize_key() { #[test_log::test] fn rsa3072pkcs_encrypt_decrypt() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Volatile)).key; let message = [1u8, 2u8, 3u8]; let pk = syscall!(client.derive_rsa3072pkcs_public_key(sk, Volatile)).key; @@ -108,7 +108,7 @@ fn rsa3072pkcs_encrypt_decrypt() { #[test_log::test] fn rsa3072pkcs_sign_verify() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa3072pkcs_private_key(Volatile)).key; let hash_prefix = hex!("3041 300d 0609 608648016503040202 0500 0430"); let message = [1u8, 2u8, 3u8]; @@ -127,7 +127,7 @@ fn rsa3072pkcs_sign_verify() { #[test_log::test] fn rsa3072pkcs_inject() { - client::get(|client| { + virt::with_ram_client(|client| { let n = hex!("c2094ddae7a5de41dbe4b38ad72169027167482983e07b10efb3f549c5d85a1c2d68aba2e2178fc549a51bd18ea2f995bde529f5c6ccfcbef442e8fe6d113fc09a00ccffda8e78e4ecde7aaa30f83fad1d3aa1f923df1ce9fd06f2bb0f73dca779b0645220c5e6da1f4730392af7ea62520c9a80bc49e11c5a53fd6db0be1ba4b6c78fbc6da5d5b86b58b9f512dd580e60cd10a5995c71cabdab4b9afa718bf112da2ed4ab9d419387148116a9cc94b62bafab962f961a3efcd8584b661a1cb5f9b5ddbe2e96f1e7fd1cb3a8ae15d3685eae33c34451d55d8e48a423e910ebde619cba0f6c000c7d85e5d1f9cb03da22764fa67b426f11ba5965491c9ab2697ac050ea1bb6b011a441e707bb2c3380502dc617e3ec3b226350a49c66fa2a6a6e2818fccc9e6c46097d7f658b47665f958a7870fd0ec595b9fe73156d977936b3c952ee24cb71574c68f995260cf59cb1cc5548d512a471708a6aedd32704b8a40ab7fed7de64dca930678e9dae0e02076ab5ff1993547e68b1dfd8e9adf9d9b7"); let e = hex!("010001"); let d = hex!("46af20ff779782e9b6f30f3caab5ef0d06c6bb10f48b98094968e31826cc73b7040bb74ab4d6247798265f85ed520d5db1398419967c222e65c8e21b9d1bc57fa21a5c936fc8aaddaa3439b739f3952eb9111ce4275f25a74f9772611675fc91bdd0b61afcf95ae966af862fad2976e6ae410d1f8c77d55b80c44bf3e388bd85395865521d17664db23d3630c2d8833569aa0a406927b6044727d978f176dfa5c85a56e8fe43611a1f01272d5c59bc6ac86b4c347fcd4c6e59a96e30b95715d1c920454006d5dfbf1b1f4c0305dc8a862e131185478b813dfdba873b926f70714c38023698d4b0e933f36c37e32a9dd98d852bf3395dbebb27e2c2ddb378ef56d32614def94e2a44ba3f3ae2d199d1cd57928dec1a3ac1a7ebfd8603ee0948ebc1372e91a8f0ef5e61e069c13d07ba5dc9d6fac172f316812a730b9f80a6ada8508a36c3278ba713809fbfe336369e60983e848ca10607eca3dafa2ecb1cd8d1bfde49830d489f32135e95cb396b589a37fbf81cd4752a2ffd621d70e8a3d0b1"); diff --git a/tests/rsa4096.rs b/tests/rsa4096.rs index 5f7de1e..f746a9d 100644 --- a/tests/rsa4096.rs +++ b/tests/rsa4096.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "virt")] + use rsa::sha2::Sha512; use rsa::{Pkcs1v15Encrypt, Pkcs1v15Sign, PublicKeyParts}; use trussed::client::CryptoClient; @@ -16,13 +18,11 @@ use hex_literal::hex; use num_bigint_dig::BigUint; use rsa::{PublicKey, RsaPrivateKey}; -mod client; - // Tests below can be run on a PC using the "virt" feature #[test_log::test] fn rsa4096pkcs_generate_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; // This assumes we don't ever get a key with ID 0 @@ -32,7 +32,7 @@ fn rsa4096pkcs_generate_key() { #[test_log::test] fn rsa4096pkcs_derive_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa4096pkcs_public_key(sk, Volatile)).key; @@ -43,7 +43,7 @@ fn rsa4096pkcs_derive_key() { #[test_log::test] fn rsa4096pkcs_exists_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; let key_exists = syscall!(client.exists(trussed::types::Mechanism::Rsa4096Pkcs1v15, sk)).exists; @@ -54,7 +54,7 @@ fn rsa4096pkcs_exists_key() { #[test_log::test] fn rsa4096pkcs_serialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa4096pkcs_public_key(sk, Volatile)).key; @@ -66,7 +66,7 @@ fn rsa4096pkcs_serialize_key() { #[test_log::test] fn rsa4096_deserialize_key() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Internal)).key; let pk = syscall!(client.derive_rsa4096pkcs_public_key(sk, Volatile)).key; let serialized_key = syscall!(client.serialize_rsa4096_key(pk)).serialized_key; @@ -83,7 +83,7 @@ fn rsa4096_deserialize_key() { #[test_log::test] fn rsa4096pkcs_encrypt_decrypt() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Volatile)).key; let message = [1u8, 2u8, 3u8]; let pk = syscall!(client.derive_rsa4096pkcs_public_key(sk, Volatile)).key; @@ -108,7 +108,7 @@ fn rsa4096pkcs_encrypt_decrypt() { #[test_log::test] fn rsa4096pkcs_sign_verify() { - client::get(|client| { + virt::with_ram_client(|client| { let sk = syscall!(client.generate_rsa4096pkcs_private_key(Volatile)).key; let hash_prefix = hex!("3051 300d 0609 608648016503040203 0500 0440"); let message = [1u8, 2u8, 3u8]; @@ -127,7 +127,7 @@ fn rsa4096pkcs_sign_verify() { #[test_log::test] fn rsa4096pkcs_inject() { - client::get(|client| { + virt::with_ram_client(|client| { let n = hex!("adc511a420f48ffcb2c62f1a5113fe4e7f3efa20b0c6e711b93aca7e5ad662ba089ac27e9a1ae4cda8032d0b1ec8e3f719f6d3a04e1572a35caec922b439d53324b020d07e3ff70de2fb53409d26af1034b8f8fbf3776b1decd515af1298d98a671e3b2fd871f1b38c8917ed15c8732f96f75166df782d190913653a21da5d0647abc63c5b157910c455a4ac10d8ca3123ccdb98baf947e3a9ab9bff34fa9b0a19ab61f82c560ca46bad10391e49ef2bf5f01f198db12225338d68b0b18eaab962686e6a445b943952981fdf4c6f8135b6e86b1658d930df7fc2288c178401784a90977d1cf4444b401dabe2ebc6448979f74930aab7933a2fadc4c7b96c2fc65e85b2c49f0fa48381928ba64466b7c13303f8fa4b4afd5ad98e9d0990211a2b80caf6b071301e63d9f46b5d52016ec239e983efc1f6a0cd39a1fb9ae91483cc63f1467d13550802aab45ee03223ab1c13bdd82d79c4575f1bc4179ee9d7183534dd27b0a240497b951338fd29f863ff769140a4251219e61ef1ec8274d48431b58f7201c9069499488ac6c9e13e1ed790df14987760ef568a9fae55fd14e4e85232a345e265fb0ace01a0dc10a5390f67c9dba1de7d90b5b542eaaa370d935e153c97e82fe5108d9065991ad65181b2a2136d1474f91b3b85167cd3c04bdc6d144c4034b96b6be93b33c6d14bb511ffc04659cf807cbfd5039f68fb2ebe8ae9"); let e = hex!("010001"); let d = hex!("974447d9127f12a0ad976c0582b2dedbc255363422eee2d340e576c48b9ab892ad4edb248e4dff032fd0a3f35c37108b5864cf506ae8acc49cb7e28b7d4c22d5c48835e8891e7197fb114125ac27b2996eebde82a52c3d68ed7388cec067a267a2e06431803fa061e662a91b4fad10e84a88bca9cabab8b7647927d37508bb95edea1045161d19288960ec5a84c7d32af7b92b28470b1d93876dc5fc61480e92ba49c09ce32b7d11dc51e91f6fc87895522057524d4ff7235f3f27f5387bb30e7225ea88433d5d489127b0071868b097ebc363052f0ed2469cd68da9760709a887705b0f249756");