From 22c8e332d169723ced70811b08ac8cce5a4d0575 Mon Sep 17 00:00:00 2001 From: SW van Heerden Date: Fri, 12 Jul 2024 12:12:31 +0200 Subject: [PATCH] feat: simplify leader sig generation (#6399) Description --- Simplify the key generation of the leader in m-of-n party Motivation and Context --- This will allow us to only have to implement 1 function on the ledger for key generation of the m-n scripts How Has This Been Tested? --- --- .../get_script_signature_from_challenge.rs | 81 -------- .../minotari_ledger_wallet/wallet/src/main.rs | 2 - .../src/transactions/key_manager/inner.rs | 181 ++++-------------- .../src/transactions/key_manager/interface.rs | 15 +- .../src/transactions/key_manager/wrapper.rs | 26 +-- .../transaction_components/wallet_output.rs | 65 +++---- .../wallet_output_builder.rs | 21 +- .../transaction_protocol/sender.rs | 4 - .../src/output_manager_service/service.rs | 1 - base_layer/wallet_ffi/wallet.h | 2 +- 10 files changed, 85 insertions(+), 313 deletions(-) delete mode 100644 applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature_from_challenge.rs diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature_from_challenge.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature_from_challenge.rs deleted file mode 100644 index 5234a10647..0000000000 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature_from_challenge.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2024 The Tari Project -// SPDX-License-Identifier: BSD-3-Clause - -use alloc::format; - -use ledger_device_sdk::{io::Comm, ui::gadgets::SingleMessage}; -use tari_crypto::{ - keys::PublicKey, - ristretto::{ - pedersen::{extended_commitment_factory::ExtendedPedersenCommitmentFactory, PedersenCommitment}, - RistrettoComAndPubSig, - RistrettoPublicKey, - RistrettoSecretKey, - }, -}; -use zeroize::Zeroizing; - -use crate::{ - alloc::string::ToString, - utils::{alpha_hasher, derive_from_bip32_key, get_key_from_canonical_bytes}, - AppSW, - KeyType, - RESPONSE_VERSION, - STATIC_ALPHA_INDEX, -}; - -pub fn handler_get_script_signature_from_challenge(comm: &mut Comm) -> Result<(), AppSW> { - let data = comm.get_data().map_err(|_| AppSW::WrongApduLength)?; - - let mut account_bytes = [0u8; 8]; - account_bytes.clone_from_slice(&data[0..8]); - let account = u64::from_le_bytes(account_bytes); - - let alpha = derive_from_bip32_key(account, STATIC_ALPHA_INDEX, KeyType::Alpha)?; - let blinding_factor: Zeroizing = - get_key_from_canonical_bytes::(&data[8..40])?.into(); - let script_private_key = alpha_hasher(alpha, blinding_factor)?; - let _script_public_key = RistrettoPublicKey::from_secret_key(&script_private_key); - - let value: Zeroizing = - get_key_from_canonical_bytes::(&data[40..72])?.into(); - let spend_private_key: Zeroizing = - get_key_from_canonical_bytes::(&data[72..104])?.into(); - - let _commitment: PedersenCommitment = get_key_from_canonical_bytes(&data[104..136])?; - - let mut challenge = [0u8; 64]; - challenge.clone_from_slice(&data[136..200]); - - let r_a: Zeroizing = - get_key_from_canonical_bytes::(&data[200..232])?.into(); - let r_x: Zeroizing = - get_key_from_canonical_bytes::(&data[232..264])?.into(); - let r_y: Zeroizing = - get_key_from_canonical_bytes::(&data[264..296])?.into(); - - let factory = ExtendedPedersenCommitmentFactory::default(); - - let script_signature = match RistrettoComAndPubSig::sign( - &value, - &spend_private_key, - &script_private_key, - &r_a, - &r_x, - &r_y, - &challenge, - &factory, - ) { - Ok(sig) => sig, - Err(e) => { - SingleMessage::new(&format!("Signing error: {:?}", e.to_string())).show_and_wait(); - return Err(AppSW::ScriptSignatureFail); - }, - }; - - comm.append(&[RESPONSE_VERSION]); // version - comm.append(&script_signature.to_vec()); - comm.reply_ok(); - - Ok(()) -} diff --git a/applications/minotari_ledger_wallet/wallet/src/main.rs b/applications/minotari_ledger_wallet/wallet/src/main.rs index 87005a7fbc..19e37ef3d1 100644 --- a/applications/minotari_ledger_wallet/wallet/src/main.rs +++ b/applications/minotari_ledger_wallet/wallet/src/main.rs @@ -20,7 +20,6 @@ mod handlers { pub mod get_public_key; pub mod get_script_offset; pub mod get_script_signature; - pub mod get_script_signature_from_challenge; pub mod get_version; pub mod get_view_key; } @@ -120,7 +119,6 @@ pub enum Instruction { GetScriptSignature, GetScriptOffset { chunk: u8, more: bool }, GetMetadataSignature, - GetScriptSignatureFromChallenge, GetViewKey, GetDHSharedSecret, } diff --git a/base_layer/core/src/transactions/key_manager/inner.rs b/base_layer/core/src/transactions/key_manager/inner.rs index 045af0d275..9dfed11033 100644 --- a/base_layer/core/src/transactions/key_manager/inner.rs +++ b/base_layer/core/src/transactions/key_manager/inner.rs @@ -793,106 +793,40 @@ where TBackend: KeyManagerBackend + 'static } } - pub async fn get_script_signature_from_challenge( + pub async fn get_partial_script_signature( &self, - script_key_id: &TariKeyId, - spend_key_id: &TariKeyId, + commitment_mask_id: &TariKeyId, value: &PrivateKey, - challenge: &[u8; 64], - r_a: &PrivateKey, - r_x: &PrivateKey, - r_y: &PrivateKey, + txi_version: &TransactionInputVersion, + ephemeral_pubkey: &PublicKey, + script_public_key: &PublicKey, + script_message: &[u8; 32], ) -> Result { - let spend_private_key = self.get_private_key(spend_key_id).await?; - - #[allow(unused_variables)] // When ledger isn't enabled - match (&self.wallet_type, script_key_id) { - ( - WalletType::Ledger(ledger), - KeyId::Derived { - branch, - label: _, - index, - }, - ) => { - #[cfg(not(feature = "ledger"))] - { - Err(TransactionError::LedgerNotSupported) - } - - #[cfg(feature = "ledger")] - { - let km = self - .key_managers - .get(branch) - .ok_or(KeyManagerServiceError::UnknownKeyBranch)? - .read() - .await; - let branch_key = km - .get_private_key(*index) - .map_err(|e| TransactionError::KeyManagerError(e.to_string()))?; - - let mut data = branch_key.as_bytes().to_vec(); - data.extend_from_slice(value.as_bytes()); - data.extend_from_slice(spend_private_key.as_bytes()); - data.extend_from_slice(challenge); - data.extend_from_slice(r_a.as_bytes()); - data.extend_from_slice(r_x.as_bytes()); - data.extend_from_slice(r_y.as_bytes()); - - let command = ledger.build_command(Instruction::GetScriptSignatureFromChallenge, data); - let transport = get_transport()?; - - match command.execute_with_transport(&transport) { - Ok(result) => { - if result.data().len() < 161 { - debug!(target: LOG_TARGET, "result less than 161"); - return Err(LedgerDeviceError::Processing(format!( - "'get_script_signature' insufficient data - expected 161 got {} bytes ({:?})", - result.data().len(), - result - )) - .into()); - } - let data = result.data(); - debug!(target: LOG_TARGET, "result length: {}, data: {:?}", result.data().len(), result.data()); - Ok(ComAndPubSignature::new( - Commitment::from_canonical_bytes(&data[1..33]) - .map_err(|e| TransactionError::InvalidSignatureError(e.to_string()))?, - PublicKey::from_canonical_bytes(&data[33..65]) - .map_err(|e| TransactionError::InvalidSignatureError(e.to_string()))?, - PrivateKey::from_canonical_bytes(&data[65..97]) - .map_err(|e| TransactionError::InvalidSignatureError(e.to_string()))?, - PrivateKey::from_canonical_bytes(&data[97..129]) - .map_err(|e| TransactionError::InvalidSignatureError(e.to_string()))?, - PrivateKey::from_canonical_bytes(&data[129..161]) - .map_err(|e| TransactionError::InvalidSignatureError(e.to_string()))?, - )) - }, - Err(e) => Err(LedgerDeviceError::Instruction(format!( - "GetScriptSignatureFromChallenge: {}", - e - )) - .into()), - } - } - }, - (_, _) => { - let script_private_key = self.get_private_key(script_key_id).await?; + let private_commitment_mask = self.get_private_key(commitment_mask_id).await?; + let commitment = self.get_commitment(commitment_mask_id, value).await?; + let r_a = PrivateKey::random(&mut OsRng); + let r_x = PrivateKey::random(&mut OsRng); + let ephemeral_commitment = self.crypto_factories.commitment.commit(&r_x, &r_a); + let challenge = TransactionInput::finalize_script_signature_challenge( + txi_version, + &ephemeral_commitment, + ephemeral_pubkey, + script_public_key, + &commitment, + script_message, + ); - let script_signature = ComAndPubSignature::sign( - value, - &spend_private_key, - &script_private_key, - r_a, - r_x, - r_y, - challenge.as_slice(), - &*self.crypto_factories.commitment, - )?; - Ok(script_signature) - }, - } + let script_signature = ComAndPubSignature::sign( + value, + &private_commitment_mask, + &PrivateKey::default(), + &r_a, + &r_x, + &PrivateKey::default(), + &challenge, + &*self.crypto_factories.commitment, + )?; + Ok(script_signature) } // ----------------------------------------------------------------------------------------------------------------- @@ -1124,47 +1058,6 @@ where TBackend: KeyManagerBackend + 'static Ok(self.crypto_factories.commitment.commit(&nonce_b, &nonce_a)) } - pub async fn get_metadata_signature_raw( - &self, - spending_key_id: &TariKeyId, - value_as_private_key: &PrivateKey, - ephemeral_private_nonce_id: &TariKeyId, - sender_offset_key_id: &TariKeyId, - ephemeral_pubkey: &PublicKey, - ephemeral_commitment: &Commitment, - txo_version: &TransactionOutputVersion, - metadata_signature_message: &[u8; 32], - range_proof_type: RangeProofType, - ) -> Result { - let sender_offset_public_key = self.get_public_key_at_key_id(sender_offset_key_id).await?; - let receiver_partial_metadata_signature = self - .get_receiver_partial_metadata_signature( - spending_key_id, - value_as_private_key, - &sender_offset_public_key, - ephemeral_pubkey, - txo_version, - metadata_signature_message, - range_proof_type, - ) - .await?; - let commitment = self.get_commitment(spending_key_id, value_as_private_key).await?; - let sender_partial_metadata_signature = self - .get_sender_partial_metadata_signature( - ephemeral_private_nonce_id, - sender_offset_key_id, - &commitment, - ephemeral_commitment, - txo_version, - None, - None, - metadata_signature_message, - ) - .await?; - let metadata_signature = &receiver_partial_metadata_signature + &sender_partial_metadata_signature; - Ok(metadata_signature) - } - pub async fn sign_script_message( &self, private_key_id: &TariKeyId, @@ -1223,8 +1116,6 @@ where TBackend: KeyManagerBackend + 'static &commitment, ephemeral_commitment, txo_version, - None, - None, metadata_signature_message, ) .await?; @@ -1283,23 +1174,15 @@ where TBackend: KeyManagerBackend + 'static commitment: &Commitment, ephemeral_commitment: &Commitment, txo_version: &TransactionOutputVersion, - aggregated_sender_offset_public_keys: Option<&PublicKey>, - aggregated_ephemeral_public_keys: Option<&PublicKey>, metadata_signature_message: &[u8; 32], ) -> Result { match &self.wallet_type { WalletType::Software => { let ephemeral_private_key = self.get_private_key(ephemeral_private_nonce_id).await?; - let ephemeral_pubkey = match aggregated_ephemeral_public_keys { - Some(agg) => agg.clone(), - None => PublicKey::from_secret_key(&ephemeral_private_key), - }; + let ephemeral_pubkey = PublicKey::from_secret_key(&ephemeral_private_key); PublicKey::from_secret_key(&ephemeral_private_key); let sender_offset_private_key = self.get_private_key(sender_offset_key_id).await?; // Take the index and use it to find the key from ledger - let sender_offset_public_key = match aggregated_sender_offset_public_keys { - Some(agg) => agg.clone(), - None => PublicKey::from_secret_key(&sender_offset_private_key), - }; + let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); let challenge = TransactionOutput::finalize_metadata_signature_challenge( txo_version, diff --git a/base_layer/core/src/transactions/key_manager/interface.rs b/base_layer/core/src/transactions/key_manager/interface.rs index f9dd47d09b..c7b1eeed8a 100644 --- a/base_layer/core/src/transactions/key_manager/interface.rs +++ b/base_layer/core/src/transactions/key_manager/interface.rs @@ -189,15 +189,14 @@ pub trait TransactionKeyManagerInterface: KeyManagerInterface { script_message: &[u8; 32], ) -> Result; - async fn get_script_signature_from_challenge( + async fn get_partial_script_signature( &self, - script_key_id: &TariKeyId, - spend_key_id: &TariKeyId, + commitment_mask_id: &TariKeyId, value: &PrivateKey, - challenge: &[u8; 64], - r_a: &PrivateKey, - r_x: &PrivateKey, - r_y: &PrivateKey, + txi_version: &TransactionInputVersion, + ephemeral_pubkey: &PublicKey, + script_public_key: &PublicKey, + script_message: &[u8; 32], ) -> Result; async fn get_partial_txo_kernel_signature( @@ -296,8 +295,6 @@ pub trait TransactionKeyManagerInterface: KeyManagerInterface { commitment: &Commitment, ephemeral_commitment: &Commitment, txo_version: &TransactionOutputVersion, - aggregated_sender_offset_public_keys: Option<&PublicKey>, - aggregated_ephemeral_public_keys: Option<&PublicKey>, metadata_signature_message: &[u8; 32], ) -> Result; diff --git a/base_layer/core/src/transactions/key_manager/wrapper.rs b/base_layer/core/src/transactions/key_manager/wrapper.rs index d023613783..8e7a8b959d 100644 --- a/base_layer/core/src/transactions/key_manager/wrapper.rs +++ b/base_layer/core/src/transactions/key_manager/wrapper.rs @@ -301,20 +301,26 @@ where TBackend: KeyManagerBackend + 'static .await } - async fn get_script_signature_from_challenge( + async fn get_partial_script_signature( &self, - script_key_id: &TariKeyId, - spend_key_id: &TariKeyId, + commitment_mask_id: &TariKeyId, value: &PrivateKey, - challenge: &[u8; 64], - r_a: &PrivateKey, - r_x: &PrivateKey, - r_y: &PrivateKey, + txi_version: &TransactionInputVersion, + ephemeral_pubkey: &PublicKey, + script_public_key: &PublicKey, + script_message: &[u8; 32], ) -> Result { self.transaction_key_manager_inner .read() .await - .get_script_signature_from_challenge(script_key_id, spend_key_id, value, challenge, r_a, r_x, r_y) + .get_partial_script_signature( + commitment_mask_id, + value, + txi_version, + ephemeral_pubkey, + script_public_key, + script_message, + ) .await } @@ -502,8 +508,6 @@ where TBackend: KeyManagerBackend + 'static commitment: &Commitment, ephemeral_commitment: &Commitment, txo_version: &TransactionOutputVersion, - aggregated_sender_offset_public_keys: Option<&PublicKey>, - aggregated_ephemeral_public_keys: Option<&PublicKey>, metadata_signature_message: &[u8; 32], ) -> Result { self.transaction_key_manager_inner @@ -515,8 +519,6 @@ where TBackend: KeyManagerBackend + 'static commitment, ephemeral_commitment, txo_version, - aggregated_sender_offset_public_keys, - aggregated_ephemeral_public_keys, metadata_signature_message, ) .await diff --git a/base_layer/core/src/transactions/transaction_components/wallet_output.rs b/base_layer/core/src/transactions/transaction_components/wallet_output.rs index 86173ca5e8..a47d7011ae 100644 --- a/base_layer/core/src/transactions/transaction_components/wallet_output.rs +++ b/base_layer/core/src/transactions/transaction_components/wallet_output.rs @@ -28,21 +28,8 @@ use std::{ fmt::{Debug, Formatter}, }; -use rand::rngs::OsRng; use serde::{Deserialize, Serialize}; -use tari_common_types::types::{ - ComAndPubSignature, - Commitment, - CommitmentFactory, - FixedHash, - PrivateKey, - PublicKey, - RangeProof, -}; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PK, SecretKey}, -}; +use tari_common_types::types::{ComAndPubSignature, Commitment, FixedHash, PublicKey, RangeProof}; use tari_script::{ExecutionStack, TariScript}; use super::TransactionOutputVersion; @@ -256,49 +243,43 @@ impl WalletOutput { /// `script_signature_public_nonces` and `script_public_key_shares` exclude the caller's data. pub async fn to_transaction_input_with_multi_party_script_signature( &self, - factory: &CommitmentFactory, aggregated_script_signature_public_nonces: &PublicKey, aggregated_script_public_key_shares: &PublicKey, key_manager: &KM, ) -> Result<(TransactionInput, PublicKey), TransactionError> { let value = self.value.into(); - let commitment = key_manager.get_commitment(&self.spending_key_id, &value).await?; let version = TransactionInputVersion::get_current_version(); + let commitment = key_manager.get_commitment(&self.spending_key_id, &value).await?; - // TODO this is bad and insecure, we need to fix this, the nonces should live in the key manager and or only in - // the ledger. - let r_a = PrivateKey::random(&mut OsRng); - let r_x = PrivateKey::random(&mut OsRng); - let ephemeral_commitment = factory.commit(&r_x, &r_a); - - let r_y = PrivateKey::random(&mut OsRng); - let ephemeral_public_key_self = PublicKey::from_secret_key(&r_y); - - let ephemeral_public_key = aggregated_script_signature_public_nonces + ephemeral_public_key_self; - + let message = TransactionInput::build_script_signature_message(&version, &self.script, &self.input_data); + let (ephemeral_public_key_id, ephemeral_public_key_self) = key_manager.get_random_key().await?; let script_public_key_self = key_manager.get_public_key_at_key_id(&self.script_key_id).await?; let script_public_key = aggregated_script_public_key_shares + script_public_key_self; - let challenge = TransactionInput::build_script_signature_challenge( + let total_ephemeral_public_key = aggregated_script_signature_public_nonces + ephemeral_public_key_self; + let commitment_partial_script_signature = key_manager + .get_partial_script_signature( + &self.spending_key_id, + &value, + &version, + &total_ephemeral_public_key, + &script_public_key, + &message, + ) + .await?; + let challenge = TransactionInput::finalize_script_signature_challenge( &version, - &ephemeral_commitment, - &ephemeral_public_key, - &self.script, - &self.input_data, + commitment_partial_script_signature.ephemeral_commitment(), + &total_ephemeral_public_key, &script_public_key, &commitment, + &message, ); - let script_signature = key_manager - .get_script_signature_from_challenge( - &self.script_key_id, - &self.spending_key_id, - &value, - &challenge, - &r_a, - &r_x, - &r_y, - ) + let script_key_partial_script_signature = key_manager + .sign_with_nonce_and_message(&self.script_key_id, &ephemeral_public_key_id, &challenge) .await?; + let script_signature = &commitment_partial_script_signature + &script_key_partial_script_signature; + let input = TransactionInput::new_current_version( SpentOutput::OutputData { features: self.features.clone(), diff --git a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs index 083afd3eaa..58e192d079 100644 --- a/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/wallet_output_builder.rs @@ -247,17 +247,16 @@ impl WalletOutputBuilder { .get_commitment(&self.spending_key_id, &self.value.into()) .await?; let ephemeral_commitment = receiver_partial_metadata_signature.ephemeral_commitment(); + let challenge = TransactionOutput::finalize_metadata_signature_challenge( + &TransactionOutputVersion::get_current_version(), + &aggregate_sender_offset_public_key, + ephemeral_commitment, + &aggregate_ephemeral_pubkey, + &commitment, + &metadata_message, + ); let sender_partial_metadata_signature_self = key_manager - .get_sender_partial_metadata_signature( - &ephemeral_private_nonce_id, - sender_offset_key_id, - &commitment, - ephemeral_commitment, - &TransactionOutputVersion::get_current_version(), - Some(&aggregate_sender_offset_public_key), - Some(&aggregate_ephemeral_pubkey), - &metadata_message, - ) + .sign_with_nonce_and_message(sender_offset_key_id, &ephemeral_private_nonce_id, &challenge) .await?; let metadata_signature = &receiver_partial_metadata_signature + &sender_partial_metadata_signature_self; @@ -417,8 +416,6 @@ mod test { &commitment, receiver_metadata_signature.ephemeral_commitment(), &wallet_output.version, - None, - None, &metadata_message, ) .await diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index 000b9f3c05..04b58100ee 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -558,8 +558,6 @@ impl SenderTransactionProtocol { &received_output.commitment, received_output.metadata_signature.ephemeral_commitment(), &version, - None, - None, &metadata_message, ) .await?; @@ -1020,8 +1018,6 @@ mod test { &output.commitment, partial_metadata_signature.ephemeral_commitment(), &txo_version, - None, - None, &metadata_message, ) .await diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index aefd16e3e0..28e44cade9 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1471,7 +1471,6 @@ where // Update the input's script signature let (updated_input, total_script_public_key) = input .to_transaction_input_with_multi_party_script_signature( - &self.resources.factories.commitment, &aggregated_script_signature_public_nonces, &aggregated_script_public_key_shares, &self.resources.key_manager, diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index 321b19a998..576a0cc4bf 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -640,7 +640,7 @@ struct ByteVector *public_key_get_bytes(TariPublicKey *pk, int *error_out); /** - * Converts public key to emoji encding + * Converts public key to emoji encoding * * ## Arguments * `pk` - The pointer to a TariPublicKey