diff --git a/Cargo.lock b/Cargo.lock index 3bcda5fa..0c9a9c18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,7 +327,7 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "ledger_device_sdk" -version = "1.27.1" +version = "1.28.0" dependencies = [ "const-zero", "include_gif", @@ -342,7 +342,7 @@ dependencies = [ [[package]] name = "ledger_secure_sdk_sys" -version = "1.11.3" +version = "1.11.4" dependencies = [ "bindgen", "cc", diff --git a/README.md b/README.md index 61c736ef..634ab43c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ | [ledger_device_sdk](./ledger_device_sdk) | Rust SDK | ![Dynamic TOML Badge](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FLedgerHQ%2Fledger-device-rust-sdk%2Frefs%2Fheads%2Fmaster%2Fledger_device_sdk%2FCargo.toml&query=%24.package.version&label=version) | | [ledger_secure_sdk_sys](./ledger_secure_sdk_sys) | [C SDK](https://github.com/LedgerHQ/ledger-secure-sdk) bindings | ![Dynamic TOML Badge](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FLedgerHQ%2Fledger-device-rust-sdk%2Frefs%2Fheads%2Fmaster%2Fledger_secure_sdk_sys%2FCargo.toml&query=%24.package.version&label=version) | | [include_gif](./include_gif) | Procedural macro to integrate logo in the UI/UX | ![Dynamic TOML Badge](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FLedgerHQ%2Fledger-device-rust-sdk%2Frefs%2Fheads%2Fmaster%2Finclude_gif%2FCargo.toml&query=%24.package.version&label=version) | -| [testmacro](./testmacro) | Procedural macro used by unit and integrations tests | ![Dynamic TOML Badge](https://img.shields.io/badge/dynamic/toml?url=https%3A%2F%2Fraw.githubusercontent.com%2FLedgerHQ%2Fledger-device-rust-sdk%2Frefs%2Fheads%2Fmaster%2Ftestmacro%2FCargo.toml&query=%24.package.version&label=version) | ## Docker builder diff --git a/ledger_device_sdk/Cargo.toml b/ledger_device_sdk/Cargo.toml index 62865f04..4c2b110e 100644 --- a/ledger_device_sdk/Cargo.toml +++ b/ledger_device_sdk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ledger_device_sdk" -version = "1.27.1" +version = "1.28.0" authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"] edition = "2021" license.workspace = true @@ -20,7 +20,7 @@ rand_core = { version = "0.6.3", default-features = false } zeroize = { version = "1.6.0", default-features = false } numtoa = "0.2.4" const-zero = "0.1.1" -ledger_secure_sdk_sys = { path = "../ledger_secure_sdk_sys", version = "1.11.3" } +ledger_secure_sdk_sys = { path = "../ledger_secure_sdk_sys", version = "1.11.4" } [features] debug = [] diff --git a/ledger_device_sdk/examples/apdu/test_pki_fail.apdu b/ledger_device_sdk/examples/apdu/test_pki_fail.apdu new file mode 100644 index 00000000..649051a7 --- /dev/null +++ b/ledger_device_sdk/examples/apdu/test_pki_fail.apdu @@ -0,0 +1 @@ +B0060E00B0010101020102110400000002120100130200021401011604000000002016496E737472756374696F6E5F44657363726970746F723002000F31010D3201213321028E03CDF2147B980260C7800A07199D910D381E6F3F45BF625E805D466E96F03F340101350103154730450221009929503B375B192B1E91AF0B1AA9039E68A399932F30EE74CE376C1C7939CF2002201B1EAB947C29FA7B1D66A15DC8A9208BC363F289EB7EB71F973FF81154094674 diff --git a/ledger_device_sdk/examples/apdu/test_pki_flex.apdu b/ledger_device_sdk/examples/apdu/test_pki_flex.apdu new file mode 100644 index 00000000..8e10f5ed --- /dev/null +++ b/ledger_device_sdk/examples/apdu/test_pki_flex.apdu @@ -0,0 +1,2 @@ +=> b0060800a701010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010515473045022100b31ef0a2398641fe938c55568dfc5f1a4297244c765a0c0659821361b812a4c6022023559941a0d58feda2b37008dde3b1b27c85a4aeeaff7d74bc68e093cec4585e +=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437 diff --git a/ledger_device_sdk/examples/apdu/test_pki_nanosplus.apdu b/ledger_device_sdk/examples/apdu/test_pki_nanosplus.apdu new file mode 100644 index 00000000..e25b8192 --- /dev/null +++ b/ledger_device_sdk/examples/apdu/test_pki_nanosplus.apdu @@ -0,0 +1,3 @@ +=> b0060800a601010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe73401013501031546304402207756cfa4c31d0732f45fe12ad824262c1359bd49a89ee847a5322d99023e1d2802204d6b36f57be3dd3d0ff2bbd5ecc71faae5d52899742673200d9f8236a58913e3 +=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437 + \ No newline at end of file diff --git a/ledger_device_sdk/examples/apdu/test_pki_nanox.apdu b/ledger_device_sdk/examples/apdu/test_pki_nanox.apdu new file mode 100644 index 00000000..7a4aa81f --- /dev/null +++ b/ledger_device_sdk/examples/apdu/test_pki_nanox.apdu @@ -0,0 +1,2 @@ +=> b0060800a701010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010215473045022100b483bb3a8d6b4eef83dd75e7adbfb4330f5c46bbcfaf343b6997b964c6da1dcb022077f1f41f2c7931cf191364d0a4071e1db8ce4fa510bc0e7e7517e7e973f4a1da +=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437 diff --git a/ledger_device_sdk/examples/apdu/test_pki_stax.apdu b/ledger_device_sdk/examples/apdu/test_pki_stax.apdu new file mode 100644 index 00000000..8ffe4a69 --- /dev/null +++ b/ledger_device_sdk/examples/apdu/test_pki_stax.apdu @@ -0,0 +1,2 @@ +=> b0060800a601010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010415463044022037c48a6217ce3ea24351b4deb714a8420a64f985df62e1820d421c6ac8aff75f0220290a3296a4263346866f52520197c6fa2a5e6e32e464833d3598706ee9158db0 +=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437 diff --git a/ledger_device_sdk/examples/pki.rs b/ledger_device_sdk/examples/pki.rs new file mode 100644 index 00000000..703a0d6c --- /dev/null +++ b/ledger_device_sdk/examples/pki.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] + +use include_gif::include_gif; +use ledger_device_sdk::ecc::CurvesId; +use ledger_device_sdk::hash::HashInit; +use ledger_device_sdk::io::*; +use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings}; +use ledger_device_sdk::pki::pki_verify_data; + +ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic); +pub enum Instruction { + GetVersion = 0x01, + GetAppName = 0x02, + CheckPki = 0x03, +} + +impl TryFrom for Instruction { + type Error = StatusWords; + fn try_from(value: ApduHeader) -> Result { + match value.ins { + 1 => Ok(Instruction::GetVersion), + 2 => Ok(Instruction::GetAppName), + 3 => Ok(Instruction::CheckPki), + _ => Err(StatusWords::NothingReceived), + } + } +} + +#[no_mangle] +extern "C" fn sample_main() { + let mut comm = Comm::new(); + + #[cfg(target_os = "apex_p")] + const FERRIS: NbglGlyph = + NbglGlyph::from_include(include_gif!("examples/crab_48x48.png", NBGL)); + #[cfg(any(target_os = "stax", target_os = "flex"))] + const FERRIS: NbglGlyph = + NbglGlyph::from_include(include_gif!("examples/crab_64x64.gif", NBGL)); + #[cfg(any(target_os = "nanosplus", target_os = "nanox"))] + const FERRIS: NbglGlyph = + NbglGlyph::from_include(include_gif!("examples/crab_14x14.png", NBGL)); + + let mut home = NbglHomeAndSettings::new() + .glyph(&FERRIS) + .infos( + "PKI Example App", + env!("CARGO_PKG_VERSION"), + env!("CARGO_PKG_AUTHORS"), + ) + .tagline("Send APDU from apdu folder"); + + home.show_and_return(); + + loop { + let ins: Instruction = comm.next_command(); + match ins { + Instruction::GetVersion => { + ledger_device_sdk::testing::debug_print("GetVersion\n"); + let version = [0, 1, 0]; // version 0.1.0 + comm.append(&version); + comm.reply_ok(); + } + Instruction::GetAppName => { + let app_name = b"PKI Example"; + comm.append(app_name); + comm.reply_ok(); + } + Instruction::CheckPki => { + ledger_device_sdk::testing::debug_print("Starting PKI Check\n"); + let buffer = match comm.get_data() { + Ok(buf) => buf, + Err(_err) => { + ledger_device_sdk::testing::debug_print("Failed to get data: {}\n"); + break; + } + }; + + let mut data = [0u8; 80]; + data.copy_from_slice(&buffer[..80]); + + let mut hasher = ledger_device_sdk::hash::sha2::Sha2_256::new(); + let mut hash_data = [0u8; 32]; + let _ = hasher.hash(&data[..], &mut hash_data); + + let mut signature = [0u8; 72]; + signature.copy_from_slice(&buffer[82..]); + + match pki_verify_data( + &mut hash_data[..], + 8u8, + CurvesId::Secp256k1, + signature.as_mut_slice(), + ) { + Ok(()) => { + ledger_device_sdk::testing::debug_print("PKI Check successful\n"); + comm.reply_ok(); + } + Err(err) => { + ledger_device_sdk::testing::debug_print("PKI Check failed\n"); + comm.reply(err); + } + } + } + } + } +} diff --git a/ledger_device_sdk/src/io_legacy.rs b/ledger_device_sdk/src/io_legacy.rs index b1871249..d1a73ce1 100644 --- a/ledger_device_sdk/src/io_legacy.rs +++ b/ledger_device_sdk/src/io_legacy.rs @@ -54,6 +54,7 @@ pub enum SyscallError { NotSupported, InvalidState, Timeout, + InvalidPkiCertificate, Unspecified, } @@ -69,6 +70,9 @@ impl From for SyscallError { 8 => SyscallError::NotSupported, 9 => SyscallError::InvalidState, 10 => SyscallError::Timeout, + 0x422F | 0x4230..0x4239 | 0x422D | 0x3301 | 0x422E | 0x5720 | 0x4118 => { + SyscallError::InvalidPkiCertificate + } _ => SyscallError::Unspecified, } } @@ -326,8 +330,8 @@ impl Comm { return None; } - // Manage BOLOS specific APDUs B0xx0000 - if self.io_buffer[1] == 0xB0 && self.io_buffer[3] == 0x00 && self.io_buffer[4] == 0x00 { + // Manage BOLOS specific APDUs B0xxyyzz + if self.io_buffer[1] == 0xB0 { handle_bolos_apdu(self, self.io_buffer[2]); return None; } @@ -682,11 +686,15 @@ fn default_nbgl_reply_status(reply: Reply) { } } +pub(crate) const BOLOS_INS_GET_VERSION: u8 = 0x01; +pub(crate) const BOLOS_INS_QUIT: u8 = 0xa7; +pub(crate) const BOLOS_INS_SET_PKI_CERT: u8 = 0x06; + // BOLOS APDU Handling (see https://developers.ledger.com/docs/connectivity/ledgerJS/open-close-info-on-apps) fn handle_bolos_apdu(com: &mut Comm, ins: u8) { match ins { // Get Information INS: retrieve App name and version - 0x01 => { + BOLOS_INS_GET_VERSION => { unsafe { com.tx_length = 0; com.io_buffer[com.tx_length] = 0x01; @@ -717,10 +725,26 @@ fn handle_bolos_apdu(com: &mut Comm, ins: u8) { com.reply_ok(); } // Quit Application INS - 0xa7 => { + BOLOS_INS_QUIT => { com.reply_ok(); crate::exit_app(0); } + BOLOS_INS_SET_PKI_CERT => unsafe { + let public_key = cx_ecfp_384_public_key_t::default(); + let err = os_pki_load_certificate( + com.io_buffer[3], // P1 + com.io_buffer[6..].as_mut_ptr(), // Data + com.io_buffer[5] as usize, // Length + core::ptr::null_mut(), + core::ptr::null_mut(), + &public_key as *const cx_ecfp_384_public_key_t as *mut cx_ecfp_384_public_key_t, + ); + if err != 0 { + com.reply(SyscallError::from(err)); + return; + } + com.reply_ok(); + }, _ => { com.reply(StatusWords::BadIns); } diff --git a/ledger_device_sdk/src/io_new/bolos.rs b/ledger_device_sdk/src/io_new/bolos.rs index 51b01472..c8f1a344 100644 --- a/ledger_device_sdk/src/io_new/bolos.rs +++ b/ledger_device_sdk/src/io_new/bolos.rs @@ -1,11 +1,16 @@ use super::{Comm, StatusWords, DEFAULT_BUF_SIZE}; use ledger_secure_sdk_sys::*; +use crate::io_legacy::SyscallError; +use crate::io_legacy::BOLOS_INS_GET_VERSION; +use crate::io_legacy::BOLOS_INS_QUIT; +use crate::io_legacy::BOLOS_INS_SET_PKI_CERT; + /// Handle internal BOLOS APDUs (CLA = 0xB0, P1 = 0x00, P2 = 0x00). pub(crate) fn handle_bolos_apdu(comm: &mut Comm, ins: u8) { match ins { // Get Information INS: retrieve App name and version - 0x01 => { + BOLOS_INS_GET_VERSION => { let mut response = comm.begin_response(); let _ = response.append(&[0x01]); const MAX_TAG_LENGTH: u8 = 32; // maximum length for the buffer containing app name/version. @@ -53,10 +58,26 @@ pub(crate) fn handle_bolos_apdu(comm: &mut Comm, ins: u8) { let _ = response.send(StatusWords::Ok); } // Quit Application INS - 0xA7 | 0xa7 => { + BOLOS_INS_QUIT => { let _ = comm.begin_response().send(StatusWords::Ok); crate::exit_app(0); } + BOLOS_INS_SET_PKI_CERT => unsafe { + let public_key = cx_ecfp_384_public_key_t::default(); + let err = os_pki_load_certificate( + comm.buf[3], // P1 + comm.buf[6..].as_mut_ptr(), // Data + comm.buf[5] as usize, // Length + core::ptr::null_mut(), + core::ptr::null_mut(), + &public_key as *const cx_ecfp_384_public_key_t as *mut cx_ecfp_384_public_key_t, + ); + if err != 0 { + comm.begin_response().send(SyscallError::from(err)); + return; + } + comm.begin_response().send(StatusWords::Ok); + }, // Unknown INS within BOLOS namespace _ => { let _ = comm.begin_response().send(StatusWords::BadIns); diff --git a/ledger_device_sdk/src/lib.rs b/ledger_device_sdk/src/lib.rs index afe892cc..0949d66e 100644 --- a/ledger_device_sdk/src/lib.rs +++ b/ledger_device_sdk/src/lib.rs @@ -26,6 +26,7 @@ pub mod io { pub mod libcall; pub mod math; pub mod nvm; +pub mod pki; pub mod random; pub mod screen; pub mod seph; diff --git a/ledger_device_sdk/src/pki.rs b/ledger_device_sdk/src/pki.rs new file mode 100644 index 00000000..43f22429 --- /dev/null +++ b/ledger_device_sdk/src/pki.rs @@ -0,0 +1,72 @@ +use crate::ecc::CurvesId; +use crate::io::Reply; +use ledger_secure_sdk_sys::{ + cx_ecfp_384_public_key_t, os_pki_get_info, os_pki_verify, CERTIFICATE_TRUSTED_NAME_MAXLEN, +}; + +/// PKI verification errors +pub enum PkiVerifyError { + Success = 0, + MissingCertificate = 1, + WrongCertificateUsage = 2, + WrongCertificateCurve = 3, + WrongSignature = 4, +} + +impl From for Reply { + fn from(exc: PkiVerifyError) -> Reply { + Reply(0x6900 + exc as u16) + } +} +/// Verify data using the loaded certificate +/// # Arguments +/// * `data` - The data to verify. +/// * `expected_key_usage` - The expected key usage of the certificate. +/// * `expected_curve` - The expected curve of the certificate. See `CurvesId` enum +/// * `signature` - The signature to verify +/// # Returns +/// * `Ok(())` if the verification is successful +/// * `Err(PkiVerifyError)` if the verification fails +pub fn pki_verify_data( + data: &mut [u8], + expected_key_usage: u8, + expected_curve: CurvesId, + signature: &mut [u8], +) -> Result<(), PkiVerifyError> { + let certificate_name = [0u8; CERTIFICATE_TRUSTED_NAME_MAXLEN as usize]; + let mut certficate_name_len: usize = 0; + let mut key_usage: u8 = 0; + let mut pub_key = cx_ecfp_384_public_key_t::default(); + + let err = unsafe { + os_pki_get_info( + &mut key_usage as *mut u8, + certificate_name.as_ptr() as *mut u8, + &mut certficate_name_len as *mut usize, + &mut pub_key as *mut cx_ecfp_384_public_key_t, + ) + }; + if err != 0 { + return Err(PkiVerifyError::MissingCertificate); + } + if key_usage != expected_key_usage { + return Err(PkiVerifyError::WrongCertificateUsage); + } + if pub_key.curve != expected_curve as u8 { + return Err(PkiVerifyError::WrongCertificateCurve); + } + + let err = unsafe { + os_pki_verify( + data.as_mut_ptr() as *mut u8, + data.len(), + signature.as_mut_ptr() as *mut u8, + signature.len(), + ) + }; + if err == true { + Ok(()) + } else { + Err(PkiVerifyError::WrongSignature) + } +} diff --git a/ledger_secure_sdk_sys/Cargo.toml b/ledger_secure_sdk_sys/Cargo.toml index 54adc101..f7234b2c 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.11.3" +version = "1.11.4" authors = ["yhql", "agrojean-ledger", "yogh333"] edition = "2021" license.workspace = true diff --git a/ledger_secure_sdk_sys/build.rs b/ledger_secure_sdk_sys/build.rs index 5fb51b56..31df2c94 100644 --- a/ledger_secure_sdk_sys/build.rs +++ b/ledger_secure_sdk_sys/build.rs @@ -588,6 +588,7 @@ impl SDKBuilder<'_> { "include/syscalls.h", "include/os_ux.h", "lib_standard_app/swap_lib_calls.h", + "include/os_pki.h", /* pki */ ], );