From 085a2081475a04fe460120500f284f0d6a1c0bc9 Mon Sep 17 00:00:00 2001 From: Leon Groot Bruinderink Date: Tue, 15 Dec 2020 13:28:52 +0100 Subject: [PATCH 1/3] remove derivation path from command + remove secp256k1 sign --- ledger-zcash/src/app.rs | 59 +++----------------------- ledger-zcash/tests/integration_test.rs | 23 +--------- 2 files changed, 7 insertions(+), 75 deletions(-) diff --git a/ledger-zcash/src/app.rs b/ledger-zcash/src/app.rs index 18d13a3..be24df9 100644 --- a/ledger-zcash/src/app.rs +++ b/ledger-zcash/src/app.rs @@ -257,7 +257,6 @@ const INS_GET_DIV_LIST: u8 = 0x09; const CLA: u8 = 0x85; const INS_GET_ADDR_SECP256K1: u8 = 0x01; -const INS_SIGN_SECP256K1: u8 = 0x02; const INS_GET_ADDR_SAPLING: u8 = 0x11; const INS_GET_ADDR_SAPLING_DIV: u8 = 0x10; @@ -295,8 +294,6 @@ pub struct AddressShielded { pub address: String, } -type SignatureUnshielded = [u8; 65]; - impl ZcashApp { /// Connect to the Ledger App pub fn new(apdu_transport: APDUTransport) -> Self { @@ -516,44 +513,6 @@ impl ZcashApp { Ok(address) } - /// Sign an unshielded transaction - pub async fn sign_unshielded( - &self, - path: &BIP44Path, - message: &[u8], - ) -> Result { - let serialized_path = path.serialize(); - let start_command = APDUCommand { - cla: self.cla(), - ins: INS_SIGN_SECP256K1, - p1: ChunkPayloadType::Init as u8, - p2: 0x00, - data: serialized_path, - }; - - log::info!("sign ->"); - let response = - ledger_zondax_generic::send_chunks(&self.apdu_transport, &start_command, message) - .await?; - log::info!("sign OK"); - - if response.data.is_empty() && response.retcode == APDUErrorCodes::NoError as u16 { - return Err(LedgerAppError::NoSignature); - } - - // Last response should contain the answer - if response.data.len() < 65 { - return Err(LedgerAppError::InvalidSignature); - } - - log::info!("{}", hex::encode(&response.data[..])); - - let mut sig: SignatureUnshielded = [0u8; 65]; - sig.copy_from_slice(&response.data[..65]); - - Ok(sig) - } - /// Retrieves a outgoing viewing key of a sapling key pub async fn get_ovk(&self, path: u32) -> Result { let mut input_data = Vec::with_capacity(4); @@ -630,13 +589,12 @@ impl ZcashApp { ///Initiates a transaction in the ledger pub async fn init_tx(&self, data: &[u8]) -> Result<[u8; 32], LedgerAppError> { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let start_command = APDUCommand { cla: self.cla(), ins: INS_INIT_TX, p1: ChunkPayloadType::Init as u8, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = @@ -668,13 +626,12 @@ impl ZcashApp { pub async fn get_spendinfo( &self, ) -> Result<(ProofGenerationKey, jubjub::Fr, jubjub::Fr), LedgerAppError> { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let command = APDUCommand { cla: self.cla(), ins: INS_EXTRACT_SPEND, p1: 0x00, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = self.apdu_transport.exchange(&command).await?; @@ -741,13 +698,12 @@ impl ZcashApp { ///Get the information needed from ledger to make a shielded output pub async fn get_outputinfo(&self) -> Result<(jubjub::Fr, Rseed), LedgerAppError> { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let command = APDUCommand { cla: self.cla(), ins: INS_EXTRACT_OUTPUT, p1: 0x00, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = self.apdu_transport.exchange(&command).await?; @@ -788,13 +744,12 @@ impl ZcashApp { ///Get a transparent signature from the ledger pub async fn get_transparent_signature(&self) -> Result { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let command = APDUCommand { cla: self.cla(), ins: INS_EXTRACT_TRANSSIG, p1: 0x00, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = self.apdu_transport.exchange(&command).await?; @@ -824,13 +779,12 @@ impl ZcashApp { ///Get a shielded spend signature from the ledger pub async fn get_spend_signature(&self) -> Result { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let command = APDUCommand { cla: self.cla(), ins: INS_EXTRACT_SPENDSIG, p1: 0x00, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = self.apdu_transport.exchange(&command).await?; @@ -861,13 +815,12 @@ impl ZcashApp { ///Initiates a transaction in the ledger pub async fn checkandsign(&self, data: &[u8]) -> Result<[u8; 32], LedgerAppError> { - let dummy_path = BIP44Path::from_string("m/44'/133'/5'/0/0").unwrap(); let start_command = APDUCommand { cla: self.cla(), ins: INS_CHECKANDSIGN, p1: ChunkPayloadType::Init as u8, p2: 0x00, - data: dummy_path.serialize(), + data: vec![], }; let response = diff --git a/ledger-zcash/tests/integration_test.rs b/ledger-zcash/tests/integration_test.rs index f9d4f22..c22bbef 100644 --- a/ledger-zcash/tests/integration_test.rs +++ b/ledger-zcash/tests/integration_test.rs @@ -19,7 +19,7 @@ extern crate hex; extern crate ledger_zcash; -#[macro_use] + extern crate matches; #[macro_use] extern crate serial_test; @@ -220,27 +220,6 @@ mod integration_tests { ); } - #[tokio::test] - #[serial] - async fn sign_empty() { - init_logging(); - - let transport = APDUTransport { - transport_wrapper: Box::new(ledger::TransportNativeHID::new().unwrap()), - }; - let app = ZcashApp::new(transport); - - let path = BIP44Path::from_string("m/44'/133'/0'/0/5").unwrap(); - let some_message0 = b""; - - let response = app.sign_unshielded(&path, some_message0).await; - assert!(response.is_err()); - assert!(matches!( - response.err().unwrap(), - ledger_zcash::LedgerAppError::InvalidEmptyMessage - )); - } - #[tokio::test] async fn get_div_list() { init_logging(); From 5274f4f4d37d72ff5d70d3fcea89fe025f2ff1de Mon Sep 17 00:00:00 2001 From: Leon Groot Bruinderink Date: Tue, 15 Dec 2020 13:58:13 +0100 Subject: [PATCH 2/3] use constants for sizes --- ledger-zcash/src/app.rs | 193 +++++++++++++++++++++++++--------------- 1 file changed, 122 insertions(+), 71 deletions(-) diff --git a/ledger-zcash/src/app.rs b/ledger-zcash/src/app.rs index be24df9..a0487c9 100644 --- a/ledger-zcash/src/app.rs +++ b/ledger-zcash/src/app.rs @@ -50,6 +50,86 @@ use zcash_hsmbuilder::{ use sha2::{Digest, Sha256}; +const INS_GET_IVK: u8 = 0xf0; +const INS_GET_OVK: u8 = 0xf1; +const INS_INIT_TX: u8 = 0xa0; +const INS_EXTRACT_SPEND: u8 = 0xa1; +const INS_EXTRACT_OUTPUT: u8 = 0xa2; +const INS_CHECKANDSIGN: u8 = 0xa3; +const INS_EXTRACT_SPENDSIG: u8 = 0xa4; +const INS_EXTRACT_TRANSSIG: u8 = 0xa5; +const INS_GET_DIV_LIST: u8 = 0x09; + +const CLA: u8 = 0x85; +const INS_GET_ADDR_SECP256K1: u8 = 0x01; +const INS_GET_ADDR_SAPLING: u8 = 0x11; +const INS_GET_ADDR_SAPLING_DIV: u8 = 0x10; + +///Lenght of diversifier index +const DIV_INDEX_SIZE: usize = 11; +///Diversifier length +const DIV_SIZE: usize = 11; +///get div list returns 20 diversifiers +const DIV_LIST_SIZE: usize = 220; + +///OVK size +const OVK_SIZE: usize = 32; + +///IVK size +const IVK_SIZE: usize = 32; + +///sha256 digest size +const SHA256_DIGEST_SIZE: usize = 32; + +///AK size +const AK_SIZE: usize = 32; + +///NSK size +const NSK_SIZE: usize = 32; + +///ALPHA size +const ALPHA_SIZE: usize = 32; + +///RCV size +const RCV_SIZE: usize = 32; + +///Spenddata length: AK (32) + NSK (32) + Alpha(32) + RCV (32) +const SPENDDATA_SIZE: usize = AK_SIZE + NSK_SIZE + ALPHA_SIZE + RCV_SIZE; + +///RCM size +const RSEED_SIZE: usize = 32; + +///Spenddata length: RCV (32) + RCM (32) + +const OUTPUTDATA_SIZE: usize = RCV_SIZE + RSEED_SIZE; + +/// Public Key Length (secp256k1) +pub const PK_LEN_SECP261K1: usize = 33; + +/// Public Key Length (sapling) +pub const PK_LEN_SAPLING: usize = 43; + +//T_IN input size: BIP44-path (20) + script (26) + value (8) +const T_IN_INPUT_SIZE: usize = 54; + +//T_OUT input size: script (26) + value (8) +const T_OUT_INPUT_SIZE: usize = 34; + +//S_SPEND input size: zip32-path (4) + address (43) + value (8) +const S_SPEND_INPUT_SIZE: usize = 55; + +//S_SPEND input size: address (43) + value (8) + memotype (1) + ovk(32) +const S_OUT_INPUT_SIZE: usize = 84; + +//Signature size for transparent and shielded signatures +const SIG_SIZE: usize = 64; + +type PublicKeySecp256k1 = [u8; PK_LEN_SECP261K1]; + +/// Ledger App +pub struct ZcashApp { + apdu_transport: APDUTransport, +} + //use zcash_primitives::transaction::Transaction; ///Data needed to handle transparent input for sapling transaction @@ -216,22 +296,22 @@ pub struct DataInput { impl DataInput { ///Prepares the data to send to the ledger pub fn to_inittx_data(&self) -> InitData { - let mut t_in = Vec::with_capacity(self.vec_tin.len() * 54); + let mut t_in = Vec::with_capacity(self.vec_tin.len() * T_IN_INPUT_SIZE); for info in self.vec_tin.iter() { t_in.push(info.to_init_data()); } - let mut t_out = Vec::with_capacity(self.vec_tout.len() * 34); + let mut t_out = Vec::with_capacity(self.vec_tout.len() * T_OUT_INPUT_SIZE); for info in self.vec_tout.iter() { t_out.push(info.to_init_data()); } - let mut s_spend = Vec::with_capacity(self.vec_sspend.len() * 55); + let mut s_spend = Vec::with_capacity(self.vec_sspend.len() * S_SPEND_INPUT_SIZE); for info in self.vec_sspend.iter() { s_spend.push(info.to_init_data()); } - let mut s_output = Vec::with_capacity(self.vec_soutput.len() * 55); + let mut s_output = Vec::with_capacity(self.vec_soutput.len() * S_OUT_INPUT_SIZE); for info in self.vec_soutput.iter() { s_output.push(info.to_init_data()); } @@ -245,35 +325,6 @@ impl DataInput { } } -const INS_GET_IVK: u8 = 0xf0; -const INS_GET_OVK: u8 = 0xf1; -const INS_INIT_TX: u8 = 0xa0; -const INS_EXTRACT_SPEND: u8 = 0xa1; -const INS_EXTRACT_OUTPUT: u8 = 0xa2; -const INS_CHECKANDSIGN: u8 = 0xa3; -const INS_EXTRACT_SPENDSIG: u8 = 0xa4; -const INS_EXTRACT_TRANSSIG: u8 = 0xa5; -const INS_GET_DIV_LIST: u8 = 0x09; - -const CLA: u8 = 0x85; -const INS_GET_ADDR_SECP256K1: u8 = 0x01; - -const INS_GET_ADDR_SAPLING: u8 = 0x11; -const INS_GET_ADDR_SAPLING_DIV: u8 = 0x10; -//const INS_SIGN_SAPLING: u8 = 0x12; - -/// Public Key Length (secp256k1) -pub const PK_LEN_SECP261K1: usize = 33; - -/// Public Key Length (sapling) -pub const PK_LEN_SAPLING: usize = 43; - -/// Ledger App -pub struct ZcashApp { - apdu_transport: APDUTransport, -} - -type PublicKeySecp256k1 = [u8; PK_LEN_SECP261K1]; //type PublicKeySapling = [u8; PK_LEN_SAPLING]; /// Zcash unshielded address @@ -421,8 +472,8 @@ impl ZcashApp { pub async fn get_div_list( &self, path: u32, - index: &[u8; 11], - ) -> Result<[u8; 220], LedgerAppError> { + index: &[u8; DIV_INDEX_SIZE], + ) -> Result<[u8; DIV_LIST_SIZE], LedgerAppError> { let mut input_data = Vec::with_capacity(4); input_data.write_u32::(path).unwrap(); @@ -444,14 +495,14 @@ impl ZcashApp { } // Last response should contain the answer - if response.data.len() < 220 { + if response.data.len() < DIV_LIST_SIZE { return Err(LedgerAppError::InvalidSignature); } log::info!("{}", hex::encode(&response.data[..])); - let mut list = [0u8; 220]; - list.copy_from_slice(&response.data[..220]); + let mut list = [0u8; DIV_LIST_SIZE]; + list.copy_from_slice(&response.data[..DIV_LIST_SIZE]); Ok(list) } @@ -460,7 +511,7 @@ impl ZcashApp { pub async fn get_address_shielded_with_div( &self, path: u32, - div: &[u8; 11], + div: &[u8; DIV_SIZE], require_confirmation: bool, ) -> Result { let p1 = if require_confirmation { 1 } else { 0 }; @@ -486,14 +537,14 @@ impl ZcashApp { } // Last response should contain the answer - if response.data.len() < 43 { + if response.data.len() < PK_LEN_SAPLING { return Err(LedgerAppError::InvalidSignature); } log::info!("{}", hex::encode(&response.data[..])); - let mut addrb = [0u8; 43]; - addrb.copy_from_slice(&response.data[..43]); + let mut addrb = [0u8; PK_LEN_SAPLING]; + addrb.copy_from_slice(&response.data[..PK_LEN_SAPLING]); let addr = PaymentAddress::from_bytes(&addrb); @@ -534,14 +585,14 @@ impl ZcashApp { )); } - if response.data.len() < 32 { + if response.data.len() < OVK_SIZE { return Err(LedgerAppError::InvalidPK); } log::info!("Received response {}", response.data.len()); - let mut bytes = [0u8; 32]; - bytes.copy_from_slice(&response.data[0..32]); + let mut bytes = [0u8; OVK_SIZE]; + bytes.copy_from_slice(&response.data[0..OVK_SIZE]); let ovk = OutgoingViewingKey(bytes); @@ -569,14 +620,14 @@ impl ZcashApp { )); } - if response.data.len() < 32 { + if response.data.len() < IVK_SIZE { return Err(LedgerAppError::InvalidPK); } log::info!("Received response {}", response.data.len()); - let mut bytes = [0u8; 32]; - bytes.copy_from_slice(&response.data[0..32]); + let mut bytes = [0u8; IVK_SIZE]; + bytes.copy_from_slice(&response.data[0..IVK_SIZE]); let f = jubjub::Fr::from_bytes(&bytes); @@ -588,7 +639,7 @@ impl ZcashApp { } ///Initiates a transaction in the ledger - pub async fn init_tx(&self, data: &[u8]) -> Result<[u8; 32], LedgerAppError> { + pub async fn init_tx(&self, data: &[u8]) -> Result<[u8; SHA256_DIGEST_SIZE], LedgerAppError> { let start_command = APDUCommand { cla: self.cla(), ins: INS_INIT_TX, @@ -605,8 +656,8 @@ impl ZcashApp { return Err(LedgerAppError::NoSignature); } - let mut hash = [0u8; 32]; - hash.copy_from_slice(&response.data[..32]); + let mut hash = [0u8; SHA256_DIGEST_SIZE]; + hash.copy_from_slice(&response.data[..SHA256_DIGEST_SIZE]); let mut sha256 = Sha256::new(); sha256.update(data); @@ -642,7 +693,7 @@ impl ZcashApp { )); } - if response.data.len() < 64 + 32 + 32 { + if response.data.len() < SPENDDATA_SIZE { return Err(LedgerAppError::InvalidPK); } @@ -650,10 +701,10 @@ impl ZcashApp { let bytes = response.data; - let mut akb = [0u8; 32]; - akb.copy_from_slice(&bytes[0..32]); - let mut nskb = [0u8; 32]; - nskb.copy_from_slice(&bytes[32..64]); + let mut akb = [0u8; AK_SIZE]; + akb.copy_from_slice(&bytes[0..AK_SIZE]); + let mut nskb = [0u8; NSK_SIZE]; + nskb.copy_from_slice(&bytes[AK_SIZE..AK_SIZE + NSK_SIZE]); let ak = jubjub::SubgroupPoint::from_bytes(&akb); let nsk = jubjub::Fr::from_bytes(&nskb); @@ -669,8 +720,8 @@ impl ZcashApp { nsk: nsk.unwrap(), }; - let mut rcvb = [0u8; 32]; - rcvb.copy_from_slice(&bytes[64..96]); + let mut rcvb = [0u8; RCV_SIZE]; + rcvb.copy_from_slice(&bytes[AK_SIZE + NSK_SIZE..AK_SIZE + NSK_SIZE + RCV_SIZE]); let f = jubjub::Fr::from_bytes(&rcvb); if f.is_none().into() { @@ -681,8 +732,8 @@ impl ZcashApp { } let rcv = f.unwrap(); - let mut alphab = [0u8; 32]; - alphab.copy_from_slice(&bytes[96..128]); + let mut alphab = [0u8; ALPHA_SIZE]; + alphab.copy_from_slice(&bytes[AK_SIZE + NSK_SIZE + RCV_SIZE..SPENDDATA_SIZE]); let f = jubjub::Fr::from_bytes(&alphab); if f.is_none().into() { @@ -714,7 +765,7 @@ impl ZcashApp { )); } - if response.data.len() < 64 { + if response.data.len() < OUTPUTDATA_SIZE { return Err(LedgerAppError::InvalidPK); } @@ -722,8 +773,8 @@ impl ZcashApp { let bytes = response.data; - let mut rcvb = [0u8; 32]; - rcvb.copy_from_slice(&bytes[0..32]); + let mut rcvb = [0u8; RCV_SIZE]; + rcvb.copy_from_slice(&bytes[0..RCV_SIZE]); let f = jubjub::Fr::from_bytes(&rcvb); if f.is_none().into() { @@ -734,8 +785,8 @@ impl ZcashApp { } let rcv = f.unwrap(); - let mut rseedb = [0u8; 32]; - rseedb.copy_from_slice(&bytes[32..64]); + let mut rseedb = [0u8; RSEED_SIZE]; + rseedb.copy_from_slice(&bytes[RCV_SIZE..OUTPUTDATA_SIZE]); let rseed = Rseed::AfterZip212(rseedb); @@ -760,13 +811,13 @@ impl ZcashApp { )); } - if response.data.len() < 64 { + if response.data.len() < SIG_SIZE { return Err(LedgerAppError::InvalidPK); } log::info!("Received response {}", response.data.len()); - let sig = secp256k1::Signature::from_compact(&response.data[0..64]); + let sig = secp256k1::Signature::from_compact(&response.data[0..SIG_SIZE]); if sig.is_err() { Err(LedgerAppError::AppSpecific( 1, @@ -795,13 +846,13 @@ impl ZcashApp { )); } - if response.data.len() < 64 { + if response.data.len() < SIG_SIZE { return Err(LedgerAppError::InvalidPK); } log::info!("Received response {}", response.data.len()); - let sig = Signature::read(&response.data[..]); + let sig = Signature::read(&response.data[..SIG_SIZE]); if sig.is_err() { Err(LedgerAppError::AppSpecific( @@ -831,8 +882,8 @@ impl ZcashApp { return Err(LedgerAppError::NoSignature); } - let mut hash = [0u8; 32]; - hash.copy_from_slice(&response.data[..32]); + let mut hash = [0u8; SHA256_DIGEST_SIZE]; + hash.copy_from_slice(&response.data[..SHA256_DIGEST_SIZE]); let mut sha256 = Sha256::new(); sha256.update(data); From 2606e9e0c45134b8984b15c83b811fdf2215af18 Mon Sep 17 00:00:00 2001 From: Leon Groot Bruinderink Date: Wed, 16 Dec 2020 16:15:36 +0100 Subject: [PATCH 3/3] bump version numbers --- ledger-zcash/Cargo.toml | 4 ++-- zcash-hsmbuilder/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ledger-zcash/Cargo.toml b/ledger-zcash/Cargo.toml index bcc1a50..71fb3bc 100644 --- a/ledger-zcash/Cargo.toml +++ b/ledger-zcash/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ledger-zcash" description = "Library to integrate with the Ledger Zcash app" -version = "0.0.3" +version = "0.0.4" license = "Apache-2.0" authors = ["Zondax GmbH "] homepage = "https://github.com/Zondax/ledger-zcash-rs" @@ -34,7 +34,7 @@ secp256k1 = { version = "0.19.0", default-features = false } group = "0.8.0" sha2 = "0.9.2" -zcash-hsmbuilder = { path = "../zcash-hsmbuilder", version = "0.0.3" } +zcash-hsmbuilder = { path = "../zcash-hsmbuilder", version = "0.0.4" } [dependencies.zcash_primitives] version = "0.4.0" diff --git a/zcash-hsmbuilder/Cargo.toml b/zcash-hsmbuilder/Cargo.toml index c4c0125..a7cd36b 100644 --- a/zcash-hsmbuilder/Cargo.toml +++ b/zcash-hsmbuilder/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zcash-hsmbuilder" description = "Library to build transactions for HSM apps" -version = "0.0.3" +version = "0.0.4" license = "Apache-2.0" authors = ["Zondax GmbH "] homepage = "https://github.com/Zondax/ledger-zcash-rs"