diff --git a/Cargo.lock b/Cargo.lock index 3d2c48feb360a3..6625d1b02caaab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1845,17 +1845,6 @@ name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "libflate" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "libloading" version = "0.5.2" @@ -1876,19 +1865,6 @@ dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "libusb1-sys" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "linked-hash-map" version = "0.5.2" @@ -3075,11 +3051,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rle-decode-fast" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rocksdb" version = "0.13.0" @@ -3098,16 +3069,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rusb" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "libusb1-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rustc-demangle" version = "0.1.15" @@ -4342,7 +4303,6 @@ dependencies = [ "hidapi 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rusb 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "solana-sdk 0.24.0", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5377,6 +5337,15 @@ dependencies = [ "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "titlecase" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio" version = "0.1.22" @@ -5767,11 +5736,6 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "vcpkg" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "vec_map" version = "0.8.1" @@ -6275,10 +6239,8 @@ dependencies = [ "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" -"checksum libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d9135df43b1f5d0e333385cb6e7897ecd1a43d7d11b91ac003f4d2c2d2401fdd" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum librocksdb-sys 6.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0785e816e1e11e7599388a492c61ef80ddc2afc91e313e61662cce537809be" -"checksum libusb1-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e01d714676f5aeb27d636f5e502b2f65ee734576bbc09c77e8cf5aa5980fb6f9" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" @@ -6408,10 +6370,8 @@ dependencies = [ "checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" "checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92" "checksum ring 0.16.7 (registry+https://github.com/rust-lang/crates.io-index)" = "796ae8317a07b04dffb1983bdc7045ccd02f741f0b411704f07fd35dbf99f757" -"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" "checksum rocksdb 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" "checksum rpassword 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" -"checksum rusb 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d10caa3e5fc7ad1879a679bf16d3304ea10614b8f2f1a1386be4ec942d44062a" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" @@ -6522,6 +6482,7 @@ dependencies = [ "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum tiny-bip39 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1cd1fb03fe8e07d17cd851a624a9fff74642a997b67fbd1ccd77533241640d92" "checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" +"checksum titlecase 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f565e410cfc24c2f2a89960b023ca192689d7f77d3f8d4f4af50c2d8affe1117" "checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" "checksum tokio 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1bef565a52394086ecac0a6fa3b8ace4cb3a138ee1d96bd2b93283b56824e3" "checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" @@ -6564,7 +6525,6 @@ dependencies = [ "checksum users 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c72f4267aea0c3ec6d07eaabea6ead7c5ddacfafc5e22bcf8d186706851fb4cf" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" -"checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/remote-wallet/Cargo.toml b/remote-wallet/Cargo.toml index 0bd09f01da1333..7a89ff17cb2323 100644 --- a/remote-wallet/Cargo.toml +++ b/remote-wallet/Cargo.toml @@ -11,12 +11,13 @@ homepage = "https://solana.com/" [dependencies] base32 = "0.4.0" dialoguer = "0.5.0" -hidapi = "1.1.1" +hidapi = { version = "1.1.1", default-features = false, features = ["linux-static-hidraw"] } log = "0.4.8" parking_lot = "0.7" -rusb = "0.5.5" semver = "0.9" solana-sdk = { path = "../sdk", version = "0.24.0" } thiserror = "1.0" -[dev-dependencies] +[[bin]] +name = "solana-ledger-udev" +path = "src/bin/ledger-udev.rs" diff --git a/remote-wallet/src/bin/ledger-udev.rs b/remote-wallet/src/bin/ledger-udev.rs new file mode 100644 index 00000000000000..88198cc947e4d4 --- /dev/null +++ b/remote-wallet/src/bin/ledger-udev.rs @@ -0,0 +1,42 @@ +/// Implements udev rules on Linux for supported Ledger devices +/// This script must be run with sudo privileges +use std::{ + error, + fs::{File, OpenOptions}, + io::{Read, Write}, + path::Path, +}; + +const LEDGER_UDEV_RULES: &'static str = r#" + # Nano S + SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001|1000|1001|1002|1003|1004|1005|1006|1007|1008|1009|100a|100b|100c|100d|100e|100f|1010|1011|1012|1013|1014|1015|1016|1017|1018|1019|101a|101b|101c|101d|101e|101f", TAG+="uaccess", TAG+="udev-acl", MODE="0666" + # Nano X + SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004|4000|4001|4002|4003|4004|4005|4006|4007|4008|4009|400a|400b|400c|400d|400e|400f|4010|4011|4012|4013|4014|4015|4016|4017|4018|4019|401a|401b|401c|401d|401e|401f", TAG+="uaccess", TAG+="udev-acl", MODE="0666" + "#; + +fn main() -> Result<(), Box> { + if cfg!(target_os = "linux") { + let mut contents = String::new(); + if Path::new("/etc/udev/rules.d/20-hw1.rules").exists() { + let mut file = File::open("/etc/udev/rules.d/20-hw1.rules")?; + file.read_to_string(&mut contents)?; + } + if !contents.contains(LEDGER_UDEV_RULES) { + let mut file = OpenOptions::new() + .append(true) + .create(true) + .open("/etc/udev/rules.d/20-hw1.rules") + .map_err(|e| { + println!("Could not write to file; this script requires sudo privileges"); + e + })?; + file.write(LEDGER_UDEV_RULES.as_bytes())?; + println!("Ledger udev rules written"); + } else { + println!("Ledger udev rules already in place"); + } + } else { + println!("Mismatched target_os; udev rules only required on linux os"); + } + Ok(()) +} diff --git a/remote-wallet/src/ledger.rs b/remote-wallet/src/ledger.rs index 474b463f372bce..10264dfb8d74ad 100644 --- a/remote-wallet/src/ledger.rs +++ b/remote-wallet/src/ledger.rs @@ -17,9 +17,17 @@ const SOL_DERIVATION_PATH_BE: [u8; 8] = [0x80, 0, 0, 44, 0x80, 0, 0x01, 0xF5]; / /// Ledger vendor ID const LEDGER_VID: u16 = 0x2c97; -/// Ledger product IDs: [Nano S and Nano X] -// TODO: do we need to support Blue? -const _LEDGER_PIDS: [u16; 2] = [0x0001, 0x0004]; // TODO: Nano S pid doesn't match expected LEDGER_PIDS value... +/// Ledger product IDs: Nano S and Nano X +const LEDGER_NANO_S_PIDS: [u16; 33] = [ + 0x0001, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100a, + 0x100b, 0x100c, 0x100d, 0x100e, 0x100f, 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, + 0x1017, 0x1018, 0x1019, 0x101a, 0x101b, 0x101c, 0x101d, 0x101e, 0x101f, +]; +const LEDGER_NANO_X_PIDS: [u16; 33] = [ + 0x0004, 0x4000, 0x4001, 0x4002, 0x4003, 0x4004, 0x4005, 0x4006, 0x4007, 0x4008, 0x4009, 0x400a, + 0x400b, 0x400c, 0x400d, 0x400e, 0x400f, 0x4010, 0x4011, 0x4012, 0x4013, 0x4014, 0x4015, 0x4016, + 0x4017, 0x4018, 0x4019, 0x401a, 0x401b, 0x401c, 0x401d, 0x401e, 0x401f, +]; const LEDGER_TRANSPORT_HEADER_LEN: usize = 5; const MAX_CHUNK_SIZE: usize = 255; @@ -181,7 +189,7 @@ impl LedgerWallet { trace!("Read status {:x}", status); #[allow(clippy::match_overlapping_arm)] match status { - // TODO: These need to be aligned with solana Ledger app error codes, and clippy allowance removed + // These need to be aligned with solana Ledger app error codes, and clippy allowance removed 0x6700 => Err(RemoteWalletError::Protocol("Incorrect length")), 0x6982 => Err(RemoteWalletError::Protocol( "Security status not satisfied (Canceled by user)", @@ -227,19 +235,6 @@ impl LedgerWallet { ver[3].into(), )) } - - fn get_derivation_path(&self, derivation: DerivationPath) -> Vec { - let byte = if derivation.change.is_some() { 4 } else { 3 }; - let mut concat_derivation = vec![byte]; - concat_derivation.extend_from_slice(&SOL_DERIVATION_PATH_BE); - concat_derivation.extend_from_slice(&[0x80, 0]); - concat_derivation.extend_from_slice(&derivation.account.to_be_bytes()); - if let Some(change) = derivation.change { - concat_derivation.extend_from_slice(&[0x80, 0]); - concat_derivation.extend_from_slice(&change.to_be_bytes()); - } - concat_derivation - } } impl RemoteWallet for LedgerWallet { @@ -273,14 +268,7 @@ impl RemoteWallet for LedgerWallet { } fn get_pubkey(&self, derivation: DerivationPath) -> Result { - // let ledger_version = self.get_firmware_version()?; - // if ledger_version < FirmwareVersion::new(1, 0, 3) { - // return Err(RemoteWalletError::Protocol( - // "Ledger version 1.0.3 is required", - // )); - // } - - let derivation_path = self.get_derivation_path(derivation); + let derivation_path = get_derivation_path(derivation); let key = self.send_apdu(commands::GET_SOL_PUBKEY, 0, 0, &derivation_path)?; if key.len() != 32 { @@ -294,18 +282,12 @@ impl RemoteWallet for LedgerWallet { derivation: DerivationPath, transaction: Transaction, ) -> Result { - // TODO: see if need version check here - let _version = self.get_firmware_version()?; - // if version < FirmwareVersion::new(1, 0, 8) { - // return Err(RemoteWalletError::Protocol( - // "Signing personal messages with Ledger requires version 1.0.8", - // )); - // } - let mut chunk = [0_u8; MAX_CHUNK_SIZE]; - let derivation_path = self.get_derivation_path(derivation); + let derivation_path = get_derivation_path(derivation); let data = transaction.message_data(); + let _firmware_version = self.get_firmware_version(); + // Copy the address of the key (only done once) chunk[0..derivation_path.len()].copy_from_slice(&derivation_path); @@ -346,12 +328,26 @@ impl RemoteWallet for LedgerWallet { } /// Check if the detected device is a valid `Ledger device` by checking both the product ID and the vendor ID -pub fn is_valid_ledger(vendor_id: u16, _product_id: u16) -> bool { - // vendor_id == LEDGER_VID && LEDGER_PIDS.contains(&product_id) +pub fn is_valid_ledger(vendor_id: u16, product_id: u16) -> bool { vendor_id == LEDGER_VID + && (LEDGER_NANO_S_PIDS.contains(&product_id) || LEDGER_NANO_X_PIDS.contains(&product_id)) +} + +/// Build the derivation path byte array from a DerivationPath selection +fn get_derivation_path(derivation: DerivationPath) -> Vec { + let byte = if derivation.change.is_some() { 4 } else { 3 }; + let mut concat_derivation = vec![byte]; + concat_derivation.extend_from_slice(&SOL_DERIVATION_PATH_BE); + concat_derivation.extend_from_slice(&[0x80, 0]); + concat_derivation.extend_from_slice(&derivation.account.to_be_bytes()); + if let Some(change) = derivation.change { + concat_derivation.extend_from_slice(&[0x80, 0]); + concat_derivation.extend_from_slice(&change.to_be_bytes()); + } + concat_derivation } -/// +/// Choose a Ledger wallet based on matching info fields pub fn get_ledger_from_info( info: RemoteWalletInfo, ) -> Result, RemoteWalletError> {