diff --git a/tee-worker/app-libs/stf/src/helpers.rs b/tee-worker/app-libs/stf/src/helpers.rs index 4842f066e7..b0f75e4eb8 100644 --- a/tee-worker/app-libs/stf/src/helpers.rs +++ b/tee-worker/app-libs/stf/src/helpers.rs @@ -14,6 +14,7 @@ limitations under the License. */ + use crate::{Vec, ENCLAVE_ACCOUNT_KEY}; use codec::{Decode, Encode}; use frame_support::ensure; @@ -23,6 +24,7 @@ use itp_stf_primitives::error::{StfError, StfResult}; use itp_storage::{storage_double_map_key, storage_map_key, storage_value_key, StorageHasher}; use itp_types::Index; use itp_utils::stringify::account_id_to_string; +use litentry_hex_utils::hex_encode; use litentry_primitives::{ErrorDetail, Identity, Web3ValidationData}; use log::*; use sp_core::blake2_256; @@ -145,18 +147,34 @@ pub fn get_expected_raw_message( blake2_256(payload.as_slice()).to_vec() } +// [P-923] Verify a web3 identity +// This function validates the signature with both the raw message and its prettified format. Any of them is valid. +// The prettified version was introduced to extend the support for utf-8 signatures for browser wallets that +// do not play well with raw bytes signing, like Solana's Phantom and OKX wallets. +// +// The prettified format is the raw message with a prefix "Token: ". pub fn verify_web3_identity( identity: &Identity, - raw_msg: &[u8], - data: &Web3ValidationData, + expected_raw_msg: &[u8], + validation_data: &Web3ValidationData, ) -> StfResult<()> { + let mut expected_prettified_msg = hex_encode(expected_raw_msg); + expected_prettified_msg.insert_str(0, "Token: "); + + let expected_prettified_msg = expected_prettified_msg.as_bytes(); + + let received_message = validation_data.message().as_slice(); + ensure!( - raw_msg == data.message().as_slice(), + expected_raw_msg == received_message || expected_prettified_msg == received_message, StfError::LinkIdentityFailed(ErrorDetail::UnexpectedMessage) ); + let signature = validation_data.signature(); + ensure!( - data.signature().verify(raw_msg, identity), + signature.verify(expected_raw_msg, identity) + || signature.verify(expected_prettified_msg, identity), StfError::LinkIdentityFailed(ErrorDetail::VerifyWeb3SignatureFailed) ); diff --git a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs index dc53e6a9ab..5e287f8eca 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs @@ -103,12 +103,14 @@ impl TrustedCallSigned { .map_err(|_| StfError::LinkIdentityFailed(ErrorDetail::SendStfRequestFailed))?; Ok(false) }, - ValidationData::Web3(data) => { + ValidationData::Web3(validation_data) => { ensure!( identity.is_web3(), StfError::LinkIdentityFailed(ErrorDetail::InvalidIdentity) ); - verify_web3_identity(&identity, &raw_msg, &data)?; + + verify_web3_identity(&identity, &raw_msg, &validation_data)?; + Ok(true) }, } diff --git a/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts b/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts index 952793f587..35871fb8c2 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/identity-helper.ts @@ -14,13 +14,21 @@ export function generateVerificationMessage( context: IntegrationTestContext, signer: CorePrimitivesIdentity, identity: CorePrimitivesIdentity, - sidechainNonce: number -): HexString { + sidechainNonce: number, + options?: { prettifiedMessage?: boolean } +): string { + const _options = { prettifiedMessage: false, ...options }; const encodedIdentity = context.api.createType('CorePrimitivesIdentity', identity).toU8a(); const encodedWho = context.api.createType('CorePrimitivesIdentity', signer).toU8a(); const encodedSidechainNonce = context.api.createType('Index', sidechainNonce); const msg = Buffer.concat([encodedSidechainNonce.toU8a(), encodedWho, encodedIdentity]); - return blake2AsHex(msg, 256); + const hash = blake2AsHex(msg, 256); + + if (_options.prettifiedMessage) { + return `Token: ${hash}`; + } + + return hash; } export async function buildIdentityHelper( @@ -139,8 +147,10 @@ export async function buildValidations( linkIdentity: CorePrimitivesIdentity, startingSidechainNonce: number, network: 'ethereum' | 'substrate' | 'bitcoin' | 'solana', - signer?: Signer + signer?: Signer, + options?: { prettifiedMessage?: boolean } ): Promise { + const _options = { prettifiedMessage: false, ...options }; const validationNonce = startingSidechainNonce++; const msg = generateVerificationMessage(context, signerIdentitity, linkIdentity, validationNonce); @@ -148,7 +158,7 @@ export async function buildValidations( const evmValidationData = { Web3Validation: { Evm: { - message: '' as HexString, + message: '', signature: { Ethereum: '' as HexString, }, @@ -168,7 +178,7 @@ export async function buildValidations( const substrateValidationData = { Web3Validation: { Substrate: { - message: '' as HexString, + message: '', signature: { Sr25519: '' as HexString, }, @@ -187,7 +197,7 @@ export async function buildValidations( const bitcoinValidationData = { Web3Validation: { Bitcoin: { - message: '' as HexString, + message: '', signature: { Bitcoin: '' as HexString, }, @@ -206,7 +216,7 @@ export async function buildValidations( const solanaValidationData = { Web3Validation: { Solana: { - message: '' as HexString, + message: '', signature: { Ed25519: '' as HexString, }, diff --git a/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts index 50a83aa3aa..9df232d003 100644 --- a/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_bitcoin_identity.test.ts @@ -107,7 +107,8 @@ describe('Test Identity (bitcoin direct invocation)', function () { bobBitcoinIdentity, bobBitcoinNonce, 'bitcoin', - context.web3Wallets.bitcoin.Bob + context.web3Wallets.bitcoin.Bob, + { prettifiedMessage: true } ); const bobBitcoinNetowrks = context.api.createType('Vec', ['BitcoinP2tr']); linkIdentityRequestParams.push({ diff --git a/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts index babcbe012b..fc897e6a74 100644 --- a/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_evm_identity.test.ts @@ -79,7 +79,8 @@ describe('Test Identity (evm direct invocation)', function () { bobEvmIdentity, bobEvmNonce, 'ethereum', - context.web3Wallets.evm.Bob + context.web3Wallets.evm.Bob, + { prettifiedMessage: true } ); const bobEvmNetworks = context.api.createType('Vec', ['Ethereum', 'Bsc']); linkIdentityRequestParams.push({ diff --git a/tee-worker/ts-tests/integration-tests/di_solana_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_solana_identity.test.ts index af5034d3eb..f625b16858 100644 --- a/tee-worker/ts-tests/integration-tests/di_solana_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_solana_identity.test.ts @@ -77,7 +77,8 @@ describe('Test Identity (solana direct invocation)', function () { bobSolanaIdentity, bobSolanaNonce, 'solana', - context.web3Wallets.solana.Bob + context.web3Wallets.solana.Bob, + { prettifiedMessage: true } ); const bobSolanaNetworks = context.api.createType('Vec', ['Solana']); linkIdentityRequestParams.push({ diff --git a/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts b/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts index 75fa2bbe67..55fe4cc4b8 100644 --- a/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts +++ b/tee-worker/ts-tests/integration-tests/di_substrate_identity.test.ts @@ -134,7 +134,8 @@ describe('Test Identity (direct invocation)', function () { eveSubstrateIdentity, eveSubstrateNonce, 'substrate', - context.web3Wallets.substrate.Eve + context.web3Wallets.substrate.Eve, + { prettifiedMessage: true } ); const eveSubstrateNetworks = context.api.createType('Vec', ['Polkadot', 'Litentry']); linkIdentityRequestParams.push({