Skip to content

Commit

Permalink
Use of heap feature for simpler code
Browse files Browse the repository at this point in the history
  • Loading branch information
kingofpayne authored and yogh333 committed Jun 4, 2024
1 parent 57d01bd commit 9ec3374
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 93 deletions.
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ runner = "speculos -a=1 --model=nanosp"
target = "nanosplus"

[unstable]
build-std = ["core"]
build-std = ["core", "alloc"]
build-std-features = ["compiler-builtins-mem"]
28 changes: 26 additions & 2 deletions Cargo.lock

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

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ authors = ["yhql", "agrojean-ledger"]
edition = "2021"

[dependencies]
ledger_device_sdk = "1.9.2"
ledger_device_sdk = { version="1.10.0", features = ["heap"] }
include_gif = "1.1.0"
serde = {version="1.0.192", default_features = false, features = ["derive"]}
serde-json-core = { git = "https://github.com/rust-embedded-community/serde-json-core"}
hex = { version = "0.4.3", default-features = false, features = ["serde"] }
hex = { version = "0.4.3", default-features = false, features = ["serde", "alloc"] }
numtoa = "0.2.4"

[profile.release]
Expand Down
14 changes: 5 additions & 9 deletions src/app_ui/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*****************************************************************************/

use crate::AppSW;
use alloc::format;
use core::str::from_utf8_mut;

#[cfg(not(any(target_os = "stax", target_os = "flex")))]
Expand All @@ -34,15 +35,10 @@ use include_gif::include_gif;
const DISPLAY_ADDR_BYTES_LEN: usize = 20;

pub fn ui_display_pk(addr: &[u8]) -> Result<bool, AppSW> {
let mut addr_hex = [0u8; DISPLAY_ADDR_BYTES_LEN * 2 + 2];
addr_hex[..2].copy_from_slice("0x".as_bytes());
hex::encode_to_slice(
&addr[addr.len() - DISPLAY_ADDR_BYTES_LEN..],
&mut addr_hex[2..],
)
.unwrap();
let addr_hex = from_utf8_mut(&mut addr_hex).unwrap();
addr_hex[2..].make_ascii_uppercase();
let addr_hex = format!(
"0x{}",
hex::encode(&addr[addr.len() - DISPLAY_ADDR_BYTES_LEN..]).to_uppercase()
);

#[cfg(not(any(target_os = "stax", target_os = "flex")))]
{
Expand Down
24 changes: 6 additions & 18 deletions src/app_ui/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* limitations under the License.
*****************************************************************************/
use crate::handlers::sign_tx::Tx;
use crate::utils::concatenate;
use crate::AppSW;

#[cfg(not(any(target_os = "stax", target_os = "flex")))]
Expand All @@ -35,6 +34,8 @@ use numtoa::NumToA;

const MAX_COIN_LENGTH: usize = 10;

use alloc::format;

/// Displays a transaction and returns true if user approved it.
///
/// This method can return [`AppSW::TxDisplayFail`] error if the coin name length is too long.
Expand All @@ -43,31 +44,18 @@ const MAX_COIN_LENGTH: usize = 10;
///
/// * `tx` - Transaction to be displayed for validation
pub fn ui_display_tx(tx: &Tx) -> Result<bool, AppSW> {
// Generate string for amount
let mut numtoa_buf = [0u8; 20];
let mut value_buf = [0u8; 20 + MAX_COIN_LENGTH + 1];

let value_str = concatenate(
&[tx.coin, " ", tx.value.numtoa_str(10, &mut numtoa_buf)],
&mut value_buf,
)
.map_err(|_| AppSW::TxDisplayFail)?; // Fails if value_buf is too small

// Generate destination address string in hexadecimal format.
let mut to_str = [0u8; 42];
to_str[..2].copy_from_slice("0x".as_bytes());
hex::encode_to_slice(tx.to, &mut to_str[2..]).unwrap();
to_str[2..].make_ascii_uppercase();
let value_str = format!("{} {}", tx.coin, tx.value);
let to_str = format!("0x{}", hex::encode(tx.to).to_uppercase());

// Define transaction review fields
let my_fields = [
Field {
name: "Amount",
value: value_str,
value: value_str.as_str(),
},
Field {
name: "Destination",
value: core::str::from_utf8(&to_str).unwrap(),
value: to_str.as_str(),
},
Field {
name: "Memo",
Expand Down
20 changes: 8 additions & 12 deletions src/handlers/sign_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use crate::app_ui::sign::ui_display_tx;
use crate::utils::Bip32Path;
use crate::AppSW;
use alloc::vec::Vec;
use ledger_device_sdk::ecc::{Secp256k1, SeedDerive};
use ledger_device_sdk::hash::{sha3::Keccak256, HashInit};
use ledger_device_sdk::io::Comm;
Expand All @@ -38,24 +39,21 @@ pub struct Tx<'a> {
}

pub struct TxContext {
raw_tx: [u8; MAX_TRANSACTION_LEN], // raw transaction serialized
raw_tx_len: usize, // length of raw transaction
raw_tx: Vec<u8>,
path: Bip32Path,
}

// Implement constructor for TxInfo with default values
impl TxContext {
pub fn new() -> TxContext {
TxContext {
raw_tx: [0u8; MAX_TRANSACTION_LEN],
raw_tx_len: 0,
raw_tx: Vec::new(),
path: Default::default(),
}
}
// Implement reset for TxInfo
fn reset(&mut self) {
self.raw_tx = [0u8; MAX_TRANSACTION_LEN];
self.raw_tx_len = 0;
self.raw_tx.clear();
self.path = Default::default();
}
}
Expand All @@ -78,22 +76,20 @@ pub fn handler_sign_tx(
// Next chunks, append data to raw_tx and return or parse
// the transaction if it is the last chunk.
} else {
if ctx.raw_tx_len + data.len() > MAX_TRANSACTION_LEN {
if ctx.raw_tx.len() + data.len() > MAX_TRANSACTION_LEN {
return Err(AppSW::TxWrongLength);
}

// Append data to raw_tx
ctx.raw_tx[ctx.raw_tx_len..ctx.raw_tx_len + data.len()].copy_from_slice(data);
ctx.raw_tx_len += data.len();
ctx.raw_tx.extend(data);

// If we expect more chunks, return
if more {
Ok(())
// Otherwise, try to parse the transaction
} else {
// Try to deserialize the transaction
let (tx, _): (Tx, usize) =
from_slice(&ctx.raw_tx[..ctx.raw_tx_len]).map_err(|_| AppSW::TxParsingFail)?;
let (tx, _): (Tx, usize) = from_slice(&ctx.raw_tx).map_err(|_| AppSW::TxParsingFail)?;
// Display transaction. If user approves
// the transaction, sign it. Otherwise,
// return a "deny" status word.
Expand All @@ -110,7 +106,7 @@ fn compute_signature_and_append(comm: &mut Comm, ctx: &mut TxContext) -> Result<
let mut keccak256 = Keccak256::new();
let mut message_hash: [u8; 32] = [0u8; 32];

let _ = keccak256.hash(&ctx.raw_tx[..ctx.raw_tx_len], &mut message_hash);
let _ = keccak256.hash(&ctx.raw_tx, &mut message_hash);

let (sig, siglen, parity) = Secp256k1::derive_from_path(ctx.path.as_ref())
.deterministic_sign(&message_hash)
Expand Down
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ use ledger_device_sdk::ui::gadgets::display_pending_review;
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic);

// Required for using String, Vec, format!...
extern crate alloc;

#[cfg(any(target_os = "stax", target_os = "flex"))]
use ledger_device_sdk::nbgl::init_comm;

Expand Down
61 changes: 12 additions & 49 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,42 @@
use alloc::vec::Vec;

use crate::AppSW;
use core::str::from_utf8;

/// BIP32 path stored as an array of [`u32`].
///
/// # Generic arguments
///
/// * `S` - Maximum possible path length, i.e. the capacity of the internal buffer.
pub struct Bip32Path<const S: usize = 10> {
buffer: [u32; S],
len: usize,
}
#[derive(Default)]
pub struct Bip32Path(Vec<u32>);

impl AsRef<[u32]> for Bip32Path {
fn as_ref(&self) -> &[u32] {
&self.buffer[..self.len]
}
}

impl<const S: usize> Default for Bip32Path<S> {
fn default() -> Self {
Self {
buffer: [0u32; S],
len: 0,
}
&self.0
}
}

impl<const S: usize> TryFrom<&[u8]> for Bip32Path<S> {
impl TryFrom<&[u8]> for Bip32Path {
type Error = AppSW;

/// Constructs a [`Bip32Path`] from a given byte array.
///
/// This method will return an error in the following cases:
/// - the input array is empty,
/// - the number of bytes in the input array is not a multiple of 4,
/// - the input array exceeds the capacity of the [`Bip32Path`] internal buffer.
///
/// # Arguments
///
/// * `data` - Encoded BIP32 path. First byte is the length of the path, as encoded by ragger.
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let input_path_len = (data.len() - 1) / 4;
// Check data length
if data.is_empty() // At least the length byte is required
|| (input_path_len > S)
|| (data[0] as usize * 4 != data.len() - 1)
{
return Err(AppSW::WrongApduLength);
}

let mut path = [0; S];
for (chunk, p) in data[1..].chunks(4).zip(path.iter_mut()) {
*p = u32::from_be_bytes(chunk.try_into().unwrap());
}

Ok(Self {
buffer: path,
len: input_path_len,
})
Ok(Bip32Path(
data[1..]
.chunks(4)
.map(|chunk| u32::from_be_bytes(chunk.try_into().unwrap()))
.collect(),
))
}
}

/// Returns concatenated strings, or an error if the concatenation buffer is too small.
pub fn concatenate<'a>(strings: &[&str], output: &'a mut [u8]) -> Result<&'a str, ()> {
let mut offset = 0;

for s in strings {
let s_len = s.len();
if offset + s_len > output.len() {
return Err(());
}

output[offset..offset + s_len].copy_from_slice(s.as_bytes());
offset += s_len;
}

Ok(from_utf8(&output[..offset]).unwrap())
}

0 comments on commit 9ec3374

Please sign in to comment.