Skip to content

Commit 2d9ca67

Browse files
committed
More documents, less magic numbers, more tests
1 parent 74a2dde commit 2d9ca67

File tree

8 files changed

+130
-29
lines changed

8 files changed

+130
-29
lines changed

app/src/blake2b_hasher.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use ledger_secure_sdk_sys::*;
44
pub const BLAKE2B_HASH_SIZE: usize = 32;
55
pub struct Blake2bHasher(cx_blake2b_s);
66

7+
// A wrapper around the Ledger SDK's blake2b implementation
78
impl Blake2bHasher {
89
pub fn new() -> Self {
910
let mut v = cx_blake2b_t::default();

app/src/handler.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub fn handle_apdu(
5757
return Err(ErrorCode::BadCla.into());
5858
}
5959

60+
// Common instructions
6061
match ins {
6162
Ins::GetVersion => {
6263
let version_major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
@@ -79,8 +80,8 @@ pub fn handle_apdu(
7980

8081
println("raw path");
8182
println_slice::<PATH_HEX_LENGTH>(raw_path);
82-
let p1 = apdu_header.p1;
83-
let p2 = apdu_header.p2;
83+
let p1 = apdu_header.p1; // Group number: 0 for all groups
84+
let p2 = apdu_header.p2; // Target group
8485
let (pk, hd_index) = derive_pub_key(&mut path, p1, p2)?;
8586

8687
let need_to_display = data[PATH_LENGTH] != 0;
@@ -115,6 +116,8 @@ pub fn handle_apdu(
115116
return Ok(());
116117
}
117118
Ok(()) => {
119+
// The transaction is signed when all the data is processed
120+
// The signature is returned in the response
118121
let sign_result = tx_reviewer
119122
.approve_tx()
120123
.and_then(|_| sign_tx_context.sign_tx());
@@ -136,6 +139,10 @@ pub fn handle_apdu(
136139
Ok(())
137140
}
138141

142+
// The transaction is split into multiple APDU commands
143+
// The first APDU command contains the path and token metadata
144+
// The subsequent APDU commands contain the transaction data
145+
// The transaction data is processed in chunks
139146
fn handle_sign_tx(
140147
apdu_header: &ApduHeader,
141148
data: &[u8],
@@ -145,7 +152,10 @@ fn handle_sign_tx(
145152
match apdu_header.p1 {
146153
0 if data.len() < FIRST_FRAME_PREFIX_LENGTH => Err(ErrorCode::BadLen),
147154
0 => {
155+
// handle the path
148156
sign_tx_context.init(&data[..PATH_LENGTH])?;
157+
158+
// handle the token metadata
149159
let token_size = data[FIRST_FRAME_PREFIX_LENGTH - 1];
150160
if token_size > MAX_TOKEN_SIZE {
151161
return Err(ErrorCode::InvalidTokenSize);
@@ -170,6 +180,8 @@ fn handle_sign_tx(
170180
}
171181
}
172182

183+
// Check the token metadata version
184+
// The token metadata version should be 0 for now
173185
fn check_token_metadata(token_size: u8, token_metadata: &[u8]) -> Result<(), ErrorCode> {
174186
for i in 0..token_size {
175187
let version_index = (i as usize) * TOKEN_METADATA_SIZE;

app/src/ledger_sdk_stub/nvm.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use crate::error_code::ErrorCode;
44

55
pub const NVM_DATA_SIZE: usize = 2048;
66

7+
// The following code is from ledger-rust-sdk
8+
// We've made modifications here to add the `get_mut` functions to `NVMData`
9+
710
#[allow(clippy::upper_case_acronyms)]
811
#[repr(align(64))]
912
pub struct NVM<const N: usize>(pub [u8; N]);

app/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ mod ui;
2424

2525
ledger_device_sdk::set_panic!(ledger_device_sdk::exiting_panic);
2626

27+
// This function is the app entry point
2728
#[no_mangle]
2829
extern "C" fn sample_main() {
2930
let mut comm = io::Comm::new();
3031

32+
// Initialize the sign tx context and tx reviewer
3133
let mut sign_tx_context: SignTxContext = SignTxContext::new();
3234
let mut tx_reviewer: TxReviewer = TxReviewer::new();
3335

app/src/public_key.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,17 @@ use ledger_device_sdk::ecc::SeedDerive;
55
use ledger_device_sdk::ecc::{ECPublicKey, Secp256k1};
66
use ledger_device_sdk::io::Reply;
77
use utils::base58::base58_encode_inputs;
8-
use utils::{djb_hash, xor_bytes};
8+
use utils::{check_group, djb_hash, xor_bytes};
99

10-
pub const TOTAL_NUMBER_OF_GROUPS: u8 = 4;
11-
12-
fn check_group(group_num: u8, target_group: u8) -> Result<(), Reply> {
13-
if group_num == 0 && target_group == 0 {
14-
return Ok(());
15-
}
16-
if target_group >= group_num || group_num != TOTAL_NUMBER_OF_GROUPS {
17-
return Err(ErrorCode::BadP1P2.into());
18-
}
19-
Ok(())
20-
}
10+
const RAW_PUBKEY_SIZE: usize = 65;
11+
const COMPRESSED_PUBKEY_SIZE: usize = 33;
2112

2213
pub fn derive_pub_key(
2314
path: &mut [u32],
2415
group_num: u8,
2516
target_group: u8,
2617
) -> Result<(ECPublicKey<65, 'W'>, u32), Reply> {
27-
check_group(group_num, target_group)?;
18+
check_group::<Reply>(group_num, target_group, ErrorCode::BadP1P2.into())?;
2819
if group_num == 0 {
2920
let pub_key = derive_pub_key_by_path(path)?;
3021
Ok((pub_key, path[path.len() - 1]))
@@ -40,6 +31,8 @@ pub fn derive_pub_key_by_path(path: &[u32]) -> Result<ECPublicKey<65, 'W'>, Repl
4031
Ok(pk)
4132
}
4233

34+
// Derive a public key for a specific group from a path
35+
// The path is incremented until the target group is found
4336
fn derive_pub_key_for_group(
4437
path: &mut [u32],
4538
group_num: u8,
@@ -55,9 +48,9 @@ fn derive_pub_key_for_group(
5548
}
5649

5750
pub fn hash_of_public_key(pub_key: &[u8]) -> [u8; BLAKE2B_HASH_SIZE] {
58-
assert!(pub_key.len() == 65);
59-
let mut compressed = [0_u8; 33];
60-
compressed[1..33].copy_from_slice(&pub_key[1..33]);
51+
assert!(pub_key.len() == RAW_PUBKEY_SIZE);
52+
let mut compressed = [0_u8; COMPRESSED_PUBKEY_SIZE];
53+
compressed[1..COMPRESSED_PUBKEY_SIZE].copy_from_slice(&pub_key[1..COMPRESSED_PUBKEY_SIZE]);
6154
if pub_key.last().unwrap() % 2 == 0 {
6255
compressed[0] = 0x02
6356
} else {

app/src/sign_tx_context.rs

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use ledger_device_sdk::io::ApduHeader;
2-
use utils::{buffer::Buffer, decode::StreamingDecoder, deserialize_path, types::UnsignedTx};
2+
use utils::{
3+
buffer::Buffer, decode::StreamingDecoder, deserialize_path, types::UnsignedTx, PATH_LENGTH,
4+
};
35

46
use crate::ledger_sdk_stub::nvm::{NVMData, NVM, NVM_DATA_SIZE};
57
use crate::ledger_sdk_stub::swapping_buffer::{SwappingBuffer, RAM_SIZE};
@@ -12,6 +14,7 @@ use crate::{
1214
error_code::ErrorCode,
1315
};
1416

17+
// The NVM data is used for SwappingBuffer to store temporary data in case RAM is not enough
1518
#[link_section = ".nvm_data"]
1619
static mut DATA: NVMData<NVM<NVM_DATA_SIZE>> = NVMData::new(NVM::zeroed());
1720

@@ -22,6 +25,9 @@ enum DecodeStep {
2225
Complete,
2326
}
2427

28+
// The context for signing a transaction
29+
// It keeps track of the current step, the transaction decoder, the path, and the device address
30+
// A streaming decoder is used to decode the transaction in chunks so that it can handle large transactions
2531
pub struct SignTxContext {
2632
pub path: [u32; 5],
2733
tx_decoder: StreamingDecoder<UnsignedTx>,
@@ -34,7 +40,7 @@ pub struct SignTxContext {
3440
impl SignTxContext {
3541
pub fn new() -> Self {
3642
SignTxContext {
37-
path: [0; 5],
43+
path: [0; PATH_LENGTH],
3844
tx_decoder: StreamingDecoder::default(),
3945
current_step: DecodeStep::Init,
4046
hasher: Blake2bHasher::new(),
@@ -43,6 +49,7 @@ impl SignTxContext {
4349
}
4450
}
4551

52+
// Initialize the context with the path
4653
pub fn init(&mut self, data: &[u8]) -> Result<(), ErrorCode> {
4754
deserialize_path(data, &mut self.path, ErrorCode::HDPathDecodingFailed)?;
4855

@@ -58,11 +65,13 @@ impl SignTxContext {
5865
self.current_step == DecodeStep::Complete
5966
}
6067

68+
// Get the transaction ID by finalizing the hash
6169
pub fn get_tx_id(&mut self) -> Result<[u8; BLAKE2B_HASH_SIZE], ErrorCode> {
6270
assert!(self.is_complete());
6371
self.hasher.finalize()
6472
}
6573

74+
// Sign the transaction by signing the transaction ID
6675
pub fn sign_tx(&mut self) -> Result<([u8; 72], u32, u32), ErrorCode> {
6776
let tx_id = self.get_tx_id()?;
6877
sign_hash(&self.path, &tx_id)
@@ -75,6 +84,7 @@ impl SignTxContext {
7584
) -> Result<(), ErrorCode> {
7685
while !buffer.is_empty() {
7786
match self.tx_decoder.step(buffer) {
87+
// New transaction details are available
7888
Ok(true) => {
7989
tx_reviewer.review_tx_details(
8090
&self.tx_decoder.inner,
@@ -90,42 +100,51 @@ impl SignTxContext {
90100
self.tx_decoder.reset_stage();
91101
}
92102
}
103+
// No new transaction details are available
93104
Ok(false) => return Ok(()),
94105
Err(_) => return Err(ErrorCode::TxDecodingFailed),
95106
}
96107
}
97108
Ok(())
98109
}
99110

100-
fn decode_tx(&mut self, data: &[u8], tx_reviewer: &mut TxReviewer) -> Result<(), ErrorCode> {
101-
if data.len() > (u8::MAX as usize) {
111+
// Decode a transaction chunk
112+
fn decode_tx(
113+
&mut self,
114+
tx_chunk: &[u8],
115+
tx_reviewer: &mut TxReviewer,
116+
) -> Result<(), ErrorCode> {
117+
if tx_chunk.len() > (u8::MAX as usize) {
102118
return Err(ErrorCode::BadLen);
103119
}
104-
let mut buffer = Buffer::new(data, &mut self.temp_data);
120+
let mut buffer = Buffer::new(tx_chunk, &mut self.temp_data);
105121
let result = self._decode_tx(&mut buffer, tx_reviewer);
106-
self.hasher.update(data)?;
122+
self.hasher.update(tx_chunk)?;
107123
result
108124
}
109125

126+
// Handle a transaction data chunk
110127
pub fn handle_data(
111128
&mut self,
112129
apdu_header: &ApduHeader,
113-
tx_data: &[u8],
130+
tx_data_chunk: &[u8],
114131
tx_reviewer: &mut TxReviewer,
115132
) -> Result<(), ErrorCode> {
116133
match self.current_step {
117134
DecodeStep::Complete => Err(ErrorCode::InternalError),
118135
DecodeStep::Init => {
136+
// The first chunk of the transaction
119137
if apdu_header.p1 == 0 {
120138
self.current_step = DecodeStep::DecodingTx;
121-
self.decode_tx(tx_data, tx_reviewer)
139+
self.decode_tx(tx_data_chunk, tx_reviewer)
122140
} else {
123141
Err(ErrorCode::BadP1P2)
124142
}
125143
}
126144
DecodeStep::DecodingTx => {
145+
// The subsequent chunks of the transaction
127146
if apdu_header.p1 == 1 {
128-
self.decode_tx(tx_data, tx_reviewer)
147+
self.decode_tx(tx_data_chunk, tx_reviewer)
129148
} else {
130149
Err(ErrorCode::BadP1P2)
131150
}

0 commit comments

Comments
 (0)