Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions ledger_device_sdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 = []
Expand Down
1 change: 1 addition & 0 deletions ledger_device_sdk/examples/apdu/test_pki_fail.apdu
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
B0060E00B0010101020102110400000002120100130200021401011604000000002016496E737472756374696F6E5F44657363726970746F723002000F31010D3201213321028E03CDF2147B980260C7800A07199D910D381E6F3F45BF625E805D466E96F03F340101350103154730450221009929503B375B192B1E91AF0B1AA9039E68A399932F30EE74CE376C1C7939CF2002201B1EAB947C29FA7B1D66A15DC8A9208BC363F289EB7EB71F973FF81154094674
2 changes: 2 additions & 0 deletions ledger_device_sdk/examples/apdu/test_pki_flex.apdu
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
=> b0060800a701010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010515473045022100b31ef0a2398641fe938c55568dfc5f1a4297244c765a0c0659821361b812a4c6022023559941a0d58feda2b37008dde3b1b27c85a4aeeaff7d74bc68e093cec4585e
=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437
3 changes: 3 additions & 0 deletions ledger_device_sdk/examples/apdu/test_pki_nanosplus.apdu
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
=> b0060800a601010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe73401013501031546304402207756cfa4c31d0732f45fe12ad824262c1359bd49a89ee847a5322d99023e1d2802204d6b36f57be3dd3d0ff2bbd5ecc71faae5d52899742673200d9f8236a58913e3
=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437

2 changes: 2 additions & 0 deletions ledger_device_sdk/examples/apdu/test_pki_nanox.apdu
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
=> b0060800a701010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010215473045022100b483bb3a8d6b4eef83dd75e7adbfb4330f5c46bbcfaf343b6997b964c6da1dcb022077f1f41f2c7931cf191364d0a4071e1db8ce4fa510bc0e7e7517e7e973f4a1da
=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437
2 changes: 2 additions & 0 deletions ledger_device_sdk/examples/apdu/test_pki_stax.apdu
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
=> b0060800a601010102010211040000000212010013020002140101160400000000200d44796e616d69635f546f6b656e3002000e310108320121332102e3c05b637a0626ab382004a9350beb1de47958a3b9fc1efc6e16a72104c05fe734010135010415463044022037c48a6217ce3ea24351b4deb714a8420a64f985df62e1820d421c6ac8aff75f0220290a3296a4263346866f52520197c6fa2a5e6e32e464833d3598706ee9158db0
=> e00300009a010190020101030201f50406536f6c616e610504474f524b0601060733100100112c333850677a704a597532486b695976563871655046616b423874756f625064476d324646456e374470756d7012000848304602210091d254b2f9e8aad92754fc6275b4d218227ce89370cb724348d5c4426a9e95d6022100d1186c28811f334848867efa10b0611ad2dfdf6b50fcecdd55c11d572241b437
107 changes: 107 additions & 0 deletions ledger_device_sdk/examples/pki.rs
Original file line number Diff line number Diff line change
@@ -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<ApduHeader> for Instruction {
type Error = StatusWords;
fn try_from(value: ApduHeader) -> Result<Self, Self::Error> {
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);
}
}
}
}
}
}
32 changes: 28 additions & 4 deletions ledger_device_sdk/src/io_legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub enum SyscallError {
NotSupported,
InvalidState,
Timeout,
InvalidPkiCertificate,
Unspecified,
}

Expand All @@ -69,6 +70,9 @@ impl From<u32> for SyscallError {
8 => SyscallError::NotSupported,
9 => SyscallError::InvalidState,
10 => SyscallError::Timeout,
0x422F | 0x4230..0x4239 | 0x422D | 0x3301 | 0x422E | 0x5720 | 0x4118 => {
SyscallError::InvalidPkiCertificate
}
_ => SyscallError::Unspecified,
}
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
25 changes: 23 additions & 2 deletions ledger_device_sdk/src/io_new/bolos.rs
Original file line number Diff line number Diff line change
@@ -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<const N: usize>(comm: &mut Comm<N>, 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.
Expand Down Expand Up @@ -53,10 +58,26 @@ pub(crate) fn handle_bolos_apdu<const N: usize>(comm: &mut Comm<N>, 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);
Expand Down
1 change: 1 addition & 0 deletions ledger_device_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
72 changes: 72 additions & 0 deletions ledger_device_sdk/src/pki.rs
Original file line number Diff line number Diff line change
@@ -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<PkiVerifyError> 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)
}
}
2 changes: 1 addition & 1 deletion ledger_secure_sdk_sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions ledger_secure_sdk_sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
],
);

Expand Down