From 03bac580c7445968140e82601200a61a60117d9b Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 16:45:23 +0000 Subject: [PATCH 01/14] creating authentication crate, with custom jwt implementation --- tee-worker/Cargo.lock | 14 ++ tee-worker/Cargo.toml | 1 + .../litentry/core/authentication/Cargo.toml | 28 +++ .../litentry/core/authentication/src/jwt.rs | 169 ++++++++++++++++++ .../litentry/core/authentication/src/lib.rs | 35 ++++ 5 files changed, 247 insertions(+) create mode 100644 tee-worker/identity/litentry/core/authentication/Cargo.toml create mode 100644 tee-worker/identity/litentry/core/authentication/src/jwt.rs create mode 100644 tee-worker/identity/litentry/core/authentication/src/lib.rs diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 9a1c92c74c..3b8ac3555f 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -3327,6 +3327,7 @@ dependencies = [ "itp-storage", "itp-types", "itp-utils", + "lc-authentication", "lc-stf-task-sender", "litentry-hex-utils", "litentry-macros", @@ -5004,6 +5005,19 @@ dependencies = [ "thiserror 1.0.9", ] +[[package]] +name = "lc-authentication" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "core-primitives", + "parity-scale-codec", + "ring 0.16.20", + "serde 1.0.210", + "serde_json 1.0.120", + "sgx_tstd", +] + [[package]] name = "lc-common" version = "0.1.0" diff --git a/tee-worker/Cargo.toml b/tee-worker/Cargo.toml index 963d8c1e09..eebf47ab45 100644 --- a/tee-worker/Cargo.toml +++ b/tee-worker/Cargo.toml @@ -53,6 +53,7 @@ members = [ "identity/litentry/core/data-providers", "identity/litentry/core/vc-task/sender", "identity/litentry/core/vc-task/receiver", + "identity/litentry/core/authentication", "identity/litentry/core/native-task/sender", "identity/litentry/core/native-task/receiver", "identity/litentry/core/identity-verification", diff --git a/tee-worker/identity/litentry/core/authentication/Cargo.toml b/tee-worker/identity/litentry/core/authentication/Cargo.toml new file mode 100644 index 0000000000..47891f028a --- /dev/null +++ b/tee-worker/identity/litentry/core/authentication/Cargo.toml @@ -0,0 +1,28 @@ +[package] +authors = ["Trust Computing GmbH "] +edition = "2021" +name = "lc-authentication" +version = "0.1.0" + +[dependencies] +codec = { package = "parity-scale-codec", workspace = true } +serde = { workspace = true } +ring = { workspace = true } +serde_json = { workspace = true } +base64 = { version = "0.22", default-features = false, features = ["alloc"] } # a newer base64 +parentchain-primitives = { workspace = true } + +sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "serde/std", + "serde_json/std", + "ring/std", + "parentchain-primitives/std", +] +sgx = [ + "sgx_tstd", +] diff --git a/tee-worker/identity/litentry/core/authentication/src/jwt.rs b/tee-worker/identity/litentry/core/authentication/src/jwt.rs new file mode 100644 index 0000000000..5f0a15cb4e --- /dev/null +++ b/tee-worker/identity/litentry/core/authentication/src/jwt.rs @@ -0,0 +1,169 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{AuthOptions, BlockNumber}; +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD}; +use ring::hmac; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidToken, + InvalidSignature, + ExpiredToken, + InvalidSubject, + Base64DecodeError, + JsonError, +} + +impl From for Error { + fn from(_: base64::DecodeError) -> Self { + Error::Base64DecodeError + } +} + +#[derive(Serialize, Deserialize)] +pub struct Header { + alg: String, + typ: String, +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct Payload { + sub: String, + #[serde(skip_serializing_if = "Option::is_none")] + exp: Option, +} + +impl Payload { + pub fn new(sub: String, options: AuthOptions) -> Self { + Self { sub, exp: options.expires_at } + } +} + +pub struct Validation { + pub sub: String, + pub current_block: BlockNumber, +} + +impl Validation { + pub fn new(sub: String, current_block: BlockNumber) -> Self { + Self { sub, current_block } + } + + pub fn validate(&self, payload: &Payload) -> Result<(), Error> { + if self.sub != payload.sub { + return Err(Error::InvalidSubject) + } + + if let Some(exp_block) = payload.exp { + if self.current_block > exp_block { + return Err(Error::ExpiredToken) + } + } + + Ok(()) + } +} + +fn base64_encode>(input: T) -> String { + BASE64_URL_SAFE_NO_PAD.encode(input) +} + +fn base64_decode>(input: T) -> Result, base64::DecodeError> { + BASE64_URL_SAFE_NO_PAD.decode(input) +} + +pub fn create(payload: &Payload, secret: &[u8]) -> Result { + let header = Header { alg: "HS256".to_string(), typ: "JWT".to_string() }; + let encoded_header = + base64_encode(&serde_json::to_string(&header).map_err(|_| Error::JsonError)?); + let encoded_payload = + base64_encode(&serde_json::to_string(&payload).map_err(|_| Error::JsonError)?); + let data = [encoded_header, encoded_payload].join("."); + let key = hmac::Key::new(hmac::HMAC_SHA256, secret); + let signature = hmac::sign(&key, data.as_bytes()); + let encoded_signature = base64_encode(signature); + + Ok([data, encoded_signature].join(".")) +} + +pub fn decode(jwt: &str, secret: &[u8], validation: Validation) -> Result { + let parts: Vec<&str> = jwt.split('.').collect(); + if parts.len() != 3 { + return Err(Error::InvalidToken) + } + + let data = [parts[0], parts[1]].join("."); + let key = hmac::Key::new(hmac::HMAC_SHA256, secret); + let decoded_signature = base64_decode(parts[2])?; + + hmac::verify(&key, data.as_bytes(), &decoded_signature).map_err(|_| Error::InvalidSignature)?; + + let payload = base64_decode(parts[1])?; + let payload: Payload = serde_json::from_slice(&payload).map_err(|_| Error::JsonError)?; + + validation.validate(&payload)?; + + Ok(payload) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_jwt() { + let secret = "secret".as_bytes(); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: Some(10) }); + let jwt = create(&payload, secret).unwrap(); + + let current_block = 5; + let decoded_payload = + decode(&jwt, secret, Validation::new("subject".to_string(), current_block)).unwrap(); + + assert_eq!(decoded_payload, payload); + } + + #[test] + fn test_jwt_exp_validation() { + let secret = "secret".as_bytes(); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: Some(10) }); + let jwt = create(&payload, secret).unwrap(); + + let current_block = 12; + let result = decode(&jwt, secret, Validation::new("subject".to_string(), current_block)); + + assert_eq!(result, Err(Error::ExpiredToken)); + } + + #[test] + fn test_jwt_sub_validation() { + let secret = "secret".as_bytes(); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: None }); + let jwt = create(&payload, secret).unwrap(); + + let current_block = 5; + let result = + decode(&jwt, secret, Validation::new("other-subject".to_string(), current_block)); + + assert_eq!(result, Err(Error::InvalidSubject)); + } +} diff --git a/tee-worker/identity/litentry/core/authentication/src/lib.rs b/tee-worker/identity/litentry/core/authentication/src/lib.rs new file mode 100644 index 0000000000..a2f4da59b9 --- /dev/null +++ b/tee-worker/identity/litentry/core/authentication/src/lib.rs @@ -0,0 +1,35 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +pub mod jwt; + +use codec::{Decode, Encode}; +use parentchain_primitives::BlockNumber; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct AuthOptions { + expires_at: Option, +} From 0eed3b51fab36697f481f992574fa4d74fd5b2fd Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 16:47:13 +0000 Subject: [PATCH 02/14] adding request_auth_token trusted call --- tee-worker/identity/app-libs/stf/Cargo.toml | 3 +++ .../identity/app-libs/stf/src/trusted_call.rs | 6 ++++++ tee-worker/identity/enclave-runtime/Cargo.lock | 14 ++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/tee-worker/identity/app-libs/stf/Cargo.toml b/tee-worker/identity/app-libs/stf/Cargo.toml index 02b7067a1b..8bb4f8308d 100644 --- a/tee-worker/identity/app-libs/stf/Cargo.toml +++ b/tee-worker/identity/app-libs/stf/Cargo.toml @@ -38,6 +38,7 @@ sp-std = { workspace = true } # litentry itp-node-api-metadata-provider = { workspace = true } lc-stf-task-sender = { path = "../../litentry/core/stf-task/sender", default-features = false } +lc-authentication = { path = "../../litentry/core/authentication", default-features = false } litentry-hex-utils = { workspace = true } litentry-macros = { workspace = true } litentry-primitives = { workspace = true } @@ -56,6 +57,7 @@ sgx = [ "itp-node-api/sgx", "litentry-primitives/sgx", "lc-stf-task-sender/sgx", + "lc-authentication/sgx", "itp-node-api-metadata-provider/sgx", ] std = [ @@ -79,6 +81,7 @@ std = [ "sp-io/std", "litentry-primitives/std", "lc-stf-task-sender/std", + "lc-authentication/std", "itp-node-api-metadata-provider/std", ] test = [] diff --git a/tee-worker/identity/app-libs/stf/src/trusted_call.rs b/tee-worker/identity/app-libs/stf/src/trusted_call.rs index 0ec7a6b566..9a84fec27b 100644 --- a/tee-worker/identity/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/identity/app-libs/stf/src/trusted_call.rs @@ -50,6 +50,7 @@ use itp_types::{ Moment, OpaqueCall, H256, }; use itp_utils::stringify::account_id_to_string; +use lc_authentication::AuthOptions; use litentry_hex_utils::hex_encode; pub use litentry_primitives::{ aes_encrypt_default, all_evm_web3networks, all_substrate_web3networks, AesOutput, Assertion, @@ -149,6 +150,8 @@ pub enum TrustedCall { remove_accounts(Identity, Vec), #[codec(index = 30)] publicize_account(Identity, Identity), + #[codec(index = 31)] + request_auth_token(Identity, AuthOptions), // original integritee trusted calls, starting from index 50 #[codec(index = 50)] @@ -244,6 +247,7 @@ impl TrustedCall { Self::add_account(sender_identity, ..) => sender_identity, Self::remove_accounts(sender_identity, ..) => sender_identity, Self::publicize_account(sender_identity, ..) => sender_identity, + Self::request_auth_token(sender_identity, ..) => sender_identity, } } @@ -262,6 +266,7 @@ impl TrustedCall { Self::add_account(..) => "add_account", Self::remove_accounts(..) => "remove_account", Self::publicize_account(..) => "publicize_account", + Self::request_auth_token(..) => "request_auth_token", _ => "unsupported_trusted_call", } } @@ -920,6 +925,7 @@ where | TrustedCall::create_account_store(..) | TrustedCall::add_account(..) | TrustedCall::remove_accounts(..) + | TrustedCall::request_auth_token(..) | TrustedCall::publicize_account(..) => { error!("please use author_submitNativeRequest instead"); Ok(TrustedCallResult::Empty) diff --git a/tee-worker/identity/enclave-runtime/Cargo.lock b/tee-worker/identity/enclave-runtime/Cargo.lock index f26d7f9f42..487e12ab36 100644 --- a/tee-worker/identity/enclave-runtime/Cargo.lock +++ b/tee-worker/identity/enclave-runtime/Cargo.lock @@ -1864,6 +1864,7 @@ dependencies = [ "itp-storage", "itp-types", "itp-utils", + "lc-authentication", "lc-stf-task-sender", "litentry-hex-utils", "litentry-macros", @@ -2986,6 +2987,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "lc-authentication" +version = "0.1.0" +dependencies = [ + "base64 0.22.1", + "core-primitives", + "parity-scale-codec", + "ring 0.16.20", + "serde 1.0.204", + "serde_json 1.0.120", + "sgx_tstd", +] + [[package]] name = "lc-common" version = "0.1.0" From 95c281b1a06cc70d4588abbbfa207c25cf8fd234 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 16:51:13 +0000 Subject: [PATCH 03/14] making exp option mandatory --- .../litentry/core/authentication/src/jwt.rs | 15 ++++++--------- .../litentry/core/authentication/src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tee-worker/identity/litentry/core/authentication/src/jwt.rs b/tee-worker/identity/litentry/core/authentication/src/jwt.rs index 5f0a15cb4e..795ea4a888 100644 --- a/tee-worker/identity/litentry/core/authentication/src/jwt.rs +++ b/tee-worker/identity/litentry/core/authentication/src/jwt.rs @@ -48,8 +48,7 @@ pub struct Header { #[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct Payload { sub: String, - #[serde(skip_serializing_if = "Option::is_none")] - exp: Option, + exp: BlockNumber, } impl Payload { @@ -73,10 +72,8 @@ impl Validation { return Err(Error::InvalidSubject) } - if let Some(exp_block) = payload.exp { - if self.current_block > exp_block { - return Err(Error::ExpiredToken) - } + if self.current_block > payload.exp { + return Err(Error::ExpiredToken) } Ok(()) @@ -132,7 +129,7 @@ mod tests { #[test] fn test_jwt() { let secret = "secret".as_bytes(); - let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: Some(10) }); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: 10 }); let jwt = create(&payload, secret).unwrap(); let current_block = 5; @@ -145,7 +142,7 @@ mod tests { #[test] fn test_jwt_exp_validation() { let secret = "secret".as_bytes(); - let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: Some(10) }); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: 10 }); let jwt = create(&payload, secret).unwrap(); let current_block = 12; @@ -157,7 +154,7 @@ mod tests { #[test] fn test_jwt_sub_validation() { let secret = "secret".as_bytes(); - let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: None }); + let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: 10 }); let jwt = create(&payload, secret).unwrap(); let current_block = 5; diff --git a/tee-worker/identity/litentry/core/authentication/src/lib.rs b/tee-worker/identity/litentry/core/authentication/src/lib.rs index a2f4da59b9..7bc7f8b4ad 100644 --- a/tee-worker/identity/litentry/core/authentication/src/lib.rs +++ b/tee-worker/identity/litentry/core/authentication/src/lib.rs @@ -31,5 +31,5 @@ use parentchain_primitives::BlockNumber; #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] pub struct AuthOptions { - expires_at: Option, + expires_at: BlockNumber, } From 58b73b2051d2f13b56725aae22acda6b79e505b2 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 17:17:23 +0000 Subject: [PATCH 04/14] adding new secret for jwt key --- local-setup/.env.dev | 4 ++++ .../identity/litentry/core/data-providers/src/lib.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/local-setup/.env.dev b/local-setup/.env.dev index 641630178a..896b6cf4dd 100644 --- a/local-setup/.env.dev +++ b/local-setup/.env.dev @@ -35,6 +35,10 @@ GENIIDATA_API_KEY= MORALIS_API_KEY= MAGIC_CRAFT_API_KEY= +# The following key/token are MANDATORY to give when running worker. +# use a secure 256-bit key for JWT token generation. +JWT_SECRET= + # The followings are default value. # Can be skipped; or overwrite within non-production mode. TWITTER_OFFICIAL_URL=http://localhost:19527 diff --git a/tee-worker/identity/litentry/core/data-providers/src/lib.rs b/tee-worker/identity/litentry/core/data-providers/src/lib.rs index cba11218fe..37a26bc24c 100644 --- a/tee-worker/identity/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/identity/litentry/core/data-providers/src/lib.rs @@ -215,6 +215,7 @@ pub struct DataProviderConfig { pub blockchain_info_api_url: String, pub sendgrid_api_key: String, pub sendgrid_from_email: String, + pub jwt_secret: String, } impl DataProviderConfig { @@ -272,6 +273,7 @@ impl DataProviderConfig { blockchain_info_api_url: "https://blockchain.info/".to_string(), sendgrid_api_key: "".to_string(), sendgrid_from_email: "".to_string(), + jwt_secret: "".to_string(), }; // we allow to override following config properties for non prod dev @@ -423,6 +425,9 @@ impl DataProviderConfig { if let Ok(v) = env::var("SENDGRID_FROM_EMAIL") { config.set_sendgrid_from_email(v); } + if let Ok(v) = env::var("JWT_SECRET") { + config.set_jwt_secret(v); + } Ok(config) } @@ -650,6 +655,9 @@ impl DataProviderConfig { debug!("set_sendgrid_from_email: {:?}", v); self.sendgrid_from_email = v; } + pub fn set_jwt_secret(&mut self, v: String) { + self.jwt_secret = v; + } } fn check_url(v: &String) -> Result<(), Error> { From 79f26f16057605dceddb226aa1a02b7d29585eeb Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 17:36:27 +0000 Subject: [PATCH 05/14] adding request_auth_token trusted call handler (WIP) --- tee-worker/Cargo.lock | 2 ++ tee-worker/Cargo.toml | 1 + .../identity/enclave-runtime/Cargo.lock | 2 ++ .../core/native-task/receiver/Cargo.toml | 3 ++- .../core/native-task/receiver/src/lib.rs | 24 +++++++++++++++++++ .../core/native-task/receiver/src/types.rs | 1 + 6 files changed, 32 insertions(+), 1 deletion(-) diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 3b8ac3555f..d797116b9e 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -5223,6 +5223,8 @@ dependencies = [ "itp-stf-primitives", "itp-stf-state-handler", "itp-types", + "itp-utils", + "lc-authentication", "lc-data-providers", "lc-dynamic-assertion", "lc-identity-verification", diff --git a/tee-worker/Cargo.toml b/tee-worker/Cargo.toml index eebf47ab45..a58369f5b6 100644 --- a/tee-worker/Cargo.toml +++ b/tee-worker/Cargo.toml @@ -305,6 +305,7 @@ lc-vc-task-receiver = { path = "identity/litentry/core/vc-task/receiver", defaul lc-omni-account = { path = "identity/litentry/core/omni-account", default-features = false } lc-native-task-sender = { path = "identity/litentry/core/native-task/sender", default-features = false } lc-native-task-receiver = { path = "identity/litentry/core/native-task/receiver", default-features = false } +lc-authentication = { path = "identity/litentry/core/authentication", default-features = false } itc-peer-top-broadcaster = { path = "identity/core/peer-top-broadcaster", default-features = false } itc-rpc-server = { path = "identity/core/rpc-server", default-features = false } diff --git a/tee-worker/identity/enclave-runtime/Cargo.lock b/tee-worker/identity/enclave-runtime/Cargo.lock index 487e12ab36..4b42efd211 100644 --- a/tee-worker/identity/enclave-runtime/Cargo.lock +++ b/tee-worker/identity/enclave-runtime/Cargo.lock @@ -3148,6 +3148,8 @@ dependencies = [ "itp-stf-primitives", "itp-stf-state-handler", "itp-types", + "itp-utils", + "lc-authentication", "lc-data-providers", "lc-dynamic-assertion", "lc-identity-verification", diff --git a/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml b/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml index ad97302e1d..d0ccba14c0 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml +++ b/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml @@ -26,6 +26,7 @@ itp-stf-primitives = { workspace = true } itp-stf-state-handler = { workspace = true } itp-top-pool-author = { package = "id-itp-top-pool-author", path = "../../../../core-primitives/top-pool-author", default-features = false } itp-types = { workspace = true } +itp-utils = { workspace = true } frame-support = { workspace = true } lc-data-providers = { workspace = true } @@ -33,6 +34,7 @@ lc-dynamic-assertion = { workspace = true } lc-identity-verification = { workspace = true } lc-native-task-sender = { workspace = true } lc-omni-account = { workspace = true } +lc-authentication = { workspace = true } litentry-hex-utils = { workspace = true } litentry-macros = { workspace = true } litentry-primitives = { workspace = true } @@ -44,7 +46,6 @@ sgx = [ "sgx_tstd", "ita-stf/sgx", "itp-top-pool-author/sgx", - "sp-core/full_crypto", "litentry-primitives/sgx", "itp-node-api/sgx", "itp-extrinsics-factory/sgx", diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs index aa2a507fcc..b71db6d340 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs @@ -64,6 +64,8 @@ use itp_types::{ parentchain::{Address, ParachainHeader, ParentchainId}, AccountId, OpaqueCall, }; +use itp_utils::stringify::account_id_to_string_without_prefix; +use lc_authentication::{jwt, AuthOptions}; use lc_identity_verification::web2::verify as verify_web2_identity; use lc_native_task_sender::init_native_task_sender; use lc_omni_account::{ @@ -414,6 +416,28 @@ fn handle_trusted_call< identity )) )), + TrustedCall::request_auth_token(who, auth_options) => { + let omni_account = match get_omni_account(context.ocall_api.clone(), &who) { + Ok(account) => account, + Err(e) => { + log::error!("Failed to get omni account: {:?}", e); + let result: TrustedCallResult = Err(NativeTaskError::UnauthorizedSigner); + context.author_api.send_rpc_response(connection_hash, result.encode(), false); + return + }, + }; + let subject = account_id_to_string_without_prefix(&omni_account); + let payload = jwt::Payload::new(subject, auth_options); + let Ok(auth_token) = jwt::create(&payload, context.data_provider_config.jwt_secret.as_bytes()) else { + log::error!("Failed to create jwt token"); + let result: TrustedCallResult = Err(NativeTaskError::AuthTokenCreationFailed); + context.author_api.send_rpc_response(connection_hash, result.encode(), false); + return + }; + + // Return the auth token with a new variant once #3183 is merged + unimplemented!() + }, _ => { log::warn!("Received unsupported call: {:?}", call); let result: TrustedCallResult = diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs index b7f36d9926..3c618d10ff 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs @@ -136,4 +136,5 @@ pub enum NativeTaskError { ExtrinsicSendingFailed(String), // Stringified sgx_status_t InvalidRequest, NativeRequestSendFailed, + AuthTokenCreationFailed, } From 9316e64f97b4511624dad08de0ffa95646962271 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:03:54 +0000 Subject: [PATCH 06/14] refactoring jwt module --- .../litentry/core/authentication/src/jwt.rs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tee-worker/identity/litentry/core/authentication/src/jwt.rs b/tee-worker/identity/litentry/core/authentication/src/jwt.rs index 795ea4a888..b83c8851c7 100644 --- a/tee-worker/identity/litentry/core/authentication/src/jwt.rs +++ b/tee-worker/identity/litentry/core/authentication/src/jwt.rs @@ -102,7 +102,7 @@ pub fn create(payload: &Payload, secret: &[u8]) -> Result { Ok([data, encoded_signature].join(".")) } -pub fn decode(jwt: &str, secret: &[u8], validation: Validation) -> Result { +pub fn verify(jwt: &str, secret: &[u8], validation: Validation) -> Result<(), Error> { let parts: Vec<&str> = jwt.split('.').collect(); if parts.len() != 3 { return Err(Error::InvalidToken) @@ -113,11 +113,19 @@ pub fn decode(jwt: &str, secret: &[u8], validation: Validation) -> Result Result { + let parts: Vec<&str> = jwt.split('.').collect(); + if parts.len() != 3 { + return Err(Error::InvalidToken) + } + let payload = base64_decode(parts[1])?; + let payload: Payload = serde_json::from_slice(&payload).map_err(|_| Error::JsonError)?; Ok(payload) } @@ -131,10 +139,7 @@ mod tests { let secret = "secret".as_bytes(); let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: 10 }); let jwt = create(&payload, secret).unwrap(); - - let current_block = 5; - let decoded_payload = - decode(&jwt, secret, Validation::new("subject".to_string(), current_block)).unwrap(); + let decoded_payload = decode(&jwt).unwrap(); assert_eq!(decoded_payload, payload); } @@ -146,7 +151,7 @@ mod tests { let jwt = create(&payload, secret).unwrap(); let current_block = 12; - let result = decode(&jwt, secret, Validation::new("subject".to_string(), current_block)); + let result = verify(&jwt, secret, Validation::new("subject".to_string(), current_block)); assert_eq!(result, Err(Error::ExpiredToken)); } @@ -159,7 +164,7 @@ mod tests { let current_block = 5; let result = - decode(&jwt, secret, Validation::new("other-subject".to_string(), current_block)); + verify(&jwt, secret, Validation::new("other-subject".to_string(), current_block)); assert_eq!(result, Err(Error::InvalidSubject)); } From ef92f5e8c73ee6ee4cfbaebc968d5e0094f4b25c Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:16:37 +0000 Subject: [PATCH 07/14] refactoring get_omni_account to take optional header --- .../core/native-task/receiver/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs index b71db6d340..ffa9a1a6cf 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs @@ -318,7 +318,7 @@ fn handle_trusted_call< who )), TrustedCall::add_account(who, identity, validation_data, public_account) => { - let omni_account = match get_omni_account(context.ocall_api.clone(), &who) { + let omni_account = match get_omni_account(context.ocall_api.clone(), &who, None) { Ok(account) => account, Err(e) => { log::error!("Failed to get omni account: {:?}", e); @@ -417,7 +417,7 @@ fn handle_trusted_call< )) )), TrustedCall::request_auth_token(who, auth_options) => { - let omni_account = match get_omni_account(context.ocall_api.clone(), &who) { + let omni_account = match get_omni_account(context.ocall_api.clone(), &who, None) { Ok(account) => account, Err(e) => { log::error!("Failed to get omni account: {:?}", e); @@ -512,15 +512,19 @@ where fn get_omni_account( ocall_api: Arc, who: &Identity, + header: Option, ) -> Result { let omni_account = match OmniAccountStore::get_omni_account(who.hash()) { Ok(Some(account)) => account, _ => { log::warn!("Failed to get omni account from the in-memory store"); - let header: ParachainHeader = ocall_api.get_header().map_err(|_| { - log::error!("Failed to get header"); - "Failed to get header" - })?; + let header = match header { + Some(h) => h, + None => ocall_api.get_header().map_err(|_| { + log::error!("Failed to get header"); + "Failed to get header" + })?, + }; OmniAccountRepository::new(ocall_api) .with_header(header) .get_account_by_member_hash(who.hash()) From 67b97b8d5eec418f5819d615169fbb1df97e2151 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:17:08 +0000 Subject: [PATCH 08/14] adding new error variant --- .../identity/litentry/core/native-task/receiver/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs index 3c618d10ff..5f4c9234ad 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs @@ -137,4 +137,5 @@ pub enum NativeTaskError { InvalidRequest, NativeRequestSendFailed, AuthTokenCreationFailed, + ParentchainHeaderRetrievalFailed, } From 2b41216772a86950c7a8c504e975f0fcf141b005 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:17:36 +0000 Subject: [PATCH 09/14] adding TCAuthentication AuthToken variant --- .../core/native-task/receiver/src/trusted_call_authenticated.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs index c97b848bd3..225eb266b1 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs @@ -35,6 +35,7 @@ type VerificationCode = String; pub enum TCAuthentication { Web3(LitentryMultiSignature), Email(VerificationCode), + AuthToken(String), } #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] From 0884659f622d4ad28be2c3302c42d47c96a7bc16 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:18:08 +0000 Subject: [PATCH 10/14] adding verify_tca_auth_token_authentication helper --- .../receiver/src/trusted_call_authenticated.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs index 225eb266b1..ff3c99895a 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/trusted_call_authenticated.rs @@ -18,7 +18,9 @@ use alloc::string::String; use codec::{Decode, Encode}; use ita_stf::{LitentryMultiSignature, TrustedCall}; use itp_stf_primitives::traits::TrustedCallVerification; -use itp_types::parentchain::Index as ParentchainIndex; +use itp_types::parentchain::{BlockNumber, Index as ParentchainIndex}; +use itp_utils::stringify::account_id_to_string_without_prefix; +use lc_authentication::jwt; use lc_identity_verification::VerificationCodeStore; use lc_omni_account::InMemoryStore as OmniAccountStore; use litentry_hex_utils::hex_encode; @@ -130,3 +132,14 @@ fn extract_omni_account_from_call(call: &TrustedCall) -> AccountId { member_identity.to_omni_account() } } + +pub fn verify_tca_auth_token_authentication( + omni_account: &AccountId, + current_block: BlockNumber, + auth_token: String, + secret: &[u8], +) -> bool { + let expected_subject = account_id_to_string_without_prefix(&omni_account); + let validation = jwt::Validation::new(expected_subject, current_block); + jwt::verify(&auth_token, secret, validation).is_ok() +} From c617cc77d1d13f075560ca5cda45668079a0d912 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:18:35 +0000 Subject: [PATCH 11/14] handling AuthToken authentication --- .../core/native-task/receiver/src/lib.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs index ffa9a1a6cf..6adb2cb5ca 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs @@ -215,6 +215,26 @@ where ), TCAuthentication::Email(verification_code) => verify_tca_email_authentication(&tca.call, verification_code), + TCAuthentication::AuthToken(auth_token) => { + let Ok(header)= context.ocall_api.get_header::() else { + let res: Result<(), NativeTaskError> = Err(NativeTaskError::ParentchainHeaderRetrievalFailed); + context.author_api.send_rpc_response(connection_hash, res.encode(), false); + return Err("Failed to get parachain header") + }; + let current_block = header.number; + let sender_identity = tca.call.sender_identity(); + let omni_account = + match get_omni_account(context.ocall_api.clone(), sender_identity, Some(header)) { + Ok(account) => account, + _ => sender_identity.to_omni_account(), + }; + verify_tca_auth_token_authentication( + &omni_account, + current_block, + auth_token, + &context.data_provider_config.jwt_secret.as_bytes(), + ) + }, }; if !authentication_valid { From 295658947950cb41edbd01ee3a854e41e7a49be3 Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Fri, 6 Dec 2024 18:31:16 +0000 Subject: [PATCH 12/14] fixing fmt --- tee-worker/identity/app-libs/stf/Cargo.toml | 2 +- tee-worker/identity/litentry/core/authentication/Cargo.toml | 6 +++--- .../identity/litentry/core/native-task/receiver/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tee-worker/identity/app-libs/stf/Cargo.toml b/tee-worker/identity/app-libs/stf/Cargo.toml index 8bb4f8308d..7441efb61b 100644 --- a/tee-worker/identity/app-libs/stf/Cargo.toml +++ b/tee-worker/identity/app-libs/stf/Cargo.toml @@ -37,8 +37,8 @@ sp-std = { workspace = true } # litentry itp-node-api-metadata-provider = { workspace = true } -lc-stf-task-sender = { path = "../../litentry/core/stf-task/sender", default-features = false } lc-authentication = { path = "../../litentry/core/authentication", default-features = false } +lc-stf-task-sender = { path = "../../litentry/core/stf-task/sender", default-features = false } litentry-hex-utils = { workspace = true } litentry-macros = { workspace = true } litentry-primitives = { workspace = true } diff --git a/tee-worker/identity/litentry/core/authentication/Cargo.toml b/tee-worker/identity/litentry/core/authentication/Cargo.toml index 47891f028a..e5defc0f6e 100644 --- a/tee-worker/identity/litentry/core/authentication/Cargo.toml +++ b/tee-worker/identity/litentry/core/authentication/Cargo.toml @@ -5,12 +5,12 @@ name = "lc-authentication" version = "0.1.0" [dependencies] +base64 = { version = "0.22", default-features = false, features = ["alloc"] } # a newer base64 codec = { package = "parity-scale-codec", workspace = true } -serde = { workspace = true } +parentchain-primitives = { workspace = true } ring = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } -base64 = { version = "0.22", default-features = false, features = ["alloc"] } # a newer base64 -parentchain-primitives = { workspace = true } sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } diff --git a/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml b/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml index d0ccba14c0..7fa8050216 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml +++ b/tee-worker/identity/litentry/core/native-task/receiver/Cargo.toml @@ -29,12 +29,12 @@ itp-types = { workspace = true } itp-utils = { workspace = true } frame-support = { workspace = true } +lc-authentication = { workspace = true } lc-data-providers = { workspace = true } lc-dynamic-assertion = { workspace = true } lc-identity-verification = { workspace = true } lc-native-task-sender = { workspace = true } lc-omni-account = { workspace = true } -lc-authentication = { workspace = true } litentry-hex-utils = { workspace = true } litentry-macros = { workspace = true } litentry-primitives = { workspace = true } From c8e6f80f019c1e222d9557124e87da988df738cc Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Sat, 7 Dec 2024 10:14:26 +0000 Subject: [PATCH 13/14] adding jwt test assertion --- tee-worker/identity/litentry/core/authentication/src/jwt.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tee-worker/identity/litentry/core/authentication/src/jwt.rs b/tee-worker/identity/litentry/core/authentication/src/jwt.rs index b83c8851c7..401c27e46f 100644 --- a/tee-worker/identity/litentry/core/authentication/src/jwt.rs +++ b/tee-worker/identity/litentry/core/authentication/src/jwt.rs @@ -139,8 +139,10 @@ mod tests { let secret = "secret".as_bytes(); let payload = Payload::new("subject".to_string(), AuthOptions { expires_at: 10 }); let jwt = create(&payload, secret).unwrap(); - let decoded_payload = decode(&jwt).unwrap(); + assert!(verify(&jwt, secret, Validation::new("subject".to_string(), 5)).is_ok()); + + let decoded_payload = decode(&jwt).unwrap(); assert_eq!(decoded_payload, payload); } From 7325a3fe80f6e62793ed8cf9202e7a8a6d15635b Mon Sep 17 00:00:00 2001 From: Francisco Silva Date: Mon, 9 Dec 2024 17:31:29 +0000 Subject: [PATCH 14/14] adding auth token response --- .../identity/litentry/core/native-task/receiver/src/lib.rs | 7 ++++--- .../litentry/core/native-task/receiver/src/types.rs | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs index 6c6a7164d2..15bf825eba 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/lib.rs @@ -565,15 +565,16 @@ fn handle_trusted_call { log::warn!("Received unsupported call: {:?}", call); diff --git a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs index 79522578fb..2e932429f0 100644 --- a/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs +++ b/tee-worker/identity/litentry/core/native-task/receiver/src/types.rs @@ -151,6 +151,7 @@ pub enum TrustedCallOk { idx: u8, len: u8, }, + AuthToken(String), } impl From<&ExtrinsicReport> for TrustedCallOk { @@ -163,6 +164,12 @@ impl From<&ExtrinsicReport> for TrustedCallOk } } +impl From for TrustedCallOk { + fn from(auth_token: String) -> Self { + TrustedCallOk::AuthToken(auth_token) + } +} + impl From for TrustedCallOk { fn from(result: RequestVcResultOrError) -> Self { TrustedCallOk::RequestVcResult { result: result.result, idx: result.idx, len: result.len }