From 7ead80a5699acba387c067c9c056d79a2cd49aad Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:43:38 +0200 Subject: [PATCH] One-time command to upload the current idgraph to parachain (#3131) * relocate aes key * add skeleton * small update * init * add cli * fix param parsing * update lockfile * fix bug * use subcommand * remove rpc * fix clippy * fix clippy * fix test --- common/primitives/core/src/omni_account.rs | 3 +- parachain/pallets/omni-account/src/lib.rs | 85 ++++-- tee-worker/Cargo.lock | 4 + .../app-libs/stf/src/trusted_call.rs | 2 +- .../bitacross/enclave-runtime/Cargo.lock | 3 + .../node-api/metadata/src/lib.rs | 12 +- .../node-api/metadata/src/metadata_mocks.rs | 37 ++- .../metadata/src/pallet_omni_account.rs | 45 +++ .../core-primitives/sgx/crypto/Cargo.toml | 10 +- .../core-primitives/sgx/crypto/src/aes256.rs | 269 ++++++++++++++++++ .../core-primitives/sgx/crypto/src/lib.rs | 9 +- .../common/litentry/primitives/src/aes.rs | 134 --------- .../common/litentry/primitives/src/lib.rs | 3 +- .../identity/app-libs/stf/src/trusted_call.rs | 2 +- .../cli/src/base_cli/commands/litentry/mod.rs | 19 +- tee-worker/identity/cli/src/base_cli/mod.rs | 12 +- .../identity/cli/src/benchmark/request_vc.rs | 6 +- .../commands/litentry/request_vc.rs | 6 +- .../enclave-api/ffi/src/lib.rs | 2 + .../enclave-api/src/enclave_base.rs | 13 + .../identity/enclave-runtime/Cargo.lock | 5 + .../identity/enclave-runtime/Cargo.toml | 2 + .../identity/enclave-runtime/Enclave.edl | 2 + .../src/initialization/global_components.rs | 11 +- .../enclave-runtime/src/initialization/mod.rs | 113 +++++++- .../identity/enclave-runtime/src/lib.rs | 10 + .../enclave-runtime/src/test/tests_main.rs | 2 + tee-worker/identity/service/src/cli.yml | 9 +- tee-worker/identity/service/src/main_impl.rs | 8 + .../src/tests/mocks/enclave_api_mock.rs | 4 + 30 files changed, 634 insertions(+), 208 deletions(-) create mode 100644 tee-worker/common/core-primitives/node-api/metadata/src/pallet_omni_account.rs create mode 100644 tee-worker/common/core-primitives/sgx/crypto/src/aes256.rs delete mode 100644 tee-worker/common/litentry/primitives/src/aes.rs diff --git a/common/primitives/core/src/omni_account.rs b/common/primitives/core/src/omni_account.rs index c30a6cf715..fb2adc411f 100644 --- a/common/primitives/core/src/omni_account.rs +++ b/common/primitives/core/src/omni_account.rs @@ -17,8 +17,7 @@ use crate::{AccountId, Hash, Identity, Vec}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; -use sp_io::hashing::blake2_256; -use sp_runtime::{BoundedVec, RuntimeDebug}; +use sp_runtime::RuntimeDebug; #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, RuntimeDebug)] pub enum MemberAccount { diff --git a/parachain/pallets/omni-account/src/lib.rs b/parachain/pallets/omni-account/src/lib.rs index cd67e8400f..4346b61b1c 100644 --- a/parachain/pallets/omni-account/src/lib.rs +++ b/parachain/pallets/omni-account/src/lib.rs @@ -55,7 +55,6 @@ pub enum RawOrigin { pub mod pallet { use super::*; - /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); #[pallet::pallet] @@ -127,6 +126,8 @@ pub mod pallet { AccountRemoved { who: T::AccountId, member_account_hashes: Vec }, /// Some member account is made public AccountMadePublic { who: T::AccountId, member_account_hash: H256 }, + /// An account store is updated + AccountStoreUpdated { who: T::AccountId }, /// Some call is dispatched as omni-account origin DispatchedAsOmniAccount { who: T::AccountId, result: DispatchResult }, /// Some call is dispatched as signed origin @@ -154,7 +155,7 @@ pub mod pallet { origin: OriginFor, member_account_hash: H256, call: Box<::RuntimeCall>, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; let omni_account = MemberAccountHash::::get(member_account_hash) .ok_or(Error::::AccountNotFound)?; @@ -163,7 +164,7 @@ pub mod pallet { who: omni_account, result: result.map(|_| ()).map_err(|e| e.error), }); - Ok(()) + Ok(Pays::No.into()) } // dispatch the `call` as the standard (frame_system) signed origin @@ -174,7 +175,7 @@ pub mod pallet { origin: OriginFor, member_account_hash: H256, call: Box<::RuntimeCall>, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; let omni_account = MemberAccountHash::::get(member_account_hash) .ok_or(Error::::AccountNotFound)?; @@ -186,30 +187,19 @@ pub mod pallet { who: omni_account, result: result.map(|_| ()).map_err(|e| e.error), }); - Ok(()) + Ok(Pays::No.into()) } #[pallet::call_index(2)] #[pallet::weight((195_000_000, DispatchClass::Normal))] - pub fn create_account_store(origin: OriginFor, identity: Identity) -> DispatchResult { + pub fn create_account_store( + origin: OriginFor, + identity: Identity, + ) -> DispatchResultWithPostInfo { // initial creation request has to come from `TEECallOrigin` let _ = T::TEECallOrigin::ensure_origin(origin)?; - let hash = identity.hash(); - let omni_account = T::OmniAccountConverter::convert(&identity); - - ensure!(!MemberAccountHash::::contains_key(hash), Error::::AccountAlreadyAdded); - - let mut member_accounts: MemberAccounts = BoundedVec::new(); - member_accounts - .try_push(identity.into()) - .map_err(|_| Error::::AccountStoreLenLimitReached)?; - - MemberAccountHash::::insert(hash, omni_account.clone()); - AccountStore::::insert(omni_account.clone(), member_accounts); - - Self::deposit_event(Event::AccountStoreCreated { who: omni_account }); - - Ok(()) + let _ = Self::do_create_account_store(identity)?; + Ok(Pays::No.into()) } #[pallet::call_index(3)] @@ -305,6 +295,57 @@ pub mod pallet { Self::deposit_event(Event::IntentionRequested { who, intention }); Ok(()) } + + /// temporary extrinsic to upload the existing IDGraph from the worker onto chain + #[pallet::call_index(7)] + #[pallet::weight((195_000_000, DispatchClass::Normal))] + pub fn update_account_store_by_one( + origin: OriginFor, + who: Identity, + member_account: MemberAccount, + ) -> DispatchResultWithPostInfo { + let _ = T::TEECallOrigin::ensure_origin(origin.clone())?; + + let who_account = T::OmniAccountConverter::convert(&who); + + let mut member_accounts = match AccountStore::::get(&who_account) { + Some(s) => s, + None => Self::do_create_account_store(who)?, + }; + + if !member_accounts.contains(&member_account) { + member_accounts + .try_push(member_account.clone()) + .map_err(|_| Error::::AccountStoreLenLimitReached)?; + } + + MemberAccountHash::::insert(member_account.hash(), who_account.clone()); + AccountStore::::insert(who_account.clone(), member_accounts); + Self::deposit_event(Event::AccountStoreUpdated { who: who_account }); + + Ok(Pays::No.into()) + } + } + + impl Pallet { + fn do_create_account_store(identity: Identity) -> Result, Error> { + let hash = identity.hash(); + let omni_account = T::OmniAccountConverter::convert(&identity); + + ensure!(!MemberAccountHash::::contains_key(hash), Error::::AccountAlreadyAdded); + + let mut member_accounts: MemberAccounts = BoundedVec::new(); + member_accounts + .try_push(identity.into()) + .map_err(|_| Error::::AccountStoreLenLimitReached)?; + + MemberAccountHash::::insert(hash, omni_account.clone()); + AccountStore::::insert(omni_account.clone(), member_accounts.clone()); + + Self::deposit_event(Event::AccountStoreCreated { who: omni_account }); + + Ok(member_accounts) + } } } diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 030f39a120..99096e111c 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -4194,6 +4194,9 @@ dependencies = [ "log 0.4.20", "ofb", "parity-scale-codec", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "ring 0.16.20", "secp256k1 0.28.0", "serde_json 1.0.120", "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", @@ -4202,6 +4205,7 @@ dependencies = [ "sgx_tstd", "sgx_types", "sp-core", + "sp-std", ] [[package]] diff --git a/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs b/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs index ac2a93ffef..173a5c4126 100644 --- a/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs @@ -38,7 +38,7 @@ use itp_types::{ use itp_utils::stringify::account_id_to_string; pub use litentry_primitives::{ aes_encrypt_default, AesOutput, Identity, LitentryMultiSignature, ParentchainBlockNumber, - RequestAesKey, RequestAesKeyNonce, ValidationData, + RequestAesKey, ValidationData, }; use log::*; use sp_core::{ diff --git a/tee-worker/bitacross/enclave-runtime/Cargo.lock b/tee-worker/bitacross/enclave-runtime/Cargo.lock index f51264201a..f8926bab5d 100644 --- a/tee-worker/bitacross/enclave-runtime/Cargo.lock +++ b/tee-worker/bitacross/enclave-runtime/Cargo.lock @@ -2419,6 +2419,8 @@ dependencies = [ "log 0.4.21", "ofb", "parity-scale-codec", + "rand 0.7.3", + "ring 0.16.20", "secp256k1 0.28.0", "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", "sgx_crypto_helper", @@ -2426,6 +2428,7 @@ dependencies = [ "sgx_tstd", "sgx_types", "sp-core", + "sp-std", ] [[package]] diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs b/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs index 352decf8ca..0362f47b5b 100644 --- a/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs +++ b/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs @@ -22,9 +22,10 @@ use crate::{ error::Result, pallet_balances::BalancesCallIndexes, pallet_bitacross::BitAcrossCallIndexes, pallet_evm_assertion::EvmAssertionsCallIndexes, pallet_imp::IMPCallIndexes, - pallet_proxy::ProxyCallIndexes, pallet_system::SystemConstants, - pallet_teebag::TeebagCallIndexes, pallet_timestamp::TimestampCallIndexes, - pallet_utility::UtilityCallIndexes, pallet_vcmp::VCMPCallIndexes, + pallet_omni_account::OmniAccountCallIndexes, pallet_proxy::ProxyCallIndexes, + pallet_system::SystemConstants, pallet_teebag::TeebagCallIndexes, + pallet_timestamp::TimestampCallIndexes, pallet_utility::UtilityCallIndexes, + pallet_vcmp::VCMPCallIndexes, }; use codec::{Decode, Encode}; use sp_core::storage::StorageKey; @@ -37,6 +38,7 @@ pub mod pallet_balances; pub mod pallet_bitacross; pub mod pallet_evm_assertion; pub mod pallet_imp; +pub mod pallet_omni_account; pub mod pallet_proxy; pub mod pallet_system; pub mod pallet_teebag; @@ -60,6 +62,7 @@ pub trait NodeMetadataTrait: + TimestampCallIndexes + EvmAssertionsCallIndexes + BitAcrossCallIndexes + + OmniAccountCallIndexes { } @@ -73,7 +76,8 @@ impl< + BalancesCallIndexes + TimestampCallIndexes + EvmAssertionsCallIndexes - + BitAcrossCallIndexes, + + BitAcrossCallIndexes + + OmniAccountCallIndexes, > NodeMetadataTrait for T { } diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs b/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs index 6fb986da21..0189d1309d 100644 --- a/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs +++ b/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs @@ -18,9 +18,10 @@ use crate::{ error::Result, pallet_balances::BalancesCallIndexes, pallet_bitacross::BitAcrossCallIndexes, pallet_evm_assertion::EvmAssertionsCallIndexes, pallet_imp::IMPCallIndexes, - pallet_proxy::ProxyCallIndexes, pallet_system::SystemConstants, - pallet_teebag::TeebagCallIndexes, pallet_timestamp::TimestampCallIndexes, - pallet_utility::UtilityCallIndexes, pallet_vcmp::VCMPCallIndexes, runtime_call::RuntimeCall, + pallet_omni_account::OmniAccountCallIndexes, pallet_proxy::ProxyCallIndexes, + pallet_system::SystemConstants, pallet_teebag::TeebagCallIndexes, + pallet_timestamp::TimestampCallIndexes, pallet_utility::UtilityCallIndexes, + pallet_vcmp::VCMPCallIndexes, runtime_call::RuntimeCall, }; use codec::{Decode, Encode}; @@ -95,6 +96,12 @@ pub struct NodeMetadataMock { btc_wallet_generated: u8, eth_wallet_generated: u8, ton_wallet_generated: u8, + + omni_account_module: u8, + dispatch_as_omni_account: u8, + dispatch_as_signed: u8, + create_account_store: u8, + update_account_store_by_one: u8, } impl NodeMetadataMock { @@ -157,6 +164,12 @@ impl NodeMetadataMock { btc_wallet_generated: 2u8, eth_wallet_generated: 3u8, ton_wallet_generated: 4u8, + + omni_account_module: 70u8, + dispatch_as_omni_account: 0u8, + dispatch_as_signed: 1u8, + create_account_store: 2u8, + update_account_store_by_one: 3u8, } } } @@ -346,3 +359,21 @@ impl EvmAssertionsCallIndexes for NodeMetadataMock { Ok([self.evm_assertions_module, self.evm_assertions_void_assertion]) } } + +impl OmniAccountCallIndexes for NodeMetadataMock { + fn dispatch_as_omni_account_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.omni_account_module, self.dispatch_as_omni_account]) + } + + fn dispatch_as_signed_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.omni_account_module, self.dispatch_as_signed]) + } + + fn create_account_store_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.omni_account_module, self.create_account_store]) + } + + fn update_account_store_by_one_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.omni_account_module, self.update_account_store_by_one]) + } +} diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/pallet_omni_account.rs b/tee-worker/common/core-primitives/node-api/metadata/src/pallet_omni_account.rs new file mode 100644 index 0000000000..cdd1101080 --- /dev/null +++ b/tee-worker/common/core-primitives/node-api/metadata/src/pallet_omni_account.rs @@ -0,0 +1,45 @@ +// 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 . + +// TODO: maybe use macros to simplify this +use crate::{error::Result, NodeMetadata}; + +const OMNIACCOUNT: &str = "OmniAccount"; + +pub trait OmniAccountCallIndexes { + fn dispatch_as_omni_account_call_indexes(&self) -> Result<[u8; 2]>; + fn dispatch_as_signed_call_indexes(&self) -> Result<[u8; 2]>; + fn create_account_store_call_indexes(&self) -> Result<[u8; 2]>; + fn update_account_store_by_one_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl OmniAccountCallIndexes for NodeMetadata { + fn dispatch_as_omni_account_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(OMNIACCOUNT, "dispatch_as_omni_account") + } + + fn dispatch_as_signed_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(OMNIACCOUNT, "dispatch_as_signed") + } + + fn create_account_store_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(OMNIACCOUNT, "create_account_store") + } + + fn update_account_store_by_one_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(OMNIACCOUNT, "update_account_store_by_one") + } +} diff --git a/tee-worker/common/core-primitives/sgx/crypto/Cargo.toml b/tee-worker/common/core-primitives/sgx/crypto/Cargo.toml index 2aa3a3b5a4..4c560037d5 100644 --- a/tee-worker/common/core-primitives/sgx/crypto/Cargo.toml +++ b/tee-worker/common/core-primitives/sgx/crypto/Cargo.toml @@ -11,16 +11,20 @@ hex = { workspace = true } k256 = { workspace = true, features = ["ecdsa-core", "schnorr", "alloc"] } log = { workspace = true } ofb = { workspace = true } +rand = { workspace = true, optional = true } +rand-sgx = { workspace = true, optional = true } +ring = { workspace = true } secp256k1 = { workspace = true, features = ["alloc", "recovery"] } serde_json = { workspace = true, optional = true } - serde_json_sgx = { workspace = true, optional = true } + sgx_crypto_helper = { workspace = true } sgx_rand = { workspace = true, optional = true } sgx_tstd = { workspace = true, optional = true } sgx_types = { workspace = true } sp-core = { workspace = true } +sp-std = { workspace = true } itp-sgx-io = { workspace = true } itp-sgx-temp-dir = { workspace = true, optional = true } @@ -30,8 +34,11 @@ default = ["std"] std = [ "codec/std", "log/std", + "rand", + "ring/std", "itp-sgx-io/std", "sp-core/std", + "sp-std/std", "serde_json/std", "sgx_crypto_helper/default", ] @@ -39,6 +46,7 @@ sgx = [ "sgx_crypto_helper/mesalock_sgx", "sgx_tstd", "sgx_rand", + "rand-sgx", "itp-sgx-io/sgx", "serde_json_sgx", ] diff --git a/tee-worker/common/core-primitives/sgx/crypto/src/aes256.rs b/tee-worker/common/core-primitives/sgx/crypto/src/aes256.rs new file mode 100644 index 0000000000..11ecea9ae1 --- /dev/null +++ b/tee-worker/common/core-primitives/sgx/crypto/src/aes256.rs @@ -0,0 +1,269 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use codec::{Decode, Encode}; +use rand::Rng; +pub use ring::aead::{MAX_TAG_LEN, NONCE_LEN}; +use ring::{ + aead::{Aad, BoundKey, LessSafeKey, Nonce, NonceSequence, SealingKey, UnboundKey, AES_256_GCM}, + error::Unspecified, +}; +use std::vec::Vec; + +// we use 256-bit AES-GCM as request enc/dec key +pub const AES_KEY_LEN: usize = 32; + +pub type Aes256Key = [u8; AES_KEY_LEN]; +pub type Aes256KeyNonce = [u8; NONCE_LEN]; + +/// File name of the sealed seed file. +pub const SEALED_SIGNER_SEED_FILE: &str = "aes256_key_sealed.bin"; + +// all-in-one struct containing the encrypted ciphertext with other +// metadata that is required for decryption +// +// by default a postfix tag is used => last 16 bytes of ciphertext is MAC tag +#[derive(Debug, Default, Clone, Eq, PartialEq, Encode, Decode)] +pub struct AesOutput { + pub ciphertext: Vec, + pub aad: Vec, + pub nonce: Aes256KeyNonce, // IV +} + +// Returns the default if any error happens +// We don't propagate the error to upper level as this function is used in too many places, +// it's too verbose to handle them all and pass back to the parentchain as events. +// We rely on the parentchain event consumers to handle them correctly (and they kind of +// have to, because they'll find all fields are 0) +pub fn aes_encrypt_default(key: &Aes256Key, data: &[u8]) -> AesOutput { + let mut in_out = data.to_vec(); + + let mut nonce = RingAeadNonceSequence::new(); + if nonce.advance().is_ok() { + let aad = b""; + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let mut sealing_key = SealingKey::new(unbound_key, nonce.clone()); + if sealing_key.seal_in_place_append_tag(Aad::from(aad), &mut in_out).is_ok() { + return AesOutput { + ciphertext: in_out.to_vec(), + aad: aad.to_vec(), + nonce: nonce.nonce, + } + } + } + } + + AesOutput::default() +} + +// use LessSafeKey::seal_in_place_append_tag to encrypt the data using the given nonce +// don't be scared by the name, it's similar to `SealingKey::seal_in_place_append_tag`, +// except that it accepts an arbitrary nonce. +// It's only used by the one-off verification message calculation. +pub fn aes_encrypt_nonce(key: &Aes256Key, data: &[u8], nonce: Aes256KeyNonce) -> AesOutput { + let mut in_out = data.to_vec(); + let aad = b""; + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let less_safe_key = LessSafeKey::new(unbound_key); + if less_safe_key + .seal_in_place_append_tag( + Nonce::assume_unique_for_key(nonce), + Aad::from(aad), + &mut in_out, + ) + .is_ok() + { + return AesOutput { ciphertext: in_out.to_vec(), aad: aad.to_vec(), nonce } + } + } + + AesOutput::default() +} + +pub fn aes_decrypt(key: &Aes256Key, data: &mut AesOutput) -> Option> { + let in_out = data.ciphertext.as_mut(); + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let less_safe_key = LessSafeKey::new(unbound_key); + return less_safe_key + .open_in_place( + Nonce::assume_unique_for_key(data.nonce), + Aad::from(data.aad.clone()), + in_out, + ) + .ok() + .map(|data| data.to_vec()) + } + None +} + +#[derive(Clone)] +struct RingAeadNonceSequence { + pub nonce: Aes256KeyNonce, +} + +impl RingAeadNonceSequence { + fn new() -> RingAeadNonceSequence { + RingAeadNonceSequence { nonce: [0u8; NONCE_LEN] } + } +} + +impl NonceSequence for RingAeadNonceSequence { + fn advance(&mut self) -> core::result::Result { + let nonce = Nonce::assume_unique_for_key(self.nonce); + let nonce_vec = rand::thread_rng().gen::(); + self.nonce.copy_from_slice(&nonce_vec[0..NONCE_LEN]); + Ok(nonce) + } +} + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use crate::{ + error::{Error, Result}, + key_repository::KeyRepository, + }; + use itp_sgx_io::{seal, unseal, SealedIO}; + use log::*; + use sgx_rand::{Rng, StdRng}; + use std::{ + path::PathBuf, + sgxfs::SgxFile, + string::{String, ToString}, + }; + + /// Gets a repository for an Rsa3072 keypair and initializes + /// a fresh key pair if it doesn't exist at `path`. + pub fn create_aes256_repository( + path: PathBuf, + key_file_prefix: &str, + key: Option, + ) -> Result> { + let seal = Seal::new(path, key_file_prefix.to_string()); + Ok(KeyRepository::new(seal.init(key)?, seal.into())) + } + + #[derive(Clone, Debug)] + pub struct Seal { + base_path: PathBuf, + key_file_prefix: String, + } + + impl Seal { + pub fn new(base_path: PathBuf, key_file_prefix: String) -> Self { + Self { base_path, key_file_prefix } + } + + pub fn path(&self) -> PathBuf { + self.base_path + .join(self.key_file_prefix.clone() + "_" + SEALED_SIGNER_SEED_FILE) + } + } + + impl Seal { + fn unseal_key(&self) -> Result { + self.unseal() + } + + pub fn exists(&self) -> bool { + SgxFile::open(self.path()).is_ok() + } + + pub fn init(&self, key: Option) -> Result { + if !self.exists() || key.is_some() { + if !self.exists() { + info!("Keyfile not found, creating new! {}", self.path().display()); + } + if key.is_some() { + info!("New key provided, it will be sealed!"); + } + let key = if let Some(key) = key { + key + } else { + let mut seed = Aes256Key::default(); + let mut rand = StdRng::new()?; + rand.fill_bytes(&mut seed); + seed + }; + seal(&key, self.path())?; + } + self.unseal_key() + } + } + + impl SealedIO for Seal { + type Error = Error; + type Unsealed = Aes256Key; + + fn unseal(&self) -> Result { + Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) + } + } +} + +#[cfg(feature = "test")] +pub mod sgx_tests { + use super::sgx::*; + use crate::key_repository::AccessKey; + use itp_sgx_temp_dir::TempDir; + + pub fn aes256_creating_repository_with_same_path_and_prefix_results_in_same_key() { + let prefix = "test"; + let temp_dir = TempDir::with_prefix( + "aes256_creating_repository_with_same_path_and_prefix_results_in_same_key", + ) + .unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + let key1 = create_aes256_repository(temp_path.clone(), prefix, None) + .unwrap() + .retrieve_key() + .unwrap(); + let key2 = create_aes256_repository(temp_path, prefix, None) + .unwrap() + .retrieve_key() + .unwrap(); + assert_eq!(key1, key2); + } + + pub fn aes256_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key() { + let prefix = "test"; + let temp_dir = TempDir::with_prefix( + "aes256_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key", + ) + .unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + + let new_key: [u8; 32] = [1u8; 32]; + let first_key = create_aes256_repository(temp_path.clone(), prefix, None) + .unwrap() + .retrieve_key() + .unwrap(); + let second_key = create_aes256_repository(temp_path, prefix, Some(new_key)) + .unwrap() + .retrieve_key() + .unwrap(); + + assert_ne!(first_key, second_key); + assert_eq!(second_key, new_key); + } +} diff --git a/tee-worker/common/core-primitives/sgx/crypto/src/lib.rs b/tee-worker/common/core-primitives/sgx/crypto/src/lib.rs index f52d555abb..b9c8fa9037 100644 --- a/tee-worker/common/core-primitives/sgx/crypto/src/lib.rs +++ b/tee-worker/common/core-primitives/sgx/crypto/src/lib.rs @@ -29,10 +29,12 @@ 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 rand_sgx as rand; pub use serde_json_sgx as serde_json; } pub mod aes; +pub mod aes256; pub mod ecdsa; pub mod ed25519; pub mod ed25519_derivation; @@ -42,7 +44,7 @@ pub mod rsa3072; pub mod schnorr; pub mod traits; -pub use self::{aes::*, ecdsa::*, ed25519::*, rsa3072::*}; +pub use self::{aes::*, aes256::*, ecdsa::*, ed25519::*, rsa3072::*}; pub use error::*; pub use traits::*; @@ -63,6 +65,11 @@ pub mod tests { aes_sealing_works, using_get_aes_repository_twice_initializes_key_only_once, }; + pub use super::aes256::sgx_tests::{ + aes256_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key, + aes256_creating_repository_with_same_path_and_prefix_results_in_same_key, + }; + pub use super::ecdsa::sgx_tests::{ ecdsa_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key, ecdsa_creating_repository_with_same_path_and_prefix_results_in_same_key, diff --git a/tee-worker/common/litentry/primitives/src/aes.rs b/tee-worker/common/litentry/primitives/src/aes.rs deleted file mode 100644 index 8abbb7b149..0000000000 --- a/tee-worker/common/litentry/primitives/src/aes.rs +++ /dev/null @@ -1,134 +0,0 @@ -// 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(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate rand_sgx as rand; - -use crate::{Decode, Encode, Vec}; - -use rand::Rng; - -use ring::{ - aead::{Aad, BoundKey, LessSafeKey, Nonce, NonceSequence, SealingKey, UnboundKey, AES_256_GCM}, - error::Unspecified, -}; - -// we use 256-bit AES-GCM as request enc/dec key -pub const REQUEST_AES_KEY_LEN: usize = 32; -pub use ring::aead::{MAX_TAG_LEN, NONCE_LEN}; - -pub type RequestAesKey = [u8; REQUEST_AES_KEY_LEN]; -pub type RequestAesKeyNonce = [u8; NONCE_LEN]; - -// all-in-one struct containing the encrypted ciphertext with other -// metadata that is required for decryption -// -// by default a postfix tag is used => last 16 bytes of ciphertext is MAC tag -#[derive(Debug, Default, Clone, Eq, PartialEq, Encode, Decode)] -pub struct AesOutput { - pub ciphertext: Vec, - pub aad: Vec, - pub nonce: RequestAesKeyNonce, // IV -} - -// Returns the default if any error happens -// We don't propagate the error to upper level as this function is used in too many places, -// it's too verbose to handle them all and pass back to the parentchain as events. -// We rely on the parentchain event consumers to handle them correctly (and they kind of -// have to, because they'll find all fields are 0) -pub fn aes_encrypt_default(key: &RequestAesKey, data: &[u8]) -> AesOutput { - let mut in_out = data.to_vec(); - - let mut nonce = RingAeadNonceSequence::new(); - if nonce.advance().is_ok() { - let aad = b""; - if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { - let mut sealing_key = SealingKey::new(unbound_key, nonce.clone()); - if sealing_key.seal_in_place_append_tag(Aad::from(aad), &mut in_out).is_ok() { - return AesOutput { - ciphertext: in_out.to_vec(), - aad: aad.to_vec(), - nonce: nonce.nonce, - } - } - } - } - - AesOutput::default() -} - -// use LessSafeKey::seal_in_place_append_tag to encrypt the data using the given nonce -// don't be scared by the name, it's similar to `SealingKey::seal_in_place_append_tag`, -// except that it accepts an arbitrary nonce. -// It's only used by the one-off verification message calculation. -pub fn aes_encrypt_nonce(key: &RequestAesKey, data: &[u8], nonce: RequestAesKeyNonce) -> AesOutput { - let mut in_out = data.to_vec(); - let aad = b""; - if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { - let less_safe_key = LessSafeKey::new(unbound_key); - if less_safe_key - .seal_in_place_append_tag( - Nonce::assume_unique_for_key(nonce), - Aad::from(aad), - &mut in_out, - ) - .is_ok() - { - return AesOutput { ciphertext: in_out.to_vec(), aad: aad.to_vec(), nonce } - } - } - - AesOutput::default() -} - -pub fn aes_decrypt(key: &RequestAesKey, data: &mut AesOutput) -> Option> { - let in_out = data.ciphertext.as_mut(); - if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { - let less_safe_key = LessSafeKey::new(unbound_key); - return less_safe_key - .open_in_place( - Nonce::assume_unique_for_key(data.nonce), - Aad::from(data.aad.clone()), - in_out, - ) - .ok() - .map(|data| data.to_vec()) - } - None -} - -#[derive(Clone)] -pub struct RingAeadNonceSequence { - pub nonce: RequestAesKeyNonce, -} - -impl RingAeadNonceSequence { - fn new() -> RingAeadNonceSequence { - RingAeadNonceSequence { nonce: [0u8; NONCE_LEN] } - } -} - -impl NonceSequence for RingAeadNonceSequence { - fn advance(&mut self) -> Result { - let nonce = Nonce::assume_unique_for_key(self.nonce); - let nonce_vec = rand::thread_rng().gen::(); - self.nonce.copy_from_slice(&nonce_vec[0..NONCE_LEN]); - Ok(nonce) - } -} diff --git a/tee-worker/common/litentry/primitives/src/lib.rs b/tee-worker/common/litentry/primitives/src/lib.rs index 0615b49327..07ea4cf198 100644 --- a/tee-worker/common/litentry/primitives/src/lib.rs +++ b/tee-worker/common/litentry/primitives/src/lib.rs @@ -23,7 +23,6 @@ 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"); -mod aes; mod aes_request; mod bitcoin_address; mod bitcoin_signature; @@ -32,7 +31,6 @@ mod plain_request; mod stf_request; mod validation_data; -pub use aes::*; pub use aes_request::*; pub use bitcoin_address::*; pub use bitcoin_signature::*; @@ -43,6 +41,7 @@ pub use validation_data::*; use bitcoin::sign_message::{signed_msg_hash, MessageSignature}; use codec::{Decode, Encode, MaxEncodedLen}; +pub use itp_sgx_crypto::aes256::{Aes256Key as RequestAesKey, *}; use itp_sgx_crypto::ShieldingCryptoDecrypt; use log::error; pub use parentchain_primitives::{ 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 92be79ded3..a912525176 100644 --- a/tee-worker/identity/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/identity/app-libs/stf/src/trusted_call.rs @@ -54,7 +54,7 @@ use litentry_hex_utils::hex_encode; pub use litentry_primitives::{ aes_encrypt_default, all_evm_web3networks, all_substrate_web3networks, AesOutput, Assertion, ErrorDetail, IMPError, Identity, LitentryMultiSignature, ParentchainBlockNumber, RequestAesKey, - RequestAesKeyNonce, VCMPError, ValidationData, Web3Network, + VCMPError, ValidationData, Web3Network, }; use log::*; use sp_core::{ diff --git a/tee-worker/identity/cli/src/base_cli/commands/litentry/mod.rs b/tee-worker/identity/cli/src/base_cli/commands/litentry/mod.rs index baefc8c487..1d40f69ec2 100644 --- a/tee-worker/identity/cli/src/base_cli/commands/litentry/mod.rs +++ b/tee-worker/identity/cli/src/base_cli/commands/litentry/mod.rs @@ -14,10 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . -pub mod activate_identity; -pub mod deactivate_identity; -pub mod id_graph_hash; -pub mod link_identity; -pub mod shield_text; +mod activate_identity; +pub use activate_identity::*; + +mod deactivate_identity; +pub use deactivate_identity::*; + +mod id_graph_hash; +pub use id_graph_hash::*; + +mod link_identity; +pub use link_identity::*; + +mod shield_text; +pub use shield_text::*; pub const IMP: &str = "IdentityManagement"; diff --git a/tee-worker/identity/cli/src/base_cli/mod.rs b/tee-worker/identity/cli/src/base_cli/mod.rs index 0d4fda7fea..385b7f967a 100644 --- a/tee-worker/identity/cli/src/base_cli/mod.rs +++ b/tee-worker/identity/cli/src/base_cli/mod.rs @@ -17,16 +17,8 @@ use crate::{ base_cli::commands::{ - balance::BalanceCommand, - faucet::FaucetCommand, - listen::ListenCommand, - litentry::{ - activate_identity::ActivateIdentityCommand, - deactivate_identity::DeactivateIdentityCommand, id_graph_hash::IDGraphHashCommand, - link_identity::LinkIdentityCommand, shield_text::ShieldTextCommand, - }, - register_tcb_info::RegisterTcbInfoCommand, - transfer::TransferCommand, + balance::BalanceCommand, faucet::FaucetCommand, listen::ListenCommand, litentry::*, + register_tcb_info::RegisterTcbInfoCommand, transfer::TransferCommand, }, command_utils::*, Cli, CliResult, CliResultOk, ED25519_KEY_TYPE, SR25519_KEY_TYPE, diff --git a/tee-worker/identity/cli/src/benchmark/request_vc.rs b/tee-worker/identity/cli/src/benchmark/request_vc.rs index 2792e94172..552327c733 100644 --- a/tee-worker/identity/cli/src/benchmark/request_vc.rs +++ b/tee-worker/identity/cli/src/benchmark/request_vc.rs @@ -15,7 +15,7 @@ use itp_stf_primitives::{ traits::TrustedCallSigning, types::{KeyPair, TrustedOperation}, }; -use litentry_primitives::{RequestAesKey, ShardIdentifier, REQUEST_AES_KEY_LEN}; +use litentry_primitives::{RequestAesKey, ShardIdentifier, AES_KEY_LEN}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; use sp_core::Pair; @@ -161,6 +161,6 @@ impl BenchmarkRequestVcCommand { } fn random_aes_key() -> RequestAesKey { - let random: Vec = (0..REQUEST_AES_KEY_LEN).map(|_| rand::random::()).collect(); - random[0..REQUEST_AES_KEY_LEN].try_into().unwrap() + let random: Vec = (0..AES_KEY_LEN).map(|_| rand::random::()).collect(); + random[0..AES_KEY_LEN].try_into().unwrap() } diff --git a/tee-worker/identity/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/identity/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index d684495f05..3b137f4fe8 100644 --- a/tee-worker/identity/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/identity/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -28,7 +28,7 @@ use ita_stf::{trusted_call_result::RequestVCResult, Index, TrustedCall}; use itp_stf_primitives::{traits::TrustedCallSigning, types::KeyPair}; use litentry_primitives::{ aes_decrypt, Assertion, BoundedWeb3Network, Identity, ParameterString, RequestAesKey, - Web3Network, REQUEST_AES_KEY_LEN, + Web3Network, AES_KEY_LEN, }; use sp_core::Pair; @@ -163,8 +163,8 @@ impl RequestVcCommand { } fn random_aes_key() -> RequestAesKey { - let random: Vec = (0..REQUEST_AES_KEY_LEN).map(|_| rand::random::()).collect(); - random[0..REQUEST_AES_KEY_LEN].try_into().unwrap() + let random: Vec = (0..AES_KEY_LEN).map(|_| rand::random::()).collect(); + random[0..AES_KEY_LEN].try_into().unwrap() } } diff --git a/tee-worker/identity/core-primitives/enclave-api/ffi/src/lib.rs b/tee-worker/identity/core-primitives/enclave-api/ffi/src/lib.rs index 65b1b11aa1..cf755efcc1 100644 --- a/tee-worker/identity/core-primitives/enclave-api/ffi/src/lib.rs +++ b/tee-worker/identity/core-primitives/enclave-api/ffi/src/lib.rs @@ -238,6 +238,8 @@ extern "C" { shard_size: u32, ) -> sgx_status_t; + pub fn upload_id_graph(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; + pub fn ignore_parentchain_block_import_validation_until( eid: sgx_enclave_id_t, retval: *mut sgx_status_t, diff --git a/tee-worker/identity/core-primitives/enclave-api/src/enclave_base.rs b/tee-worker/identity/core-primitives/enclave-api/src/enclave_base.rs index 47f0bfa08f..69e8a0c9eb 100644 --- a/tee-worker/identity/core-primitives/enclave-api/src/enclave_base.rs +++ b/tee-worker/identity/core-primitives/enclave-api/src/enclave_base.rs @@ -82,6 +82,8 @@ pub trait EnclaveBase: Send + Sync + 'static { // litentry fn migrate_shard(&self, new_shard: Vec) -> EnclaveResult<()>; + + fn upload_id_graph(&self) -> EnclaveResult<()>; } /// EnclaveApi implementation for Enclave struct @@ -388,6 +390,17 @@ mod impl_ffi { Ok(()) } + + fn upload_id_graph(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { ffi::upload_id_graph(self.eid, &mut retval) }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } } fn init_parentchain_components_ffi( diff --git a/tee-worker/identity/enclave-runtime/Cargo.lock b/tee-worker/identity/enclave-runtime/Cargo.lock index a797f4427c..960d4cb82a 100644 --- a/tee-worker/identity/enclave-runtime/Cargo.lock +++ b/tee-worker/identity/enclave-runtime/Cargo.lock @@ -954,6 +954,7 @@ dependencies = [ "lc-identity-verification", "lc-native-task-receiver", "lc-parachain-extrinsic-task-receiver", + "lc-parachain-extrinsic-task-sender", "lc-stf-task-receiver", "lc-vc-task-receiver", "litentry-hex-utils", @@ -963,6 +964,7 @@ dependencies = [ "log", "multibase", "once_cell 1.4.0", + "pallet-identity-management-tee", "parity-scale-codec", "primitive-types", "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3)", @@ -2483,6 +2485,8 @@ dependencies = [ "log", "ofb", "parity-scale-codec", + "rand 0.7.3", + "ring 0.16.20", "secp256k1 0.28.0", "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", "sgx_crypto_helper", @@ -2490,6 +2494,7 @@ dependencies = [ "sgx_tstd", "sgx_types", "sp-core", + "sp-std", ] [[package]] diff --git a/tee-worker/identity/enclave-runtime/Cargo.toml b/tee-worker/identity/enclave-runtime/Cargo.toml index 5924ae3dad..94fb3534b4 100644 --- a/tee-worker/identity/enclave-runtime/Cargo.toml +++ b/tee-worker/identity/enclave-runtime/Cargo.toml @@ -149,12 +149,14 @@ lc-evm-dynamic-assertions = { path = "../litentry/core/evm-dynamic-assertions", lc-identity-verification = { path = "../litentry/core/identity-verification", default-features = false, features = ["sgx"] } lc-native-task-receiver = { path = "../litentry/core/native-task/receiver", default-features = false, features = ["sgx"] } lc-parachain-extrinsic-task-receiver = { path = "../../common/litentry/core/parachain-extrinsic-task/receiver", default-features = false, features = ["sgx"] } +lc-parachain-extrinsic-task-sender = { path = "../../common/litentry/core/parachain-extrinsic-task/sender", default-features = false, features = ["sgx"] } lc-stf-task-receiver = { path = "../litentry/core/stf-task/receiver", default-features = false, features = ["sgx"] } lc-vc-task-receiver = { path = "../litentry/core/vc-task/receiver", default-features = false, features = ["sgx"] } litentry-hex-utils = { path = "../../../common/utils/hex", default-features = false } litentry-macros = { path = "../../../common/primitives/core/macros", default-features = false } litentry-primitives = { path = "../../common/litentry/primitives", default-features = false, features = ["sgx"] } litentry-proc-macros = { path = "../../../common/primitives/core/proc-macros", default-features = false } +pallet-identity-management-tee = { path = "../../common/litentry/pallets/identity-management", default-features = false } # substrate deps frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } diff --git a/tee-worker/identity/enclave-runtime/Enclave.edl b/tee-worker/identity/enclave-runtime/Enclave.edl index 3e4a13e34f..94c1be5f1c 100644 --- a/tee-worker/identity/enclave-runtime/Enclave.edl +++ b/tee-worker/identity/enclave-runtime/Enclave.edl @@ -170,6 +170,8 @@ enclave { uint32_t shard_size ); + public sgx_status_t upload_id_graph(); + public sgx_status_t ignore_parentchain_block_import_validation_until( [in] uint32_t* until ); diff --git a/tee-worker/identity/enclave-runtime/src/initialization/global_components.rs b/tee-worker/identity/enclave-runtime/src/initialization/global_components.rs index 5255d5303e..e72d54b425 100644 --- a/tee-worker/identity/enclave-runtime/src/initialization/global_components.rs +++ b/tee-worker/identity/enclave-runtime/src/initialization/global_components.rs @@ -63,7 +63,10 @@ use itp_node_api::{ metadata::{provider::NodeMetadataRepository, NodeMetadata}, }; use itp_nonce_cache::NonceCache; -use itp_sgx_crypto::{key_repository::KeyRepository, Aes, AesSeal, Ed25519Seal, Rsa3072Seal}; +use itp_sgx_crypto::{ + aes256::sgx::Seal as Aes256Seal, key_repository::KeyRepository, Aes, Aes256Key, AesSeal, + Ed25519Seal, Rsa3072Seal, +}; use itp_stf_executor::{ enclave_signer::StfEnclaveSigner, executor::StfExecutor, getter_executor::GetterExecutor, state_getter::StfStateGetter, @@ -106,6 +109,7 @@ pub type EnclaveGetter = Getter; pub type EnclaveTrustedCallSigned = TrustedCallSigned; pub type EnclaveStf = Stf; pub type EnclaveStateKeyRepository = KeyRepository; +pub type EnclaveAccountStoreKeyRepository = KeyRepository; pub type EnclaveShieldingKeyRepository = KeyRepository; pub type EnclaveSigningKeyRepository = KeyRepository; pub type EnclaveStateFileIo = SgxStateFileIo; @@ -371,6 +375,11 @@ pub type EnclaveOffchainWorkerExecutor = itc_offchain_worker_executor::executor: pub static GLOBAL_STATE_KEY_REPOSITORY_COMPONENT: ComponentContainer = ComponentContainer::new("State key repository"); +/// IDGraph key repository +pub static GLOBAL_ACCOUNT_STORE_KEY_REPOSITORY_COMPONENT: ComponentContainer< + EnclaveAccountStoreKeyRepository, +> = ComponentContainer::new("StoreAccount key repository"); + /// Shielding key repository pub static GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT: ComponentContainer< EnclaveShieldingKeyRepository, diff --git a/tee-worker/identity/enclave-runtime/src/initialization/mod.rs b/tee-worker/identity/enclave-runtime/src/initialization/mod.rs index 843373742f..263ae6b0b3 100644 --- a/tee-worker/identity/enclave-runtime/src/initialization/mod.rs +++ b/tee-worker/identity/enclave-runtime/src/initialization/mod.rs @@ -26,16 +26,16 @@ use crate::{ EnclaveSidechainBlockSyncer, EnclaveStateFileIo, EnclaveStateHandler, EnclaveStateInitializer, EnclaveStateObserver, EnclaveStateSnapshotRepository, EnclaveStfEnclaveSigner, EnclaveTopPool, EnclaveTopPoolAuthor, - DIRECT_RPC_REQUEST_SINK_COMPONENT, GLOBAL_ASSERTION_REPOSITORY, - GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_DATA_PROVIDER_CONFIG, - GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_RPC_WS_HANDLER_COMPONENT, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, - GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, - GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, - GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, + DIRECT_RPC_REQUEST_SINK_COMPONENT, GLOBAL_ACCOUNT_STORE_KEY_REPOSITORY_COMPONENT, + GLOBAL_ASSERTION_REPOSITORY, GLOBAL_ATTESTATION_HANDLER_COMPONENT, + GLOBAL_DATA_PROVIDER_CONFIG, GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT, + GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, + GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, GLOBAL_WEB_SOCKET_SERVER_COMPONENT, }, @@ -52,7 +52,8 @@ use crate::{ use base58::ToBase58; use codec::Encode; use core::str::FromStr; -use ita_stf::{Getter, TrustedCallSigned}; +use ita_sgx_runtime::Runtime; +use ita_stf::{aes_encrypt_default, Getter, TrustedCallSigned}; use itc_direct_rpc_server::{ create_determine_watch, rpc_connection_registry::ConnectionRegistry, rpc_ws_handler::RpcWsHandler, @@ -65,6 +66,11 @@ use itc_tls_websocket_server::{ }; use itp_attestation_handler::IntelAttestationHandler; use itp_component_container::{ComponentGetter, ComponentInitializer}; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_node_api::metadata::{ + pallet_omni_account::OmniAccountCallIndexes, provider::AccessNodeMetadata, +}; +use itp_ocall_api::EnclaveOnChainOCallApi; use itp_primitives_cache::GLOBAL_PRIMITIVES_CACHE; use itp_settings::files::{ ASSERTIONS_FILE, LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, STATE_SNAPSHOTS_CACHE_SIZE, @@ -73,6 +79,7 @@ use itp_settings::files::{ use itp_sgx_crypto::{ get_aes_repository, get_ed25519_repository, get_rsa3072_repository, key_repository::AccessKey, }; +use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_state_handler::{ file_io::StateDir, handle_state::HandleState, query_shard_state::QueryShardState, state_snapshot_repository::VersionedStateAccess, @@ -80,7 +87,7 @@ use itp_stf_state_handler::{ }; use itp_top_pool::pool::Options as PoolOptions; use itp_top_pool_author::author::{AuthorTopFilter, BroadcastedTopFilter}; -use itp_types::{parentchain::ParentchainId, ShardIdentifier}; +use itp_types::{parentchain::ParentchainId, OpaqueCall, ShardIdentifier}; use its_sidechain::{ block_composer::BlockComposer, slots::{FailSlotMode, FailSlotOnDemand}, @@ -92,11 +99,13 @@ use lc_native_task_receiver::{run_native_task_receiver, NativeTaskContext}; use lc_parachain_extrinsic_task_receiver::run_parachain_extrinsic_task_receiver; use lc_stf_task_receiver::{run_stf_task_receiver, StfTaskContext}; use lc_vc_task_receiver::run_vc_handler_runner; -use litentry_primitives::BroadcastedRequest; +use litentry_primitives::{ + sgx::create_aes256_repository, BroadcastedRequest, Identity, MemberAccount, +}; use log::*; use sgx_types::sgx_status_t; use sp_core::crypto::Pair; -use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc}; +use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc, vec::Vec}; pub(crate) fn init_enclave( mu_ra_url: String, @@ -116,6 +125,10 @@ pub(crate) fn init_enclave( let state_key_repository = Arc::new(get_aes_repository(base_dir.clone())?); GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.initialize(state_key_repository.clone()); + let account_store_key_repository = + Arc::new(create_aes256_repository(base_dir.clone(), "account_store", None)?); + GLOBAL_ACCOUNT_STORE_KEY_REPOSITORY_COMPONENT.initialize(account_store_key_repository); + let integritee_light_client_seal = Arc::new(EnclaveLightClientSeal::new( base_dir.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), ParentchainId::Litentry, @@ -510,6 +523,78 @@ pub(crate) fn migrate_shard(new_shard: ShardIdentifier) -> EnclaveResult<()> { Ok(()) } +pub(crate) fn upload_id_graph() -> EnclaveResult<()> { + const BATCH_SIZE: usize = 400; + + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let extrinsic_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + + let aes_key = GLOBAL_ACCOUNT_STORE_KEY_REPOSITORY_COMPONENT + .get() + .and_then(|r| { + r.retrieve_key() + .map_err(|_| itp_component_container::error::Error::Other("".into())) + }) + .map_err(|e| Error::Other(e.into()))?; + + let call_index = node_metadata_repo + .get_from_metadata(|m| m.update_account_store_by_one_call_indexes())??; + + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + + let shard = match state_handler.list_shards()? { + shards if shards.len() == 1 => + *shards.get(0).ok_or_else(|| Error::Other("Shard len unexpected".into()))?, + _ => return Err(Error::Other("Cannot get shard".into())), + }; + + let identities: Vec<(Identity, Identity)> = match state_handler.load_cloned(&shard) { + Ok((mut state, _)) => state.execute_with(|| { + pallet_identity_management_tee::IDGraphs::::iter_keys().collect() + }), + Err(e) => return Err(Error::Other(e.into())), + }; + + info!("uploading {} identity pairs", identities.len()); + + let mut calls: Vec = Default::default(); + for (prime_id, sub_id) in identities { + let member_account: MemberAccount = if prime_id == sub_id { + sub_id.into() + } else { + let enc_id: Vec = sub_id.encode(); + MemberAccount::Private(aes_encrypt_default(&aes_key, &enc_id).encode(), sub_id.hash()) + }; + + let call = OpaqueCall::from_tuple(&(call_index, prime_id, member_account)); + calls.push(call); + + if calls.len() >= BATCH_SIZE { + extrinsic_factory + .create_batch_extrinsic(calls.drain(..).collect(), None) + .map_err(|_| Error::Other("failed to create extrinsic".into())) + .and_then(|ext| { + ocall_api + .send_to_parentchain(vec![ext], &ParentchainId::Litentry, true) + .map_err(|_| Error::Other("failed to send extrinsic".into())) + })?; + } + } + + if !calls.is_empty() { + extrinsic_factory + .create_batch_extrinsic(calls.drain(..).collect(), None) + .map_err(|_| Error::Other("failed to create extrinsic".into())) + .and_then(|ext| { + ocall_api + .send_to_parentchain(vec![ext], &ParentchainId::Litentry, true) + .map_err(|_| Error::Other("failed to send extrinsic".into())) + })?; + } + Ok(()) +} + /// Initialize the TOP pool author component. pub fn create_top_pool_author( rpc_responder: Arc, diff --git a/tee-worker/identity/enclave-runtime/src/lib.rs b/tee-worker/identity/enclave-runtime/src/lib.rs index 9531f372da..eec659a6b3 100644 --- a/tee-worker/identity/enclave-runtime/src/lib.rs +++ b/tee-worker/identity/enclave-runtime/src/lib.rs @@ -440,6 +440,16 @@ pub unsafe extern "C" fn migrate_shard(new_shard: *const u8, shard_size: u32) -> sgx_status_t::SGX_SUCCESS } +#[no_mangle] +pub unsafe extern "C" fn upload_id_graph() -> sgx_status_t { + if let Err(e) = initialization::upload_id_graph() { + error!("Failed to upload IDGraph: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + #[no_mangle] pub unsafe extern "C" fn sync_parentchain( blocks_to_sync: *const u8, diff --git a/tee-worker/identity/enclave-runtime/src/test/tests_main.rs b/tee-worker/identity/enclave-runtime/src/test/tests_main.rs index 0a61660650..0dacfbfce0 100644 --- a/tee-worker/identity/enclave-runtime/src/test/tests_main.rs +++ b/tee-worker/identity/enclave-runtime/src/test/tests_main.rs @@ -90,6 +90,8 @@ pub extern "C" fn test_main_entrance() -> size_t { itp_stf_state_handler::test::sgx_tests::test_list_state_ids_ignores_files_not_matching_the_pattern, itp_stf_state_handler::test::sgx_tests::test_in_memory_state_initializes_from_shard_directory, itp_sgx_crypto::tests::aes_sealing_works, + itp_sgx_crypto::tests::aes256_creating_repository_with_same_path_and_prefix_results_in_same_key, + itp_sgx_crypto::tests::aes256_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key, itp_sgx_crypto::tests::using_get_aes_repository_twice_initializes_key_only_once, itp_sgx_crypto::tests::ed25529_sealing_works, itp_sgx_crypto::tests::using_get_ed25519_repository_twice_initializes_key_only_once, diff --git a/tee-worker/identity/service/src/cli.yml b/tee-worker/identity/service/src/cli.yml index 2b33dd20a5..9d31cf53a2 100644 --- a/tee-worker/identity/service/src/cli.yml +++ b/tee-worker/identity/service/src/cli.yml @@ -176,7 +176,7 @@ subcommands: - mrenclave: about: Dump mrenclave to stdout. base58 encoded. - init-shard: - about: Initialize new shard (do this only if you run the first worker for that shard). if shard is not specified, the MRENCLAVE is used instead + about: Initialize new shard (do this only if you run the first worker for that shard). Default is mrenclave args: - shard: required: false @@ -185,6 +185,13 @@ subcommands: help: shard identifier base58 encoded - migrate-shard: about: Migrate state from old shards to the new(current) shard, which is identical to mrenclave + - upload-id-graph: + about: Upload the existing IDGraph in the sidechain state onto parachain + args: + - shard: + long: shard + required: false + help: shard identifier base58 encoded. Defines the state that this worker shall operate on. Default is mrenclave - test: about: Run tests involving the enclave takes_value: true diff --git a/tee-worker/identity/service/src/main_impl.rs b/tee-worker/identity/service/src/main_impl.rs index 137e0e90f8..e5f9c0dcc4 100644 --- a/tee-worker/identity/service/src/main_impl.rs +++ b/tee-worker/identity/service/src/main_impl.rs @@ -291,6 +291,14 @@ pub(crate) fn main() { setup::migrate_shard(enclave.as_ref(), &new_shard); let new_shard_name = new_shard.encode().to_base58(); setup::remove_old_shards(config.data_dir(), &new_shard_name); + } else if let Some(sub_matches) = matches.subcommand_matches("upload-id-graph") { + let tee_accountid = enclave_account(enclave.as_ref()); + let shard = extract_shard(sub_matches.value_of("shard"), enclave.as_ref()); + info!("shard is {:?}", shard); + let node_api = + node_api_factory.create_api().expect("Failed to create parentchain node API"); + init_parentchain(&enclave, &node_api, &tee_accountid, ParentchainId::Litentry, &shard); + enclave.upload_id_graph(); } else { info!("For options: use --help"); } diff --git a/tee-worker/identity/service/src/tests/mocks/enclave_api_mock.rs b/tee-worker/identity/service/src/tests/mocks/enclave_api_mock.rs index fc194e4f63..ef500df115 100644 --- a/tee-worker/identity/service/src/tests/mocks/enclave_api_mock.rs +++ b/tee-worker/identity/service/src/tests/mocks/enclave_api_mock.rs @@ -103,6 +103,10 @@ impl EnclaveBase for EnclaveMock { fn migrate_shard(&self, new_shard: Vec) -> EnclaveResult<()> { unimplemented!() } + + fn upload_id_graph(&self) -> EnclaveResult<()> { + unimplemented!() + } } impl Sidechain for EnclaveMock {