Skip to content

Commit 52bec2a

Browse files
authored
Merge pull request #225 from LedgerHQ/y333_241014/swap_support
Swap support
2 parents e9f8962 + 0c28f1c commit 52bec2a

File tree

11 files changed

+557
-65
lines changed

11 files changed

+557
-65
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ledger_device_sdk/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ledger_device_sdk"
3-
version = "1.18.4"
3+
version = "1.19.0"
44
authors = ["yhql", "yogh333", "agrojean-ledger", "kingofpayne"]
55
edition = "2021"
66
license.workspace = true
@@ -21,9 +21,10 @@ rand_core = { version = "0.6.3", default-features = false }
2121
zeroize = { version = "1.6.0", default-features = false }
2222
numtoa = "0.2.4"
2323
const-zero = "0.1.1"
24-
ledger_secure_sdk_sys = { path = "../ledger_secure_sdk_sys", version = "1.5.3" }
24+
ledger_secure_sdk_sys = { path = "../ledger_secure_sdk_sys", version = "1.6.0" }
2525

2626
[features]
27+
debug = []
2728
speculos = []
2829
ccid = []
2930
heap = [ "ledger_secure_sdk_sys/heap" ]

ledger_device_sdk/src/io.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl Comm {
170170
/// Send the currently held APDU
171171
// This is private. Users should call reply to set the satus word and
172172
// transmit the response.
173-
fn apdu_send(&mut self) {
173+
fn apdu_send(&mut self, is_swap: bool) {
174174
if !sys_seph::is_status_sent() {
175175
sys_seph::send_general_status()
176176
}
@@ -203,6 +203,13 @@ impl Comm {
203203
}
204204
_ => (),
205205
}
206+
if is_swap {
207+
if !sys_seph::is_status_sent() {
208+
sys_seph::send_general_status()
209+
}
210+
sys_seph::seph_recv(&mut spi_buffer, 0);
211+
seph::handle_event(&mut self.apdu_buffer, &spi_buffer);
212+
}
206213
self.tx = 0;
207214
self.rx = 0;
208215
unsafe {
@@ -506,7 +513,17 @@ impl Comm {
506513
self.apdu_buffer[self.tx + 1] = sw as u8;
507514
self.tx += 2;
508515
// Transmit the response
509-
self.apdu_send();
516+
self.apdu_send(false);
517+
}
518+
519+
pub fn swap_reply<T: Into<Reply>>(&mut self, reply: T) {
520+
let sw = reply.into().0;
521+
// Append status word
522+
self.apdu_buffer[self.tx] = (sw >> 8) as u8;
523+
self.apdu_buffer[self.tx + 1] = sw as u8;
524+
self.tx += 2;
525+
// Transmit the response
526+
self.apdu_send(true);
510527
}
511528

512529
/// Set the Status Word of the response to `StatusWords::OK` (which is equal
@@ -515,6 +532,10 @@ impl Comm {
515532
self.reply(StatusWords::Ok);
516533
}
517534

535+
pub fn swap_reply_ok(&mut self) {
536+
self.swap_reply(StatusWords::Ok);
537+
}
538+
518539
/// Return APDU Metadata
519540
pub fn get_apdu_metadata(&self) -> &ApduHeader {
520541
assert!(self.apdu_buffer.len() >= 4);
@@ -552,10 +573,8 @@ impl Comm {
552573
}
553574

554575
pub fn append(&mut self, m: &[u8]) {
555-
for c in m.iter() {
556-
self.apdu_buffer[self.tx] = *c;
557-
self.tx += 1;
558-
}
576+
self.apdu_buffer[self.tx..self.tx + m.len()].copy_from_slice(m);
577+
self.tx += m.len();
559578
}
560579
}
561580

ledger_device_sdk/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod ecc;
1616
pub mod hash;
1717
pub mod hmac;
1818
pub mod io;
19+
pub mod libcall;
1920
pub mod nvm;
2021
pub mod random;
2122
pub mod screen;

ledger_device_sdk/src/libcall.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use crate::testing::debug_print;
2+
3+
use ledger_secure_sdk_sys::{libargs_t, CHECK_ADDRESS, GET_PRINTABLE_AMOUNT, SIGN_TRANSACTION};
4+
5+
pub mod string;
6+
pub mod swap;
7+
8+
pub enum LibCallCommand {
9+
SwapSignTransaction,
10+
SwapGetPrintableAmount,
11+
SwapCheckAddress,
12+
}
13+
14+
impl From<u32> for LibCallCommand {
15+
fn from(command: u32) -> Self {
16+
match command {
17+
SIGN_TRANSACTION => LibCallCommand::SwapSignTransaction,
18+
GET_PRINTABLE_AMOUNT => LibCallCommand::SwapGetPrintableAmount,
19+
CHECK_ADDRESS => LibCallCommand::SwapCheckAddress,
20+
_ => panic!("Unknown command"),
21+
}
22+
}
23+
}
24+
25+
pub fn get_command(arg0: u32) -> LibCallCommand {
26+
debug_print("GET_CMD\n");
27+
let mut libarg: libargs_t = libargs_t::default();
28+
29+
let arg = arg0 as *const u32;
30+
31+
libarg.id = unsafe { *arg };
32+
libarg.command = unsafe { *arg.add(1) };
33+
libarg.unused = unsafe { *arg.add(2) };
34+
libarg.command.into()
35+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#[derive(Debug, Copy, Clone)]
2+
pub struct CustomString<const N: usize> {
3+
pub arr: [u8; N],
4+
pub capacity: usize,
5+
pub len: usize,
6+
}
7+
8+
impl<const N: usize> Default for CustomString<N> {
9+
fn default() -> Self {
10+
Self {
11+
arr: [b'0'; N],
12+
capacity: N,
13+
len: 0,
14+
}
15+
}
16+
}
17+
18+
impl<const N: usize> CustomString<N> {
19+
pub fn new() -> Self {
20+
Self {
21+
arr: [b'0'; N],
22+
capacity: N,
23+
len: 0,
24+
}
25+
}
26+
27+
pub fn clear(&mut self) {
28+
self.arr.fill(0);
29+
self.len = 0;
30+
}
31+
32+
pub fn as_str(&self) -> &str {
33+
core::str::from_utf8(&self.arr[..self.len]).unwrap()
34+
}
35+
36+
pub fn copy_from(&mut self, s: &CustomString<N>) {
37+
self.arr[..s.len].copy_from_slice(&s.arr[..s.len]);
38+
self.len = s.len;
39+
}
40+
}
41+
42+
impl From<u8> for CustomString<2> {
43+
fn from(val: u8) -> Self {
44+
let mut s = CustomString::<2>::new();
45+
let mut i: usize = 0;
46+
for c in val.to_be_bytes().into_iter() {
47+
let (c0, c1) = byte_to_hex(c);
48+
s.arr[i] = c0 as u8;
49+
s.arr[i + 1] = c1 as u8;
50+
s.len += 2;
51+
i += 2;
52+
}
53+
s
54+
}
55+
}
56+
57+
impl From<u16> for CustomString<4> {
58+
fn from(val: u16) -> Self {
59+
let mut s = CustomString::<4>::new();
60+
let mut i: usize = 0;
61+
for c in val.to_be_bytes().into_iter() {
62+
let (c0, c1) = byte_to_hex(c);
63+
s.arr[i] = c0 as u8;
64+
s.arr[i + 1] = c1 as u8;
65+
s.len += 2;
66+
i += 2;
67+
}
68+
s
69+
}
70+
}
71+
72+
impl From<u32> for CustomString<8> {
73+
fn from(val: u32) -> Self {
74+
let mut s = CustomString::<8>::new();
75+
let mut i: usize = 0;
76+
for c in val.to_be_bytes().into_iter() {
77+
let (c0, c1) = byte_to_hex(c);
78+
s.arr[i] = c0 as u8;
79+
s.arr[i + 1] = c1 as u8;
80+
s.len += 2;
81+
i += 2;
82+
}
83+
s
84+
}
85+
}
86+
87+
impl From<[u8; 32]> for CustomString<64> {
88+
fn from(arr: [u8; 32]) -> Self {
89+
let mut s = CustomString::<64>::new();
90+
let mut i: usize = 0;
91+
for c in arr.into_iter() {
92+
let (c0, c1) = byte_to_hex(c);
93+
s.arr[i] = c0 as u8;
94+
s.arr[i + 1] = c1 as u8;
95+
s.len += 2;
96+
i += 2;
97+
}
98+
s
99+
}
100+
}
101+
102+
impl<const N: usize> TryFrom<&str> for CustomString<N> {
103+
type Error = &'static str;
104+
fn try_from(st: &str) -> Result<Self, Self::Error> {
105+
if N >= st.len() {
106+
let mut s = CustomString::<N>::new();
107+
s.arr[..st.len()].copy_from_slice(st.as_bytes());
108+
s.len = st.len();
109+
Ok(s)
110+
} else {
111+
Err("CustomString's capacity overflow!")
112+
}
113+
}
114+
}
115+
116+
/// Output an uint256 as an decimal CustomString
117+
/// For instance:
118+
///
119+
/// let val: [u8; 32] = token amount (32 bytes / 256 bits);
120+
/// let s: CustomString<79> = uint256_to_integer(&val); // max number of decimal digits for Uint256 = 78 (+ 1 spare for '.')
121+
/// testing::debug_print(s.print().unwrap());
122+
pub fn uint256_to_integer(value: &[u8; 32]) -> CustomString<79> {
123+
let mut s: CustomString<79> = CustomString::new();
124+
125+
// Special case when value is 0
126+
if *value == [0u8; 32] {
127+
s.arr[0] = b'0';
128+
s.len = 1;
129+
return s;
130+
}
131+
132+
let mut n: [u16; 16] = [0u16; 16];
133+
for idx in 0..16 {
134+
n[idx] = u16::from_be_bytes([value[2 * idx], value[2 * idx + 1]]);
135+
}
136+
137+
let mut pos: usize = s.capacity;
138+
while n != [0u16; 16] {
139+
if pos == 0 {
140+
return s;
141+
}
142+
pos -= 1;
143+
let mut carry = 0u32;
144+
let mut rem: u32;
145+
for i in 0..16 {
146+
rem = ((carry << 16) | u32::from(n[i])) % 10;
147+
n[i] = (((carry << 16) | u32::from(n[i])) / 10) as u16;
148+
carry = rem;
149+
}
150+
s.arr[pos] = u8::try_from(char::from_digit(carry, 10).unwrap()).unwrap();
151+
}
152+
s.arr.copy_within(pos.., 0);
153+
s.len = s.capacity - pos;
154+
s
155+
}
156+
157+
/// Output an uint256 as a float string
158+
pub fn uint256_to_float(value: &[u8; 32], decimals: usize) -> CustomString<79> {
159+
let mut s: CustomString<79> = uint256_to_integer(value);
160+
161+
if decimals == 0 || s.arr[0] == b'0' {
162+
return s;
163+
}
164+
165+
if s.len <= decimals {
166+
s.arr.copy_within(0..s.len, 2 + decimals - s.len);
167+
s.arr[0..2 + decimals - s.len].fill(b'0');
168+
s.arr[1] = b'.';
169+
s.len += 2 + decimals - s.len;
170+
} else {
171+
s.arr
172+
.copy_within(s.len - decimals..s.len, s.len - decimals + 1);
173+
s.arr[s.len - decimals] = b'.';
174+
s.len += 1;
175+
}
176+
s
177+
}
178+
179+
fn byte_to_hex(b: u8) -> (char, char) {
180+
let c0 = char::from_digit((b >> 4).into(), 16).unwrap();
181+
let c1 = char::from_digit((b & 0xf).into(), 16).unwrap();
182+
(c0, c1)
183+
}

0 commit comments

Comments
 (0)