diff --git a/applications/minotari_ledger_wallet/common/src/lib.rs b/applications/minotari_ledger_wallet/common/src/lib.rs index a91df3f83f..1498d42449 100644 --- a/applications/minotari_ledger_wallet/common/src/lib.rs +++ b/applications/minotari_ledger_wallet/common/src/lib.rs @@ -11,7 +11,7 @@ extern crate alloc; pub mod common_types; mod utils; pub use utils::{ - get_public_spend_key_from_tari_dual_address, + get_public_spend_key_bytes_from_tari_dual_address, hex_to_bytes_serialized, tari_dual_address_display, PUSH_PUBKEY_IDENTIFIER, diff --git a/applications/minotari_ledger_wallet/common/src/utils.rs b/applications/minotari_ledger_wallet/common/src/utils.rs index 1b78f4bb19..ae5d6c379d 100644 --- a/applications/minotari_ledger_wallet/common/src/utils.rs +++ b/applications/minotari_ledger_wallet/common/src/utils.rs @@ -68,13 +68,13 @@ pub fn tari_dual_address_display(address_bytes: &[u8; TARI_DUAL_ADDRESS_SIZE]) - } /// Get the public spend key bytes from a serialized Tari dual address -pub fn get_public_spend_key_from_tari_dual_address( +pub fn get_public_spend_key_bytes_from_tari_dual_address( address_bytes: &[u8; TARI_DUAL_ADDRESS_SIZE], ) -> Result<[u8; 32], String> { validate_checksum(address_bytes.as_ref())?; - let mut public_spend_key = [0u8; 32]; - public_spend_key.copy_from_slice(&address_bytes[34..66]); - Ok(public_spend_key) + let mut public_spend_key_bytes = [0u8; 32]; + public_spend_key_bytes.copy_from_slice(&address_bytes[34..66]); + Ok(public_spend_key_bytes) } // Determine whether a byte slice ends with a valid checksum diff --git a/applications/minotari_ledger_wallet/comms/examples/ledger_demo/main.rs b/applications/minotari_ledger_wallet/comms/examples/ledger_demo/main.rs index 612211ab18..b86add1477 100644 --- a/applications/minotari_ledger_wallet/comms/examples/ledger_demo/main.rs +++ b/applications/minotari_ledger_wallet/comms/examples/ledger_demo/main.rs @@ -222,13 +222,14 @@ fn main() { // GetViewKey println!("\ntest: GetViewKey"); - match ledger_get_view_key(account) { - Ok(view_key) => println!("view_key: {}", view_key.to_hex()), + let view_key_1 = match ledger_get_view_key(account) { + Ok(val) => val, Err(e) => { println!("\nError: {}\n", e); return; }, - } + }; + println!("view_key: {}", view_key_1.to_hex()); // GetDHSharedSecret println!("\ntest: GetDHSharedSecret"); @@ -384,7 +385,10 @@ fn main() { println!("\ntest: Ledger app restart"); prompt_with_message("Start the 'MinoTari Wallet' Ledger app and press Enter to continue.."); match ledger_get_view_key(account) { - Ok(view_key) => println!("view_key: {}", view_key.to_hex()), + Ok(view_key_2) => { + println!("view_key: {}", view_key_2.to_hex()); + assert_eq!(view_key_1, view_key_2, "View key not repeatable") + }, Err(e) => { println!("\nError: {}\n", e); return; diff --git a/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs b/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs index c236e5b745..591893c1e9 100644 --- a/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs +++ b/applications/minotari_ledger_wallet/comms/src/accessor_methods.rs @@ -126,8 +126,8 @@ fn verify() -> Result<(), LedgerDeviceError> { Ok(public_key) => { if !signature.verify(&public_key, nonce) { return Err(LedgerDeviceError::Processing( - "'Minotari Wallet' application could not create a valid signature. Please update the firmware \ - on your device." + "Error 1: 'Minotari Wallet' application could not create a valid signature. Please update the \ + firmware on your device." .to_string(), )); } @@ -135,16 +135,16 @@ fn verify() -> Result<(), LedgerDeviceError> { }, Err(e) => { return Err(LedgerDeviceError::Processing(format!( - "'Minotari Wallet' application could not retrieve a public key ({:?}). Please update the firmware \ - on your device.", + "Error 2: 'Minotari Wallet' application could not retrieve a public key ({:?}). Please update the \ + firmware on your device.", e ))) }, }, Err(e) => { return Err(LedgerDeviceError::Processing(format!( - "'Minotari Wallet' application could not create a signature ({:?}). Please update the firmware on \ - your device.", + "Error 3: 'Minotari Wallet' application could not create a signature ({:?}). Please update the \ + firmware on your device.", e ))) }, @@ -153,16 +153,16 @@ fn verify() -> Result<(), LedgerDeviceError> { Ok(signature_b) => { if signature_a == signature_b { return Err(LedgerDeviceError::Processing( - "'Minotari Wallet' application is not creating unique signatures. Please update the firmware on \ - your device." + "Error 4: 'Minotari Wallet' application is not creating unique signatures. Please update the \ + firmware on your device." .to_string(), )); } }, Err(e) => { return Err(LedgerDeviceError::Processing(format!( - "'Minotari Wallet' application could not create a signature ({:?}). Please update the firmware on \ - your device.", + "Error 5: 'Minotari Wallet' application could not create a signature ({:?}). Please update the \ + firmware on your device.", e ))) }, diff --git a/applications/minotari_ledger_wallet/comms/src/lib.rs b/applications/minotari_ledger_wallet/comms/src/lib.rs index da517546d5..76eccf200b 100644 --- a/applications/minotari_ledger_wallet/comms/src/lib.rs +++ b/applications/minotari_ledger_wallet/comms/src/lib.rs @@ -28,7 +28,7 @@ pub mod ledger_wallet; mod test { use borsh::BorshSerialize; use minotari_ledger_wallet_common::{ - get_public_spend_key_from_tari_dual_address, + get_public_spend_key_bytes_from_tari_dual_address, hex_to_bytes_serialized, tari_dual_address_display, PUSH_PUBKEY_IDENTIFIER, @@ -111,7 +111,7 @@ mod test { ); // Getting the public spend key from the address assert_eq!( - get_public_spend_key_from_tari_dual_address(&tari_address_bytes) + get_public_spend_key_bytes_from_tari_dual_address(&tari_address_bytes) .unwrap() .to_vec(), tari_address.public_spend_key().to_vec() diff --git a/applications/minotari_ledger_wallet/wallet/Cargo.toml b/applications/minotari_ledger_wallet/wallet/Cargo.toml index b8f71584c6..559043e306 100644 --- a/applications/minotari_ledger_wallet/wallet/Cargo.toml +++ b/applications/minotari_ledger_wallet/wallet/Cargo.toml @@ -39,7 +39,7 @@ default = [] pending_review_screen = [] [package.metadata.ledger] -curve = ["ed25519"] +curve = ["ed25519", "secp256k1"] flags = "0" path = ["44'/535348'"] name = "MinoTari Wallet" diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_dh_shared_secret.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_dh_shared_secret.rs index 1518931d7e..d10e1b7a8f 100644 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_dh_shared_secret.rs +++ b/applications/minotari_ledger_wallet/wallet/src/handlers/get_dh_shared_secret.rs @@ -1,8 +1,6 @@ // Copyright 2024 The Tari Project // SPDX-License-Identifier: BSD-3-Clause -use core::ops::Deref; - use ledger_device_sdk::{io::Comm, ui::gadgets::SingleMessage}; use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::ByteArray}; @@ -36,7 +34,7 @@ pub fn handler_get_dh_shared_secret(comm: &mut Comm) -> Result<(), AppSW> { let public_key: RistrettoPublicKey = get_key_from_canonical_bytes(&data[24..56])?; let shared_secret_key = match derive_from_bip32_key(account, index, key) { - Ok(k) => k.deref() * public_key, + Ok(k) => k * public_key, Err(e) => return Err(e), }; diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_one_sided_metadata_signature.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_one_sided_metadata_signature.rs index 64af9a96be..08f8d53e36 100644 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_one_sided_metadata_signature.rs +++ b/applications/minotari_ledger_wallet/wallet/src/handlers/get_one_sided_metadata_signature.rs @@ -2,20 +2,21 @@ // SPDX-License-Identifier: BSD-3-Clause use alloc::{format, string::String}; -use core::ops::Deref; use blake2::Blake2b; -use digest::consts::{U32, U64}; +use digest::{ + consts::{U32, U64}, + Digest, +}; use ledger_device_sdk::{ io::Comm, - random::Random, ui::{ bitmaps::{CROSSMARK, EYE, VALIDATE_14}, gadgets::{Field, MultiFieldReview, SingleMessage}, }, }; use minotari_ledger_wallet_common::{ - get_public_spend_key_from_tari_dual_address, + get_public_spend_key_bytes_from_tari_dual_address, hex_to_bytes_serialized, tari_dual_address_display, PUSH_PUBKEY_IDENTIFIER, @@ -39,7 +40,7 @@ use zeroize::Zeroizing; use crate::{ alloc::string::ToString, hashing::DomainSeparatedConsensusHasher, - utils::{derive_from_bip32_key, get_key_from_canonical_bytes, get_key_from_uniform_bytes}, + utils::{derive_from_bip32_key, get_key_from_canonical_bytes, get_key_from_uniform_bytes, get_random_nonce}, AppSW, KeyType, RESPONSE_VERSION, @@ -69,8 +70,7 @@ pub fn handler_get_one_sided_metadata_signature(comm: &mut Comm) -> Result<(), A let value_u64 = u64::from_le_bytes(value_bytes); let value = Minotari::new(u64::from_le_bytes(value_bytes)); - let commitment_mask: Zeroizing = - get_key_from_canonical_bytes::(&data[40..72])?.into(); + let commitment_mask: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[40..72])?.into(); let mut receiver_address_bytes = [0u8; TARI_DUAL_ADDRESS_SIZE]; // 67 bytes receiver_address_bytes.clone_from_slice(&data[72..139]); @@ -109,25 +109,25 @@ pub fn handler_get_one_sided_metadata_signature(comm: &mut Comm) -> Result<(), A return Err(AppSW::UserCancelled); } - let value_as_private_key: Zeroizing = Zeroizing::new(value_u64.into()); + let value_as_private_key: RistrettoSecretKey = value_u64.into(); let sender_offset_private_key = derive_from_bip32_key(account, sender_offset_key_index, KeyType::OneSidedSenderOffset)?; let sender_offset_public_key = RistrettoPublicKey::from_secret_key(&sender_offset_private_key); - let r_a = derive_from_bip32_key(account, u32::random().into(), KeyType::Nonce)?; - let r_x = derive_from_bip32_key(account, u32::random().into(), KeyType::Nonce)?; - let ephemeral_private_key = derive_from_bip32_key(account, u32::random().into(), KeyType::Nonce)?; + let r_a = get_random_nonce()?; + let r_x = get_random_nonce()?; + let ephemeral_private_key = get_random_nonce()?; let factory = ExtendedPedersenCommitmentFactory::default(); - let commitment = factory.commit(&commitment_mask, value_as_private_key.deref()); + let commitment = factory.commit(&commitment_mask, &value_as_private_key); let ephemeral_commitment = factory.commit(&r_x, &r_a); let ephemeral_pubkey = RistrettoPublicKey::from_secret_key(&ephemeral_private_key); - let receiver_public_spend_key: Zeroizing = - match get_public_spend_key_from_tari_dual_address(&receiver_address_bytes) { - Ok(bytes) => get_key_from_canonical_bytes::(&bytes)?.into(), + let receiver_public_spend_key: RistrettoPublicKey = + match get_public_spend_key_bytes_from_tari_dual_address(&receiver_address_bytes) { + Ok(bytes) => get_key_from_canonical_bytes::(&bytes)?, Err(e) => { SingleMessage::new(&format!("Error: {:?}", e.to_string())).show_and_wait(); return Err(AppSW::MetadataSignatureFail); @@ -203,17 +203,18 @@ fn metadata_signature_message_from_script_and_common(network: u64, script: &[u8; fn message_from_script( network: u64, - commitment_mask: &Zeroizing, - receiver_public_spend_key: &Zeroizing, + commitment_mask: &RistrettoSecretKey, + receiver_public_spend_key: &RistrettoPublicKey, ) -> Result<[u8; 32], AppSW> { - let hasher = DomainSeparatedHasher::, KeyManagerTransactionsHashDomain>::new_with_label("script key"); - let hashed_bytes = hasher.chain(commitment_mask.as_bytes()).finalize(); - let hashed_commitment_mask = get_key_from_uniform_bytes(hashed_bytes.as_ref())?; - let hashed_commitment_mask_public_key = - Zeroizing::new(RistrettoPublicKey::from_secret_key(&hashed_commitment_mask)); - let stealth_key = Zeroizing::new(receiver_public_spend_key.deref() + hashed_commitment_mask_public_key.deref()); - - let serialized_script = match hex_to_bytes_serialized(PUSH_PUBKEY_IDENTIFIER, &stealth_key.deref().to_hex()) { + let mut raw_key_hashed = Zeroizing::new([0u8; 64]); + DomainSeparatedHasher::, KeyManagerTransactionsHashDomain>::new_with_label("script key") + .chain(commitment_mask.as_bytes()) + .finalize_into(raw_key_hashed.as_mut().into()); + let hashed_commitment_mask = get_key_from_uniform_bytes(&raw_key_hashed)?; + let hashed_commitment_mask_public_key = RistrettoPublicKey::from_secret_key(&hashed_commitment_mask); + let stealth_key = receiver_public_spend_key + hashed_commitment_mask_public_key; + + let serialized_script = match hex_to_bytes_serialized(PUSH_PUBKEY_IDENTIFIER, &stealth_key.to_hex()) { Ok(script) => script, Err(e) => { SingleMessage::new(&format!("Script error: {:?}", e.to_string())).show_and_wait(); diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_schnorr_signature.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_schnorr_signature.rs index 80c1b3a1b2..b1eb1ef951 100644 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_schnorr_signature.rs +++ b/applications/minotari_ledger_wallet/wallet/src/handlers/get_schnorr_signature.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: BSD-3-Clause use alloc::format; -use core::ops::Deref; use ledger_device_sdk::{io::Comm, ui::gadgets::SingleMessage}; use tari_crypto::{ @@ -59,14 +58,13 @@ pub fn handler_get_raw_schnorr_signature(comm: &mut Comm) -> Result<(), AppSW> { let mut challenge_bytes = [0u8; 64]; challenge_bytes.clone_from_slice(&data[40..104]); - let signature = - match RistrettoSchnorr::sign_raw_uniform(&private_key, private_nonce.deref().clone(), &challenge_bytes) { - Ok(sig) => sig, - Err(e) => { - SingleMessage::new(&format!("Signing error: {:?}", e.to_string())).show_and_wait(); - return Err(AppSW::RawSchnorrSignatureFail); - }, - }; + let signature = match RistrettoSchnorr::sign_raw_uniform(&private_key, private_nonce.clone(), &challenge_bytes) { + Ok(sig) => sig, + Err(e) => { + SingleMessage::new(&format!("Signing error: {:?}", e.to_string())).show_and_wait(); + return Err(AppSW::RawSchnorrSignatureFail); + }, + }; comm.append(&[RESPONSE_VERSION]); // version comm.append(&signature.get_public_nonce().to_vec()); @@ -101,7 +99,7 @@ pub fn handler_get_script_schnorr_signature(comm: &mut Comm) -> Result<(), AppSW let mut nonce_bytes = [0u8; 32]; nonce_bytes.clone_from_slice(&data[24..56]); - let random_nonce = get_random_nonce()?.deref().clone(); + let random_nonce = get_random_nonce()?.clone(); let signature = match CheckSigSchnorrSignature::sign_with_nonce_and_message(&private_key, random_nonce, &nonce_bytes) { Ok(sig) => sig, diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_offset.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_offset.rs index 312b23a9fe..fbcf15881f 100644 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_offset.rs +++ b/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_offset.rs @@ -2,11 +2,9 @@ // SPDX-License-Identifier: BSD-3-Clause use alloc::vec::Vec; -use core::ops::Deref; use ledger_device_sdk::io::Comm; use tari_crypto::{ristretto::RistrettoSecretKey, tari_utilities::ByteArray}; -use zeroize::Zeroizing; use crate::{ utils::{alpha_hasher, derive_from_bip32_key, get_key_from_canonical_bytes}, @@ -19,22 +17,22 @@ use crate::{ const MIN_UNIQUE_KEYS: usize = 2; pub struct ScriptOffsetCtx { - sender_offset_sum: Zeroizing, - script_private_key_sum: Zeroizing, + sender_offset_sum: RistrettoSecretKey, + script_private_key_sum: RistrettoSecretKey, account: u64, total_offset_indexes: u64, total_script_indexes: u64, total_derived_offset_keys: u64, total_derived_script_keys: u64, - unique_keys: Vec>, + unique_keys: Vec, } // Implement constructor for TxInfo with default values impl ScriptOffsetCtx { pub fn new() -> Self { Self { - sender_offset_sum: Zeroizing::new(RistrettoSecretKey::default()), - script_private_key_sum: Zeroizing::new(RistrettoSecretKey::default()), + sender_offset_sum: RistrettoSecretKey::default(), + script_private_key_sum: RistrettoSecretKey::default(), account: 0, total_offset_indexes: 0, total_script_indexes: 0, @@ -46,8 +44,8 @@ impl ScriptOffsetCtx { // Implement reset for TxInfo fn reset(&mut self) { - self.sender_offset_sum = Zeroizing::new(RistrettoSecretKey::default()); - self.script_private_key_sum = Zeroizing::new(RistrettoSecretKey::default()); + self.sender_offset_sum = RistrettoSecretKey::default(); + self.script_private_key_sum = RistrettoSecretKey::default(); self.account = 0; self.total_offset_indexes = 0; self.total_script_indexes = 0; @@ -56,7 +54,7 @@ impl ScriptOffsetCtx { self.unique_keys = Vec::new(); } - fn add_unique_key(&mut self, secret_key: Zeroizing) { + fn add_unique_key(&mut self, secret_key: RistrettoSecretKey) { if !self.unique_keys.contains(&secret_key) { self.unique_keys.push(secret_key); } @@ -111,13 +109,12 @@ fn derive_key_from_alpha( account: u64, data: &[u8], offset_ctx: &mut ScriptOffsetCtx, -) -> Result, AppSW> { +) -> Result { if data.len() != 32 { return Err(AppSW::WrongApduLength); } let alpha = derive_from_bip32_key(account, STATIC_SPEND_INDEX, KeyType::Spend)?; - let blinding_factor: Zeroizing = - get_key_from_canonical_bytes::(&data[0..32])?.into(); + let blinding_factor: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[0..32])?.into(); offset_ctx.add_unique_key(alpha.clone()); @@ -143,7 +140,7 @@ pub fn handler_get_script_offset( // 2. partial_script_offset if chunk_number == 1 { // Initialize 'script_private_key_sum' with 'partial_script_offset' - let partial_script_offset: Zeroizing = + let partial_script_offset: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[0..32])?.into(); offset_ctx.script_private_key_sum = partial_script_offset; @@ -159,7 +156,7 @@ pub fn handler_get_script_offset( let offset = derive_from_bip32_key(offset_ctx.account, index, branch)?; offset_ctx.add_unique_key(offset.clone()); - offset_ctx.sender_offset_sum = Zeroizing::new(offset_ctx.sender_offset_sum.deref() + offset.deref()); + offset_ctx.sender_offset_sum = &offset_ctx.sender_offset_sum + offset; } // 4. Indexed Script key @@ -169,8 +166,7 @@ pub fn handler_get_script_offset( let script_key = derive_from_bip32_key(offset_ctx.account, index, branch)?; offset_ctx.add_unique_key(script_key.clone()); - offset_ctx.script_private_key_sum = - Zeroizing::new(offset_ctx.script_private_key_sum.deref() + script_key.deref()); + offset_ctx.script_private_key_sum = &offset_ctx.script_private_key_sum + script_key; } // 5. Derived sender offsets key @@ -178,7 +174,7 @@ pub fn handler_get_script_offset( if (end_script_indexes..end_derived_offset_keys).contains(&(chunk_number as u64)) { let k = derive_key_from_alpha(offset_ctx.account, data, offset_ctx)?; - offset_ctx.sender_offset_sum = Zeroizing::new(offset_ctx.sender_offset_sum.deref() + k.deref()); + offset_ctx.sender_offset_sum = &offset_ctx.sender_offset_sum + k; } // 6. Derived script key @@ -186,7 +182,7 @@ pub fn handler_get_script_offset( if (end_derived_offset_keys..end_derived_script_keys).contains(&(chunk_number as u64)) { let k = derive_key_from_alpha(offset_ctx.account, data, offset_ctx)?; - offset_ctx.script_private_key_sum = Zeroizing::new(offset_ctx.script_private_key_sum.deref() + k.deref()); + offset_ctx.script_private_key_sum = &offset_ctx.script_private_key_sum + k } if more { @@ -198,8 +194,7 @@ pub fn handler_get_script_offset( return Err(AppSW::ScriptOffsetNotUnique); } - let script_offset = - Zeroizing::new(offset_ctx.script_private_key_sum.deref() - offset_ctx.sender_offset_sum.deref()); + let script_offset = &offset_ctx.script_private_key_sum - &offset_ctx.sender_offset_sum; comm.append(&[RESPONSE_VERSION]); // version comm.append(&script_offset.to_vec()); diff --git a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature.rs b/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature.rs index 7edc856f13..fe3bd99efb 100644 --- a/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature.rs +++ b/applications/minotari_ledger_wallet/wallet/src/handlers/get_script_signature.rs @@ -17,7 +17,6 @@ use tari_crypto::{ }, }; use tari_hashing::TransactionHashDomain; -use zeroize::Zeroizing; use crate::{ alloc::string::ToString, @@ -77,7 +76,7 @@ pub fn handler_get_script_signature_derived(comm: &mut Comm) -> Result<(), AppSW extract_common_values(data)?; let alpha = derive_from_bip32_key(account, STATIC_SPEND_INDEX, KeyType::Spend)?; - let blinding_factor: Zeroizing = + let blinding_factor: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[152..184])?.into(); let script_private_key = alpha_hasher(alpha, blinding_factor)?; let script_public_key = RistrettoPublicKey::from_secret_key(&script_private_key); @@ -107,8 +106,8 @@ fn extract_common_values( u64, u64, u64, - Zeroizing, - Zeroizing, + RistrettoSecretKey, + RistrettoSecretKey, PedersenCommitment, [u8; 32], ), @@ -129,9 +128,8 @@ fn extract_common_values( txi_version_bytes.clone_from_slice(&data[16..24]); let txi_version = u64::from_le_bytes(txi_version_bytes); - let value: Zeroizing = - get_key_from_canonical_bytes::(&data[24..56])?.into(); - let commitment_private_key: Zeroizing = + let value: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[24..56])?.into(); + let commitment_private_key: RistrettoSecretKey = get_key_from_canonical_bytes::(&data[56..88])?.into(); let commitment: PedersenCommitment = get_key_from_canonical_bytes(&data[88..120])?; @@ -153,9 +151,9 @@ fn extract_common_values( fn get_script_signature( txi_version: u64, network: u64, - value: Zeroizing, - commitment_private_key: Zeroizing, - script_private_key: Zeroizing, + value: RistrettoSecretKey, + commitment_private_key: RistrettoSecretKey, + script_private_key: RistrettoSecretKey, script_public_key: RistrettoPublicKey, commitment: PedersenCommitment, script_message: [u8; 32], diff --git a/applications/minotari_ledger_wallet/wallet/src/main.rs b/applications/minotari_ledger_wallet/wallet/src/main.rs index 97c4e058d0..807a8a5c8b 100644 --- a/applications/minotari_ledger_wallet/wallet/src/main.rs +++ b/applications/minotari_ledger_wallet/wallet/src/main.rs @@ -95,6 +95,7 @@ unsafe impl critical_section::Impl for MyCriticalSection { // Application status words. #[repr(u16)] +#[derive(Debug)] pub enum AppSW { Deny = AppSWMapping::Deny as u16, WrongP1P2 = AppSWMapping::WrongP1P2 as u16, diff --git a/applications/minotari_ledger_wallet/wallet/src/utils.rs b/applications/minotari_ledger_wallet/wallet/src/utils.rs index 7c08f37362..0e74886475 100644 --- a/applications/minotari_ledger_wallet/wallet/src/utils.rs +++ b/applications/minotari_ledger_wallet/wallet/src/utils.rs @@ -2,13 +2,11 @@ // SPDX-License-Identifier: BSD-3-Clause use alloc::format; -use core::ops::Deref; use blake2::Blake2b; use digest::{consts::U64, Digest}; use ledger_device_sdk::{ ecc::{bip32_derive, make_bip32_path, CurvesId, CxError}, - io::SyscallError, random::LedgerRng, ui::gadgets::{MessageScroller, SingleMessage}, }; @@ -75,8 +73,7 @@ impl TryFrom<&[u8]> for Bip32Path { // We cannot have too many elements in the path, and must have `u32` path elements let input_path_len = (data.len() - 1) / 4; - if input_path_len > S || data[0] as usize * 4 != data.len() - 1 - { + if input_path_len > S || data[0] as usize * 4 != data.len() - 1 { return Err(AppSW::WrongApduLength); } @@ -147,32 +144,32 @@ fn cx_error_to_string(e: CxError) -> String { } // Get a raw 64 byte key hash from the BIP32 path. -// - The wrapper function for the syscall `os_perso_derive_node_bip32`, `bip32_derive`, requires a 96 byte buffer when -// called with `CurvesId::Ed25519` as it checks the consistency of the curve choice and key length in order to prevent -// the underlying syscall from panicking. -// - The syscall `os_perso_derive_node_bip32` returns 96 bytes as: -// private key: 64 bytes -// chain: 32 bytes -// Example: -// d8a57c1be0c52e9643485e77aac56d72fa6c4eb831466c2abd2d320c82d3d14929811c598c13d431bad433e037dbd97265492cea42bc2e3aad15440210a20a2d0000000000000000000000000000000000000000000000000000000000000000 -// - This function applies domain separated hashing to the 64 byte private key of the returned buffer to get 64 -// uniformly distributed random bytes. -fn get_raw_key_hash(path: &[u32]) -> Result, String> { - let mut key_buffer = Zeroizing::new([0u8; 96]); - const BIP32_KEY_LENGTH: usize = 64; - let raw_key_64 = match bip32_derive(CurvesId::Ed25519, path, key_buffer.as_mut(), Some(&mut [])) { +fn get_raw_bip32_key(path: &[u32], curve: CurvesId) -> Result, String> { + match curve { + CurvesId::Secp256k1 | CurvesId::Ed25519 => {}, + _ => return Err("Unsupported curve".to_string()), + } + let mut key_buffer = Zeroizing::new([0u8; 64]); + + match bip32_derive(curve, path, key_buffer.as_mut(), Some(&mut [])) { Ok(_) => { - let binding = &key_buffer.as_ref()[..BIP32_KEY_LENGTH]; - if binding == &[0u8; BIP32_KEY_LENGTH] { + let binding = &key_buffer.as_ref(); + if binding == &[0u8; 64] { return Err(cx_error_to_string(CxError::InternalError)); } - let mut key_bytes = Zeroizing::new([0u8; BIP32_KEY_LENGTH]); + let mut key_bytes = Zeroizing::new([0u8; 64]); // `copy_from_slice` will not panic as the length of the slice is equal to the length of the array key_bytes.as_mut().copy_from_slice(binding); - key_bytes + Ok(key_bytes) }, Err(e) => return Err(cx_error_to_string(e)), - }; + } +} + +// This function applies domain separated hashing to the 64 byte private key of the returned buffer to get 64 +// uniformly distributed random bytes. +fn get_raw_key_hash(path: &[u32], curve: CurvesId) -> Result, String> { + let raw_key_64 = get_raw_bip32_key(path, curve)?; let mut raw_key_hashed = Zeroizing::new([0u8; 64]); DomainSeparatedHasher::, LedgerHashDomain>::new_with_label("raw_key") @@ -182,23 +179,45 @@ fn get_raw_key_hash(path: &[u32]) -> Result, String> { Ok(raw_key_hashed) } -/// Get a raw 64 byte key hash from the BIP32 path. In cas of an error, display an interactive message on the device. -pub fn get_raw_key(path: &[u32]) -> Result, SyscallError> { - match get_raw_key_hash(&path) { - Ok(val) => Ok(val), +/// Derive a secret key from a BIP32 path. In case of an error, display an interactive message on the device. +pub fn derive_from_bip32_key( + u64_account: u64, + u64_index: u64, + u64_key_type: KeyType, +) -> Result { + let account = u64_to_string(u64_account); + let index = u64_to_string(u64_index); + let key_type = u64_to_string(u64_key_type.as_byte() as u64); + + let mut bip32_path = "m/44'/".to_string(); + bip32_path.push_str(&BIP32_COIN_TYPE.to_string()); + bip32_path.push_str(&"'/"); + bip32_path.push_str(&account); + bip32_path.push_str(&"'/0/"); + bip32_path.push_str(&index); + bip32_path.push_str(&"'/"); + bip32_path.push_str(&key_type); + let path: [u32; 6] = make_bip32_path(bip32_path.as_bytes()); + + // We use `CurvesId::Secp256k1` as the curve for the bip32 key derivation because it provides better entropy when + // compared to `CurvesId::Ed25519`. There is also no need for compatibility to `tari_crypto` as the output is only + // ever used in a subsequent key derivation function. + match get_raw_key_hash(&path, CurvesId::Secp256k1) { + Ok(val) => get_key_from_uniform_bytes(&val), Err(e) => { let mut msg = "".to_string(); msg.push_str("Err: raw key >>..."); SingleMessage::new(&msg).show_and_wait(); SingleMessage::new(&e).show_and_wait(); - Err(SyscallError::InvalidParameter.into()) + return Err(AppSW::KeyDeriveFail); }, } } -pub fn get_key_from_uniform_bytes(bytes: &[u8]) -> Result, AppSW> { - match RistrettoSecretKey::from_uniform_bytes(bytes) { - Ok(val) => Ok(Zeroizing::new(val)), +/// Get a 32 byte secret key from 64 uniform bytes +pub fn get_key_from_uniform_bytes(bytes: &Zeroizing<[u8; 64]>) -> Result { + match RistrettoSecretKey::from_uniform_bytes(bytes.as_ref()) { + Ok(val) => Ok(val), Err(e) => { MessageScroller::new(&format!( "Err: key conversion {:?}. Length: {:?}", @@ -212,6 +231,7 @@ pub fn get_key_from_uniform_bytes(bytes: &[u8]) -> Result(bytes: &[u8]) -> Result { match T::from_canonical_bytes(bytes) { Ok(val) => Ok(val), @@ -228,52 +248,26 @@ pub fn get_key_from_canonical_bytes(bytes: &[u8]) -> Result, - blinding_factor: Zeroizing, -) -> Result, AppSW> { - let hasher = DomainSeparatedHasher::, KeyManagerTransactionsHashDomain>::new_with_label("script key"); - let hasher = hasher.chain(blinding_factor.as_bytes()).finalize(); - let private_key = get_key_from_uniform_bytes(hasher.as_ref())?; - - Ok(Zeroizing::new(private_key.deref() + alpha.deref())) -} - -pub fn derive_from_bip32_key( - u64_account: u64, - u64_index: u64, - u64_key_type: KeyType, -) -> Result, AppSW> { - let account = u64_to_string(u64_account); - let index = u64_to_string(u64_index); - let key_type = u64_to_string(u64_key_type.as_byte() as u64); - - let mut bip32_path = "m/44'/".to_string(); - bip32_path.push_str(&BIP32_COIN_TYPE.to_string()); - bip32_path.push_str(&"'/"); - bip32_path.push_str(&account); - bip32_path.push_str(&"'/0/"); - bip32_path.push_str(&index); - bip32_path.push_str(&"'/"); - bip32_path.push_str(&key_type); - let path: [u32; 6] = make_bip32_path(bip32_path.as_bytes()); + alpha: RistrettoSecretKey, + blinding_factor: RistrettoSecretKey, +) -> Result { + let mut raw_key_hashed = Zeroizing::new([0u8; 64]); + DomainSeparatedHasher::, KeyManagerTransactionsHashDomain>::new_with_label("script key") + .chain(blinding_factor.as_bytes()) + .finalize_into(raw_key_hashed.as_mut().into()); + let private_key = get_key_from_uniform_bytes(&raw_key_hashed)?; - match get_raw_key(&path) { - Ok(val) => get_key_from_uniform_bytes(&val.as_ref()), - Err(e) => { - SingleMessage::new(&format!("Key error {:?}", e)).show_and_wait(); - return Err(AppSW::KeyDeriveFail); - }, - } + Ok(private_key + alpha) } -pub fn get_random_nonce() -> Result, AppSW> { +/// Get a uniform random nonce +pub fn get_random_nonce() -> Result { let mut raw_bytes = [0u8; 64]; LedgerRng.fill_bytes(&mut raw_bytes); if raw_bytes == [0u8; 64] { return Err(AppSW::RandomNonceFail); } - Ok(Zeroizing::new( - RistrettoSecretKey::from_uniform_bytes(&raw_bytes).expect("will not fail"), - )) + Ok(RistrettoSecretKey::from_uniform_bytes(&raw_bytes).expect("will not fail")) }