diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 03a9dff942..3f7693cab8 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -722,7 +722,7 @@ dependencies = [ "num-traits 0.2.16", "serde 1.0.204", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -875,7 +875,7 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-primitives" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "base58", "frame-support", @@ -1101,9 +1101,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -3270,7 +3270,7 @@ name = "itc-rpc-server" version = "0.1.0" dependencies = [ "anyhow", - "env_logger 0.9.3", + "env_logger 0.10.0", "its-peer-fetch", "its-primitives", "its-storage", @@ -4615,7 +4615,10 @@ dependencies = [ "futures 0.3.8", "hex", "http 0.2.1", + "http 0.2.9", + "http_req 0.8.1 (git+https://github.com/integritee-network/http_req?branch=master)", "http_req 0.8.1 (git+https://github.com/integritee-network/http_req)", + "itc-rest-client", "itp-sgx-crypto", "itp-utils", "lazy_static", @@ -4626,11 +4629,13 @@ dependencies = [ "lru", "parity-scale-codec", "rand 0.8.5", + "serde 1.0.204", + "serde_json 1.0.103", "sgx_rand", "sgx_tstd", "sp-core", "thiserror 1.0.9", - "url 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.5.0 (git+https://github.com/domenukk/rust-url?branch=no_std)", ] [[package]] @@ -4797,9 +4802,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.147" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -4988,7 +4993,7 @@ dependencies = [ [[package]] name = "litentry-hex-utils" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "hex", ] @@ -5000,7 +5005,7 @@ version = "0.1.0" [[package]] name = "litentry-macros" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" [[package]] name = "litentry-primitives" @@ -5030,7 +5035,7 @@ dependencies = [ [[package]] name = "litentry-proc-macros" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "cargo_toml", "proc-macro2", @@ -5332,9 +5337,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -6016,7 +6021,7 @@ dependencies = [ [[package]] name = "pallet-parachain-staking" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "core-primitives", "frame-support", @@ -6083,7 +6088,7 @@ dependencies = [ [[package]] name = "pallet-teebag" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "base64 0.13.1", "chrono 0.4.38", @@ -6414,9 +6419,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -8968,7 +8973,7 @@ dependencies = [ "backtrace", "bytes 1.4.0", "libc", - "mio 0.8.8", + "mio 0.8.11", "num_cpus 1.16.0", "parking_lot 0.12.1", "pin-project-lite", @@ -10019,18 +10024,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -10047,9 +10052,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -10065,9 +10070,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -10083,15 +10088,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -10107,9 +10112,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -10125,9 +10130,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -10143,9 +10148,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -10161,9 +10166,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 7146d0862a..e589c28d07 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -506,11 +506,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b" dependencies = [ - "libc", + "shlex", ] [[package]] @@ -600,7 +600,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-primitives" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "base58", "frame-support", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -3138,6 +3138,7 @@ dependencies = [ "hex", "http", "http_req", + "itc-rest-client", "itp-sgx-crypto", "itp-utils", "lazy_static", @@ -3147,10 +3148,13 @@ dependencies = [ "log", "lru", "parity-scale-codec", + "serde 1.0.204", + "serde_json 1.0.103", "sgx_rand", "sgx_tstd", "sp-core", "thiserror", + "url 2.5.0", ] [[package]] @@ -3358,7 +3362,7 @@ dependencies = [ [[package]] name = "litentry-hex-utils" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "hex", ] @@ -3370,7 +3374,7 @@ version = "0.1.0" [[package]] name = "litentry-macros" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" [[package]] name = "litentry-primitives" @@ -3408,7 +3412,7 @@ dependencies = [ [[package]] name = "litentry-proc-macros" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "cargo_toml", "proc-macro2", @@ -3801,7 +3805,7 @@ dependencies = [ [[package]] name = "pallet-parachain-staking" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "core-primitives", "frame-support", @@ -3868,7 +3872,7 @@ dependencies = [ [[package]] name = "pallet-teebag" version = "0.1.0" -source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#d2249ff0f8a4b20e590c5cd2c0f68baa79e13aa4" +source = "git+https://github.com/litentry/litentry-parachain?branch=release-v0.9.19#cdbe9b02c1c58ca3d0063bf2eaf26a1f9da314e9" dependencies = [ "base64 0.13.1", "chrono 0.4.31", @@ -4995,6 +4999,12 @@ dependencies = [ "keccak", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.1.0" diff --git a/tee-worker/enclave-runtime/src/rpc/common_api.rs b/tee-worker/enclave-runtime/src/rpc/common_api.rs index 13860c1591..cf0d63514b 100644 --- a/tee-worker/enclave-runtime/src/rpc/common_api.rs +++ b/tee-worker/enclave-runtime/src/rpc/common_api.rs @@ -29,7 +29,7 @@ use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use its_rpc_handler::direct_top_pool_api::add_top_pool_direct_rpc_methods; use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; use lc_data_providers::DataProviderConfig; -use lc_identity_verification::web2::twitter; +use lc_identity_verification::web2::{email, twitter}; use litentry_macros::{if_development, if_development_or}; use litentry_primitives::{aes_decrypt, AesRequest, DecryptableRequest, Identity}; use log::debug; @@ -418,6 +418,8 @@ pub fn add_common_api Ok(json!(compute_hex_encoded_return_error("Could not parse params"))), } }); + + io_handler.add_sync_method("identity_requestEmailVerification", move |params: Params| { + match params.parse::<(String, String)>() { + Ok((encoded_did, email)) => { + let account_id = match Identity::from_did(encoded_did.as_str()) { + Ok(identity) => + if let Some(account_id) = identity.to_account_id() { + account_id + } else { + return Ok(json!(compute_hex_encoded_return_error("Invalid identity"))) + }, + Err(_) => + return Ok(json!(compute_hex_encoded_return_error( + "Could not parse identity" + ))), + }; + let mut mailer = email::sendgrid_mailer::SendGridMailer::new( + data_provider_config.sendgrid_api_key.clone(), + data_provider_config.sendgrid_from_email.clone(), + ); + let verification_code = email::generate_verification_code(); + + match email::VerificationCodeStore::insert(account_id, verification_code.clone()) { + Ok(_) => { + if email::send_verification_email(&mut mailer, email, verification_code) + .is_err() + { + return Ok(json!(compute_hex_encoded_return_error( + "Could not send verification email" + ))) + } + let json_value = + RpcReturnValue::new(vec![], false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }, + Err(_) => Ok(json!(compute_hex_encoded_return_error( + "Could not save verification code" + ))), + } + }, + Err(_) => Ok(json!(compute_hex_encoded_return_error("Could not parse params"))), + } + }); } #[deprecated(note = "`state_executeAesGetter` should be preferred")] diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 35150f6e82..9a3a280162 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -213,6 +213,8 @@ pub struct DataProviderConfig { pub blockchain_info_api_retry_delay: u64, pub blockchain_info_api_retry_times: u16, pub blockchain_info_api_url: String, + pub sendgrid_api_key: String, + pub sendgrid_from_email: String, } impl DataProviderConfig { @@ -268,6 +270,8 @@ impl DataProviderConfig { blockchain_info_api_retry_delay: 5000, blockchain_info_api_retry_times: 2, blockchain_info_api_url: "https://blockchain.info/".to_string(), + sendgrid_api_key: "".to_string(), + sendgrid_from_email: "".to_string(), }; // we allow to override following config properties for non prod dev @@ -413,6 +417,13 @@ impl DataProviderConfig { if let Ok(v) = env::var("MAGIC_CRAFT_API_KEY") { config.set_magic_craft_api_key(v); } + if let Ok(v) = env::var("SENDGRID_API_KEY") { + config.set_sendgrid_api_key(v); + } + if let Ok(v) = env::var("SENDGRID_FROM_EMAIL") { + config.set_sendgrid_from_email(v); + } + Ok(config) } pub fn set_twitter_official_url(&mut self, v: String) -> Result<(), Error> { @@ -631,6 +642,14 @@ impl DataProviderConfig { self.blockchain_info_api_url = v; Ok(()) } + pub fn set_sendgrid_api_key(&mut self, v: String) { + debug!("set_sendgrid_api_key: {:?}", v); + self.sendgrid_api_key = v; + } + pub fn set_sendgrid_from_email(&mut self, v: String) { + debug!("set_sendgrid_from_email: {:?}", v); + self.sendgrid_from_email = v; + } } fn check_url(v: &String) -> Result<(), Error> { diff --git a/tee-worker/litentry/core/evm-dynamic-assertions/src/lib.rs b/tee-worker/litentry/core/evm-dynamic-assertions/src/lib.rs index 9a84a4dcec..1499ad1e88 100644 --- a/tee-worker/litentry/core/evm-dynamic-assertions/src/lib.rs +++ b/tee-worker/litentry/core/evm-dynamic-assertions/src/lib.rs @@ -203,6 +203,7 @@ pub fn identity_with_networks_to_token(identity: &IdentityNetworkTuple) -> Token Identity::Evm(addr) => (4, addr.as_ref().to_vec()), Identity::Bitcoin(addr) => (5, addr.as_ref().to_vec()), Identity::Solana(addr) => (6, addr.as_ref().to_vec()), + Identity::Email(str) => (7, str.inner_ref().to_vec()), }; let networks: Vec = identity.1.iter().map(network_to_token).collect(); Token::Tuple(vec![Token::Uint(type_index.into()), Token::Bytes(value), Token::Array(networks)]) diff --git a/tee-worker/litentry/core/identity-verification/Cargo.toml b/tee-worker/litentry/core/identity-verification/Cargo.toml index ea71cc5514..aa4e117de4 100644 --- a/tee-worker/litentry/core/identity-verification/Cargo.toml +++ b/tee-worker/litentry/core/identity-verification/Cargo.toml @@ -8,6 +8,8 @@ version = "0.1.0" # std dependencies base64 = { version = "0.22", default-features = false, features = ["alloc"] } hex = { version = "0.4.3", default-features = false } +http = { version = "0.2", optional = true } +http_req = { optional = true, features = ["rust-tls"], branch = "master", git = "https://github.com/integritee-network/http_req" } lru = "0.12.3" rand = { version = "0.8", optional = true } @@ -22,8 +24,12 @@ thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linu # no_std dependencies lazy_static = { version = "1.1.0", features = ["spin_no_std"] } log = { version = "0.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +url = { git = "https://github.com/domenukk/rust-url", branch = "no_std", default-features = false, features = ["alloc", "no_std_net"] } # internal dependencies +itc-rest-client = { path = "../../../core/rest-client", default-features = false } itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-features = false } itp-utils = { path = "../../../core-primitives/utils", default-features = false } @@ -35,9 +41,6 @@ lc-data-providers = { path = "../data-providers", default-features = false } lc-stf-task-sender = { path = "../stf-task/sender", default-features = false } litentry-primitives = { path = "../../primitives", default-features = false } -[dev-dependencies] -url = "2.5.0" - [features] default = ["std"] sgx = [ @@ -53,7 +56,12 @@ sgx = [ "lc-stf-task-sender/sgx", ] std = [ + "http", + "http_req", "log/std", + "serde/std", + "serde_json/std", + "url/std", "itp-sgx-crypto/std", "frame-support/std", "lc-data-providers/std", diff --git a/tee-worker/litentry/core/identity-verification/src/lib.rs b/tee-worker/litentry/core/identity-verification/src/lib.rs index f85d43f32b..300b612dc8 100644 --- a/tee-worker/litentry/core/identity-verification/src/lib.rs +++ b/tee-worker/litentry/core/identity-verification/src/lib.rs @@ -16,6 +16,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub extern crate alloc; + #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; diff --git a/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/mod.rs new file mode 100644 index 0000000000..d0733ded01 --- /dev/null +++ b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/mod.rs @@ -0,0 +1,14 @@ +pub mod sendgrid_mailer; +pub(crate) mod template; + +use crate::alloc::string::String; + +pub struct Mail { + pub to: String, + pub subject: String, + pub body: String, +} + +pub trait Mailer { + fn send(&mut self, mail: Mail) -> Result<(), String>; +} diff --git a/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/sendgrid_mailer.rs b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/sendgrid_mailer.rs new file mode 100644 index 0000000000..7d86cf49d8 --- /dev/null +++ b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/sendgrid_mailer.rs @@ -0,0 +1,97 @@ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use super::{Mail, Mailer}; +use crate::alloc::{string::String, vec, vec::Vec}; +use http::header::{AUTHORIZATION, CONTENT_TYPE}; +use http_req::response::Headers; +use itc_rest_client::{ + error::Error as HttpError, + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestPath, RestPost, +}; +use serde::Serialize; +use url::Url; + +#[derive(Serialize)] +struct Personalization { + to: Vec, +} + +#[derive(Serialize, Clone)] +pub(crate) struct Email { + pub email: String, + pub name: Option, +} + +#[derive(Serialize)] +pub(crate) struct Content { + content_type: String, + value: String, +} + +#[derive(Serialize)] +pub(crate) struct SendGridMail { + personalizations: Vec, + from: Email, + subject: String, + content: Vec, +} + +impl SendGridMail { + pub fn new(from_email: String, mail: Mail) -> Self { + let content = vec![Content { content_type: String::from("text/html"), value: mail.body }]; + let to = Email { email: mail.to, name: None }; + let from = Email { email: from_email, name: Some(String::from("Litentry")) }; + Self { + personalizations: vec![Personalization { to: vec![to] }], + from, + subject: mail.subject, + content, + } + } +} + +impl RestPath for SendGridMail { + fn get_path(path: String) -> Result { + Ok(path) + } +} + +pub struct SendGridMailer { + client: RestClient>, + from: String, +} + +impl SendGridMailer { + pub fn new(api_key: String, from_email: String) -> Self { + let base_url = Url::parse("https://api.sendgrid.com/v3/mail/send").unwrap(); + let authorization = std::format!("Bearer {}", api_key); + + let mut headers = Headers::new(); + headers.insert(AUTHORIZATION.as_str(), &authorization); + headers.insert(CONTENT_TYPE.as_str(), "application/json"); + + let http_client = HttpClient::new( + SendWithCertificateVerification::new(vec![]), + true, + None, + Some(headers), + None, + ); + + Self { client: RestClient::new(http_client, base_url), from: from_email } + } +} + +impl Mailer for SendGridMailer { + fn send(&mut self, mail: Mail) -> Result<(), String> { + let sendgrid_mail = SendGridMail::new(self.from.clone(), mail); + self.client + .post(String::default(), &sendgrid_mail) + .map_err(|e| std::format!("Failed to send verification email: {:?}", e))?; + + Ok(()) + } +} diff --git a/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/template.rs b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/template.rs new file mode 100644 index 0000000000..b30cf260a2 --- /dev/null +++ b/tee-worker/litentry/core/identity-verification/src/web2/email/mailer/template.rs @@ -0,0 +1,77 @@ +pub const EMAIL_VERIFICATION_TEMPLATE: &str = r#" + + + + + + + + + + + Email verification + + + +
+ Please use this verification code to link your email identity. +  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏  ͏ +
+
+
+ + + + +
+ + + + +
+

+ Email Verification +

+

+ Please use this verification code to link your email identity. +

+
+
+

{{ verification_code }}

+
+
+
+
+
+ + +"#; diff --git a/tee-worker/litentry/core/identity-verification/src/web2/email/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/email/mod.rs new file mode 100644 index 0000000000..51c58c1e5a --- /dev/null +++ b/tee-worker/litentry/core/identity-verification/src/web2/email/mod.rs @@ -0,0 +1,26 @@ +mod verification_code_store; +pub use verification_code_store::*; + +mod mailer; +pub use mailer::*; + +use crate::{alloc::string::String, web2::helpers}; + +pub fn generate_verification_code() -> String { + helpers::get_random_string(32) +} + +pub fn send_verification_email( + mailer: &mut impl Mailer, + to_email: String, + verification_code: String, +) -> Result<(), String> { + let mail = mailer::Mail { + to: to_email, + subject: String::from("Verify your email address"), + body: template::EMAIL_VERIFICATION_TEMPLATE + .replace("{{ verification_code }}", &verification_code), + }; + + mailer.send(mail) +} diff --git a/tee-worker/litentry/core/identity-verification/src/web2/email/verification_code_store.rs b/tee-worker/litentry/core/identity-verification/src/web2/email/verification_code_store.rs new file mode 100644 index 0000000000..44a86f13e8 --- /dev/null +++ b/tee-worker/litentry/core/identity-verification/src/web2/email/verification_code_store.rs @@ -0,0 +1,64 @@ +use crate::alloc::{fmt, format, string::String}; +use codec::Encode; +use core::result::Result; +use lazy_static::lazy_static; +use litentry_primitives::{ + ErrorDetail, ErrorString, IntoErrorDetail, ParentchainAccountId as AccountId, +}; +use lru::LruCache; +use std::num::NonZeroUsize; +#[cfg(feature = "std")] +use std::sync::RwLock; +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[derive(Debug)] +pub enum VerificationCodeStoreError { + LockPoisoning, + Other(String), +} + +impl fmt::Display for VerificationCodeStoreError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VerificationCodeStoreError::LockPoisoning => write!(f, "Lock poisoning"), + VerificationCodeStoreError::Other(msg) => write!(f, "{}", msg), + } + } +} + +impl std::error::Error for VerificationCodeStoreError {} + +impl IntoErrorDetail for VerificationCodeStoreError { + fn into_error_detail(self) -> ErrorDetail { + ErrorDetail::StfError(ErrorString::truncate_from(format!("{}", self).into())) + } +} + +lazy_static! { + static ref STORE: RwLock> = + RwLock::new(LruCache::new(NonZeroUsize::new(250).unwrap())); +} + +pub struct VerificationCodeStore; + +impl VerificationCodeStore { + pub fn insert( + account_id: AccountId, + verification_code: String, + ) -> Result<(), VerificationCodeStoreError> { + STORE + .write() + .map_err(|_| VerificationCodeStoreError::LockPoisoning)? + .put(hex::encode(account_id.encode()), verification_code); + Ok(()) + } + + pub fn get(account_id: &AccountId) -> Result, VerificationCodeStoreError> { + let code = STORE + .write() + .map_err(|_| VerificationCodeStoreError::LockPoisoning)? + .pop(hex::encode(account_id.encode()).as_str()); + Ok(code) + } +} diff --git a/tee-worker/litentry/core/identity-verification/src/web2/twitter/helpers.rs b/tee-worker/litentry/core/identity-verification/src/web2/helpers.rs similarity index 58% rename from tee-worker/litentry/core/identity-verification/src/web2/twitter/helpers.rs rename to tee-worker/litentry/core/identity-verification/src/web2/helpers.rs index 64cc5cd89a..8e09634cae 100644 --- a/tee-worker/litentry/core/identity-verification/src/web2/twitter/helpers.rs +++ b/tee-worker/litentry/core/identity-verification/src/web2/helpers.rs @@ -3,27 +3,10 @@ extern crate sgx_rand as rand; #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; +use crate::alloc::string::String; use rand::{thread_rng, Rng}; -use std::{string::String, vec::Vec}; -use crate::{Error, Result}; -use lc_data_providers::twitter_official::Tweet; -use litentry_primitives::{self, ErrorDetail}; - -pub(crate) fn get_code_verifier() -> String { - get_random_string(128) -} - -pub(crate) fn get_state_verifier() -> String { - get_random_string(32) -} - -pub(crate) fn payload_from_tweet(tweet: &Tweet) -> Result> { - hex::decode(tweet.text.strip_prefix("0x").unwrap_or(tweet.text.as_str())) - .map_err(|_| Error::LinkIdentityFailed(ErrorDetail::ParseError)) -} - -fn get_random_string(length: usize) -> String { +pub(crate) fn get_random_string(length: usize) -> String { let mut rng = thread_rng(); let charset: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let random_string: String = (0..length) diff --git a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs index 3b74f16506..c444f0612d 100644 --- a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs +++ b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs @@ -17,10 +17,22 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use http_req_sgx as http_req; + pub use http_sgx as http; +} + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + #[cfg(all(feature = "std", feature = "sgx"))] compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); mod discord; +pub mod email; +mod helpers; pub mod twitter; use crate::{ensure, Error, Result}; @@ -69,7 +81,7 @@ pub fn verify( .query_user_by_id(user_id.into_bytes()) .map_err(|e| Error::LinkIdentityFailed(e.into_error_detail()))?; - let payload = twitter::helpers::payload_from_tweet(&tweet)?; + let payload = twitter::payload_from_tweet(&tweet)?; ensure!( payload.as_slice() == raw_msg, Error::LinkIdentityFailed(ErrorDetail::UnexpectedMessage) @@ -200,6 +212,37 @@ pub fn verify( Ok(user.username) }, }, + Web2ValidationData::Email(data) => { + let email = vec_to_string(data.email.to_vec()) + .map_err(|e| Error::LinkIdentityFailed(e.into_error_detail()))?; + let verification_code = vec_to_string(data.verification_code.to_vec()) + .map_err(|e| Error::LinkIdentityFailed(e.into_error_detail()))?; + let Some(account_id) = who.to_account_id() else { + return Err(Error::LinkIdentityFailed(ErrorDetail::ParseError)); + }; + let stored_verification_code = match email::VerificationCodeStore::get(&account_id) { + Ok(data) => data.ok_or_else(|| { + Error::LinkIdentityFailed(ErrorDetail::StfError(ErrorString::truncate_from( + std::format!( + "no verification code found for {}", + account_id_to_string(&account_id) + ) + .as_bytes() + .to_vec(), + ))) + })?, + Err(e) => return Err(Error::LinkIdentityFailed(e.into_error_detail())), + }; + + ensure!( + verification_code == stored_verification_code, + Error::LinkIdentityFailed(ErrorDetail::StfError(ErrorString::truncate_from( + "verification code mismatch".as_bytes().to_vec() + ))) + ); + + Ok(email) + }, }?; // compare the username: @@ -219,6 +262,11 @@ pub fn verify( .map_err(|_| Error::LinkIdentityFailed(ErrorDetail::ParseError))?; ensure!(username.eq(handle), Error::LinkIdentityFailed(ErrorDetail::WrongWeb2Handle)); }, + Identity::Email(address) => { + let handle = std::str::from_utf8(address.inner_ref()) + .map_err(|_| Error::LinkIdentityFailed(ErrorDetail::ParseError))?; + ensure!(username.eq(handle), Error::LinkIdentityFailed(ErrorDetail::WrongWeb2Handle)); + }, _ => return Err(Error::LinkIdentityFailed(ErrorDetail::InvalidIdentity)), } diff --git a/tee-worker/litentry/core/identity-verification/src/web2/twitter/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/twitter/mod.rs index 7d1e09594d..df620133b3 100644 --- a/tee-worker/litentry/core/identity-verification/src/web2/twitter/mod.rs +++ b/tee-worker/litentry/core/identity-verification/src/web2/twitter/mod.rs @@ -1,11 +1,15 @@ mod oauth_store; pub use oauth_store::*; -pub(crate) mod helpers; - +use crate::{ + alloc::{format, string::String, vec::Vec}, + web2::helpers, + Error, Result, +}; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; +use lc_data_providers::twitter_official::Tweet; +use litentry_primitives::{self, ErrorDetail}; use sp_core::hashing::sha2_256; -use std::{format, string::String}; #[derive(Debug)] pub struct AuthorizeData { @@ -18,8 +22,8 @@ const BASE_URL: &str = "https://twitter.com/i/oauth2/authorize"; const SCOPES: &str = "tweet.read%20users.read"; pub fn get_authorize_data(client_id: &str, redirect_uri: &str) -> AuthorizeData { - let state = helpers::get_state_verifier(); - let code_verifier = helpers::get_code_verifier(); + let state = get_state_verifier(); + let code_verifier = get_code_verifier(); let code_verifier_hash = sha2_256(code_verifier.as_bytes()); let code_challenge = URL_SAFE_NO_PAD.encode(code_verifier_hash); @@ -36,6 +40,19 @@ pub fn get_authorize_data(client_id: &str, redirect_uri: &str) -> AuthorizeData AuthorizeData { authorize_url, code_verifier, state } } +pub fn payload_from_tweet(tweet: &Tweet) -> Result> { + hex::decode(tweet.text.strip_prefix("0x").unwrap_or(tweet.text.as_str())) + .map_err(|_| Error::LinkIdentityFailed(ErrorDetail::ParseError)) +} + +fn get_code_verifier() -> String { + helpers::get_random_string(128) +} + +fn get_state_verifier() -> String { + helpers::get_random_string(32) +} + #[cfg(test)] mod tests { use super::*; diff --git a/tee-worker/litentry/primitives/src/validation_data.rs b/tee-worker/litentry/primitives/src/validation_data.rs index 93601b05e6..51f92856ad 100644 --- a/tee-worker/litentry/primitives/src/validation_data.rs +++ b/tee-worker/litentry/primitives/src/validation_data.rs @@ -45,6 +45,13 @@ pub enum DiscordValidationData { }, } +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct EmailValidationData { + pub email: ValidationString, + pub verification_code: ValidationString, +} + #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Web3CommonValidationData { @@ -60,6 +67,8 @@ pub enum Web2ValidationData { Twitter(TwitterValidationData), #[codec(index = 1)] Discord(DiscordValidationData), + #[codec(index = 2)] + Email(EmailValidationData), } #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] diff --git a/tee-worker/service/src/prometheus_metrics.rs b/tee-worker/service/src/prometheus_metrics.rs index 45fc234a14..ff03df163b 100644 --- a/tee-worker/service/src/prometheus_metrics.rs +++ b/tee-worker/service/src/prometheus_metrics.rs @@ -288,6 +288,7 @@ fn handle_stf_call_request(req: RequestType, time: f64) { Identity::Twitter(_) => "Twitter".into(), Identity::Discord(_) => "Discord".into(), Identity::Github(_) => "Github".into(), + Identity::Email(_) => "Email".into(), Identity::Substrate(_) => "Substrate".into(), Identity::Evm(_) => "Evm".into(), Identity::Bitcoin(_) => "Bitcoin".into(),