diff --git a/.cargo/config.toml b/.cargo/config.toml index 3fdbcc87..d9de25d2 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,10 +2,10 @@ runner = "speculos -m nanos --display=headless" [target.nanox] -runner = "speculos -m nanox -a 5 --display=headless" +runner = "speculos -m nanox --display=headless" [target.nanosplus] -runner = "speculos -m nanosp -a 1 --display=headless" +runner = "speculos -m nanosp --display=headless" [unstable] build-std = ["core"] diff --git a/ledger_device_sdk/Cargo.toml b/ledger_device_sdk/Cargo.toml index 385d7937..87de2c38 100644 --- a/ledger_device_sdk/Cargo.toml +++ b/ledger_device_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_device_sdk" -version = "1.0.0" +version = "1.0.1" authors = ["yhql"] edition = "2021" license.workspace = true @@ -14,7 +14,7 @@ ledger_device_sdk = { path = ".", features = ["speculos"] } testmacro = { git = "https://github.com/yhql/testmacro"} [dependencies] -ledger_secure_sdk_sys = "1.0.0" +ledger_secure_sdk_sys = "1.0.1" num-traits = { version = "0.2.14", default_features = false } rand_core = { version = "0.6.3", default_features = false } zeroize = { version = "1.6.0", default_features = false } diff --git a/ledger_device_sdk/link.ld b/ledger_device_sdk/link.ld index 4c1ada11..7bee28e9 100644 --- a/ledger_device_sdk/link.ld +++ b/ledger_device_sdk/link.ld @@ -48,8 +48,6 @@ SECTIONS _estack = ABSOLUTE(END_STACK); } > SRAM - .ledger.api_level (INFO): { KEEP(*(.ledger.api_level)) } - .stack_sizes (INFO): { KEEP(*(.stack_sizes)); @@ -63,6 +61,18 @@ SECTIONS *(.ARM.exidx* .gnu.linkonce.armexidx.*) *(.debug_info) } + + ledger.target (INFO): { KEEP(*(ledger.target)) } + ledger.target_id (INFO): { KEEP(*(ledger.target_id)) } + ledger.target_name (INFO): { KEEP(*(ledger.target_name)) } + ledger.app_name (INFO): { KEEP(*(ledger.app_name)) } + ledger.app_version (INFO): { KEEP(*(ledger.app_version)) } + ledger.api_level (INFO): { KEEP(*(ledger.api_level)) } + ledger.sdk_version (INFO): { KEEP(*(ledger.sdk_version)) } + ledger.rust_sdk_version (INFO): { KEEP(*(ledger.rust_sdk_version)) } + ledger.rust_sdk_name (INFO): { KEEP(*(ledger.rust_sdk_name)) } + ledger.sdk_name (INFO): { KEEP(*(ledger.sdk_name)) } + ledger.sdk_hash (INFO): { KEEP(*(ledger.sdk_hash)) } } PROVIDE(_nvram = ABSOLUTE(_nvram_start)); diff --git a/ledger_device_sdk/src/infos.rs b/ledger_device_sdk/src/infos.rs deleted file mode 100644 index 2b5d9529..00000000 --- a/ledger_device_sdk/src/infos.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Expose the API_LEVEL -#[link_section = ".ledger.api_level"] -#[used] -static API_LEVEL: u8 = if cfg!(target_os = "nanos") { - 0 -} else if cfg!(target_os = "nanox") { - 5 -} else if cfg!(target_os = "nanosplus") { - 1 -} else { - 0xff -}; diff --git a/ledger_device_ui_sdk/Cargo.toml b/ledger_device_ui_sdk/Cargo.toml index fc7ce248..9b9c0760 100644 --- a/ledger_device_ui_sdk/Cargo.toml +++ b/ledger_device_ui_sdk/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "ledger_device_ui_sdk" -version = "1.1.0" +version = "1.1.1" authors = ["yhql"] edition = "2021" license.workspace = true description = "Ledger devices abstractions for displaying text, icons, menus and other common gadgets to the screen" [dependencies] -ledger_device_sdk = "1.0.0" -ledger_secure_sdk_sys = "1.0.0" +ledger_device_sdk = "1.0.1" +ledger_secure_sdk_sys = "1.0.1" include_gif = "1.0.0" numtoa = "0.2.4" diff --git a/ledger_secure_sdk_sys/Cargo.toml b/ledger_secure_sdk_sys/Cargo.toml index 68961519..7d129549 100644 --- a/ledger_secure_sdk_sys/Cargo.toml +++ b/ledger_secure_sdk_sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_secure_sdk_sys" -version = "1.0.0" +version = "1.0.1" authors = ["yhql"] edition = "2021" license.workspace = true diff --git a/ledger_secure_sdk_sys/build.rs b/ledger_secure_sdk_sys/build.rs index 919e80d3..3fa464b2 100644 --- a/ledger_secure_sdk_sys/build.rs +++ b/ledger_secure_sdk_sys/build.rs @@ -71,26 +71,180 @@ const CCID_FILES: [&str; 9] = [ "lib_stusb/STM32_USB_Device_Library/Class/CCID/src/usbd_ccid_if.c", ]; -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum Device { NanoS, NanoSPlus, NanoX, } +impl Device { + fn to_string(&self) -> String { + match self { + Device::NanoS => "nanos", + Device::NanoSPlus => "nanos2", + Device::NanoX => "nanox", + } + .to_string() + } +} + +#[derive(Default)] +struct SDKInfo { + pub bolos_sdk: PathBuf, + pub api_level: Option, + pub target_id: String, + pub target_name: String, + pub c_sdk_name: String, + pub c_sdk_hash: String, + pub c_sdk_version: String, +} + +impl SDKInfo { + pub fn new() -> Self { + SDKInfo::default() + } +} + +fn retrieve_sdk_info(device: &Device, path: &Path) -> Result { + let mut sdk_info = SDKInfo::new(); + sdk_info.bolos_sdk = path.to_path_buf(); + (sdk_info.api_level, sdk_info.c_sdk_name) = retrieve_makefile_infos(&sdk_info.bolos_sdk)?; + (sdk_info.target_id, sdk_info.target_name) = + retrieve_target_file_infos(&device, &sdk_info.bolos_sdk)?; + (sdk_info.c_sdk_hash, sdk_info.c_sdk_version) = retrieve_sdk_git_info(&sdk_info.bolos_sdk); + Ok(sdk_info) +} + +fn retrieve_sdk_git_info(bolos_sdk: &Path) -> (String, String) { + let c_sdk_hash = match Command::new("git") + .arg("-C") + .arg(bolos_sdk) + .arg("describe") + .arg("--always") + .arg("--dirty") + .arg("--exclude") + .arg("*") + .arg("--abbrev=40") + .output() + .ok() + { + Some(output) => { + if output.stdout.is_empty() { + "None".to_string() + } else { + String::from_utf8(output.stdout).unwrap_or("None".to_string()) + } + } + None => "None".to_string(), + }; + + let c_sdk_version = match Command::new("git") + .arg("-C") + .arg(bolos_sdk) + .arg("describe") + .arg("--tags") + .arg("--exact-match") + .arg("--match") + .arg("v[0-9]*") + .arg("--dirty") + .output() + .ok() + { + Some(output) => { + if output.stdout.is_empty() { + "None".to_string() + } else { + String::from_utf8(output.stdout).unwrap_or("None".to_string()) + } + } + None => "None".to_string(), + }; + (c_sdk_hash, c_sdk_version) +} + +fn retrieve_makefile_infos(bolos_sdk: &Path) -> Result<(Option, String), SDKBuildError> { + let makefile_defines = + File::open(bolos_sdk.join("Makefile.defines")).expect("Could not find Makefile.defines"); + let mut api_level: Option = None; + let mut sdk_name: Option = None; + for line in BufReader::new(makefile_defines).lines().flatten() { + if let Some(value) = line.split(":=").nth(1).map(str::trim) { + if line.contains("API_LEVEL") && api_level.is_none() { + api_level = Some(value.parse().map_err(|_| SDKBuildError::InvalidAPILevel)?); + } else if line.contains("SDK_NAME") && sdk_name.is_none() { + sdk_name = Some(value.to_string().replace("\"", "")); + } + } + + if api_level.is_some() && sdk_name.is_some() { + // Both keys found, break out of the loop + break; + } + } + let sdk_name = sdk_name.ok_or(SDKBuildError::MissingSDKName)?; + Ok((api_level, sdk_name)) +} + +fn retrieve_target_file_infos( + device: &Device, + bolos_sdk: &Path, +) -> Result<(String, String), SDKBuildError> { + let prefix = if *device == Device::NanoS { + "".to_string() + } else { + format!("target/{}/", device.to_string()) + }; + let target_file_path = bolos_sdk.join(format!("{}include/bolos_target.h", prefix)); + let target_file = + File::open(target_file_path).map_err(|_| SDKBuildError::TargetFileNotFound)?; + let mut target_id: Option = None; + let mut target_name: Option = None; + + for line in BufReader::new(target_file).lines().flatten() { + if target_id.is_none() && line.contains("TARGET_ID") { + target_id = Some( + line.split_whitespace() + .nth(2) + .ok_or("err") + .map_err(|_| SDKBuildError::MissingTargetId)? + .to_string(), + ); + } else if target_name.is_none() && line.contains("TARGET_") { + target_name = Some( + line.split_whitespace() + .nth(1) + .ok_or("err") + .map_err(|_| SDKBuildError::MissingTargetName)? + .to_string(), + ); + } + + if target_id.is_some() && target_name.is_some() { + // Both tokens found, break out of the loop + break; + } + } + + let target_id = target_id.ok_or(SDKBuildError::MissingTargetId)?; + let target_name = target_name.ok_or(SDKBuildError::MissingTargetName)?; + Ok((target_id, target_name)) +} + /// Fetch the appropriate C SDK to build -fn clone_sdk(device: &Device) -> (PathBuf, u32) { - let (repo_url, sdk_branch, api_level) = match device { - Device::NanoS => ("https://github.com/LedgerHQ/nanos-secure-sdk", "master", 0), +fn clone_sdk(device: &Device) -> PathBuf { + let (repo_url, sdk_branch) = match device { + Device::NanoS => ( + Path::new("https://github.com/LedgerHQ/nanos-secure-sdk"), + "master", + ), Device::NanoX => ( - "https://github.com/LedgerHQ/ledger-secure-sdk", + Path::new("https://github.com/LedgerHQ/ledger-secure-sdk"), "API_LEVEL_5", - 5, ), Device::NanoSPlus => ( - "https://github.com/LedgerHQ/ledger-secure-sdk", + Path::new("https://github.com/LedgerHQ/ledger-secure-sdk"), "API_LEVEL_1", - 1, ), }; @@ -99,20 +253,23 @@ fn clone_sdk(device: &Device) -> (PathBuf, u32) { if !bolos_sdk.exists() { Command::new("git") .arg("clone") - .arg(repo_url) + .arg(repo_url.to_str().unwrap()) .arg("-b") .arg(sdk_branch) .arg(bolos_sdk.as_path()) .output() .ok(); } - (bolos_sdk, api_level) + bolos_sdk.to_path_buf() } #[derive(Debug)] enum SDKBuildError { InvalidAPILevel, - CouldNotGetAPILevel, + MissingSDKName, + TargetFileNotFound, + MissingTargetId, + MissingTargetName, } /// Helper function to concatenate all paths in pathlist to bolos_sdk's path @@ -173,42 +330,46 @@ impl SDKBuilder { ), }; self.device = device; + // export TARGET into env for 'infos.rs' + println!("cargo:rustc-env=TARGET={}", self.device.to_string()); println!("cargo:warning=Device is {:?}", self.device); } - /// Manually retrieve API_LEVEL from an SDK in the case of - /// path given through the LEDGER_SDK_PATH env variable - fn retrieve_api_level(bolos_sdk: &Path) -> Result { - let makefile_defines = File::open(bolos_sdk.join("Makefile.defines")) - .expect("Could not find Makefile.defines"); - for line in BufReader::new(makefile_defines).lines().flatten() { - if line.contains("API_LEVEL") { - return line.split(":=").collect::>()[1] - .trim() - .parse() - .map_err(|_| SDKBuildError::InvalidAPILevel); - } - } - Err(SDKBuildError::CouldNotGetAPILevel) - } - pub fn bolos_sdk(&mut self) -> Result<(), SDKBuildError> { println!("cargo:rerun-if-env-changed=LEDGER_SDK_PATH"); - let (bolos_sdk, api_level) = match env::var("LEDGER_SDK_PATH") { + let sdk_path = match env::var("LEDGER_SDK_PATH") { Err(_) => clone_sdk(&self.device), - Ok(path) => { - let sdkpath = Path::new(&path).to_path_buf(); - let apilevel = SDKBuilder::retrieve_api_level(&sdkpath)?; - (sdkpath, apilevel) - } + Ok(path) => PathBuf::from(path), }; - self.bolos_sdk = bolos_sdk; - self.api_level = api_level; - // export API_LEVEL into env for 'infos.rs' - println!("cargo:rustc-env=API_LEVEL={}", self.api_level); - println!("cargo:warning=API_LEVEL is {}", self.api_level); + let sdk_info = retrieve_sdk_info(&self.device, &sdk_path)?; + + self.bolos_sdk = sdk_info.bolos_sdk; + match sdk_info.api_level { + Some(api_level) => { + self.api_level = api_level; + // Export api level into env for 'infos.rs' + println!("cargo:rustc-env=API_LEVEL={}", self.api_level); + println!("cargo:warning=API_LEVEL is {}", self.api_level); + } + None => { + if self.device != Device::NanoS { + return Err(SDKBuildError::InvalidAPILevel); + } + } + } + // Export other SDK infos into env for 'infos.rs' + println!("cargo:rustc-env=TARGET_ID={}", sdk_info.target_id); + println!("cargo:warning=TARGET_ID is {}", sdk_info.target_id); + println!("cargo:rustc-env=TARGET_NAME={}", sdk_info.target_name); + println!("cargo:warning=TARGET_NAME is {}", sdk_info.target_name); + println!("cargo:rustc-env=C_SDK_NAME={}", sdk_info.c_sdk_name); + println!("cargo:warning=C_SDK_NAME is {}", sdk_info.c_sdk_name); + println!("cargo:rustc-env=C_SDK_HASH={}", sdk_info.c_sdk_hash); + println!("cargo:warning=C_SDK_HASH is {}", sdk_info.c_sdk_hash); + println!("cargo:rustc-env=C_SDK_VERSION={}", sdk_info.c_sdk_version); + println!("cargo:warning=C_SDK_VERSION is {}", sdk_info.c_sdk_version); Ok(()) } diff --git a/ledger_secure_sdk_sys/src/infos.rs b/ledger_secure_sdk_sys/src/infos.rs index 6e64a6bb..3fcd805b 100644 --- a/ledger_secure_sdk_sys/src/infos.rs +++ b/ledger_secure_sdk_sys/src/infos.rs @@ -1,3 +1,23 @@ +const fn make_c_string(s: &str) -> [u8; N] { + let mut result = [0u8; N]; + let mut i = 0; + while i != s.len() { + result[i] = s.as_bytes()[i]; + i += 1; + } + result[i] = '\n' as u8; + result +} + +macro_rules! const_cstr { + ($name: ident, $section: literal, $in_str: expr) => { + #[used] + #[link_section = $section] + static $name: [u8; $in_str.len() + 1] = make_c_string($in_str); + }; +} + +#[cfg(not(target_os = "nanos"))] const fn const_parse_api_level(x: &str) -> u8 { let a = x.as_bytes(); let mut res = a[0] - b'0'; @@ -11,6 +31,31 @@ const fn const_parse_api_level(x: &str) -> u8 { } /// Expose the API_LEVEL -#[link_section = ".ledger.api_level"] #[used] +#[cfg(not(target_os = "nanos"))] static API_LEVEL: u8 = const_parse_api_level(env!("API_LEVEL")); + +// Store metadata in the ELF file +#[cfg(not(target_os = "nanos"))] +const_cstr!(ELF_API_LEVEL, "ledger.api_level", env!("API_LEVEL")); + +const_cstr!(ELF_TARGET, "ledger.target", env!("TARGET")); +const_cstr!(ELF_TARGET_ID, "ledger.target_id", env!("TARGET_ID")); +const_cstr!(ELF_TARGET_NAME, "ledger.target_name", env!("TARGET_NAME")); +const_cstr!( + ELF_RUST_SDK_NAME, + "ledger.rust_sdk_name", + env!("CARGO_PKG_NAME") +); +const_cstr!( + ELF_RUST_SDK_VERSION, + "ledger.rust_sdk_version", + env!("CARGO_PKG_VERSION") +); +const_cstr!(ELF_C_SDK_NAME, "ledger.sdk_name", env!("C_SDK_NAME")); +const_cstr!(ELF_C_SDK_HASH, "ledger.sdk_hash", env!("C_SDK_HASH")); +const_cstr!( + ELF_C_SDK_VERSION, + "ledger.sdk_version", + env!("C_SDK_VERSION") +);