Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stax support improvement #17

Open
wants to merge 3 commits into
base: stax-support
Choose a base branch
from
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
27 changes: 14 additions & 13 deletions nanopass.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from typing import Optional, List, Tuple
import ledgerwallet.client

MAX_NAME_LEN = 32
MAX_NAME_LEN = 64
MAX_LOGIN_LEN = 32
MAX_PASS_LEN = 32

Expand Down Expand Up @@ -110,19 +110,16 @@ def add(self, name: str, login: str, password: Optional[str] = None):
self.dev.apdu_exchange(0x03, p1=p1, data=name_bytes + login_bytes +
password_bytes)

def get_name(self, index: int) -> str:
"""
Retrieve name of a password
:param index: Password entry index
:return: Name
"""
r = self.dev.apdu_exchange(0x04, index.to_bytes(4, 'big'))
assert len(r) == 32
return bytes_to_str(r)

def get_names(self) -> List[str]:
""" :return: List of password names """
return [self.get_name(i) for i in range(self.get_size())]
names = []
for i in range(0, self.get_size(), 4):
r = self.dev.apdu_exchange(0x04, i.to_bytes(4, 'big'))
for j in range(4):
name = bytes_to_str(r[j*64:j*64+64])
if len(name) > 0:
names.append(name)
return names

def get_by_name(self, name: str) -> Tuple[str, str]:
"""
Expand Down Expand Up @@ -211,6 +208,9 @@ def has_name(self, name: str):
assert len(res) == 1
assert res[0] in (0, 1)
return bool(res[0])

def list_password_on_screen(self):
self.dev.apdu_exchange(0x0f)


@click.group()
Expand Down Expand Up @@ -317,7 +317,8 @@ def open_(ctx):
@click.pass_context
def quit(ctx):
dev = ctx.obj['DEV']
dev.quit_app()
# dev.quit_app()
dev.list_password_on_screen()


if __name__ == '__main__':
Expand Down
93 changes: 71 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use include_gif::include_gif;
#[cfg(any(target_os = "stax", target_os = "flex"))]
use ledger_device_sdk::nbgl::{NbglGlyph, NbglHomeAndSettings};
#[cfg(any(target_os = "stax", target_os = "flex"))]
use ledger_device_sdk::nbgl::{init_comm, NbglStatus, NbglChoice, NbglSpinner};
use ledger_device_sdk::nbgl::{init_comm, Field, InfosList, NbglGenericReview, NbglPageContent, NbglStatus, NbglChoice, NbglSpinner};
#[cfg(not(any(target_os = "stax", target_os = "flex")))]
use ledger_device_sdk::ui::bitmaps::{CERTIFICATE, DASHBOARD_X, Glyph};

Expand Down Expand Up @@ -88,7 +88,7 @@ pub enum Instruction {
GetVersion,
GetSize,
Add,
GetName,
GetNames,
GetByName,
DeleteByName,
Export,
Expand All @@ -99,6 +99,8 @@ pub enum Instruction {
Quit,
ShowOnScreen,
HasName,
#[cfg(any(target_os = "stax", target_os = "flex"))]
ListPasswordsOnScreen,
}

impl TryFrom<ApduHeader> for Instruction {
Expand All @@ -109,7 +111,7 @@ impl TryFrom<ApduHeader> for Instruction {
0x01 => Ok(Self::GetVersion),
0x02 => Ok(Self::GetSize),
0x03 => Ok(Self::Add),
0x04 => Ok(Self::GetName),
0x04 => Ok(Self::GetNames),
0x05 => Ok(Self::GetByName),
0x06 => Ok(Self::DeleteByName),
0x07 => Ok(Self::Export),
Expand All @@ -120,6 +122,8 @@ impl TryFrom<ApduHeader> for Instruction {
0x0c => Ok(Self::Quit),
0x0d => Ok(Self::ShowOnScreen),
0x0e => Ok(Self::HasName),
#[cfg(any(target_os = "stax", target_os = "flex"))]
0x0f => Ok(Self::ListPasswordsOnScreen),
_ => Err(Error::InsNotSupported),
}
}
Expand Down Expand Up @@ -211,8 +215,8 @@ extern "C" fn sample_main() {
// If P1 == 1, password must be generated by the device
Instruction::Add => {
let mut offset = 5;
let name = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32));
offset += 32;
let name = ArrayString::<64>::from_bytes(comm.get(offset, offset + 64));
offset += 64;
let login = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32));
offset += 32;
let pass = match comm.get_apdu_metadata().p1 {
Expand All @@ -225,25 +229,29 @@ extern "C" fn sample_main() {
});
c = 0;
},
// Get password name
// Get list of passwords name
// This is used by the client to list the names of stored password
// Login is not returned.
Instruction::GetName => {
Instruction::GetNames => {
let mut index_bytes = [0; 4];
index_bytes.copy_from_slice(comm.get(5, 5 + 4));
let index = u32::from_be_bytes(index_bytes);
match passwords.get(index as usize) {
Some(password) => {
comm.append(password.name.bytes());
comm.reply_ok()
for index in index..index + 4 {
match passwords.get(index as usize) {
Some(password) => {
comm.append(password.name.bytes());
}
None => {
break;
}
}
None => comm.reply(Error::EntryNotFound),
}
comm.reply_ok()
},
// Get password by name
// Returns login and password data.
Instruction::GetByName => {
let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32));
let name = ArrayString::<64>::from_bytes(comm.get(5, 5 + 64));

match passwords.into_iter().find(|&&x| x.name == name) {
Some(&p) => {
Expand All @@ -257,9 +265,11 @@ extern "C" fn sample_main() {
comm.append(p.login.bytes());
comm.append(p.pass.bytes());
comm.reply_ok();
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("").show(true);
} else {
comm.reply(Error::NoConsent);
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("").show(false);
}
}
Expand All @@ -274,7 +284,7 @@ extern "C" fn sample_main() {
// Display a password on the screen only, without communicating it
// to the host.
Instruction::ShowOnScreen => {
let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32));
let name = ArrayString::<64>::from_bytes(comm.get(5, 5 + 64));

match passwords.into_iter().find(|&&x| x.name == name) {
Some(&p) => {
Expand Down Expand Up @@ -303,7 +313,7 @@ extern "C" fn sample_main() {

// Delete password by name
Instruction::DeleteByName => {
let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32));
let name = ArrayString::<64>::from_bytes(comm.get(5, 5 + 64));
match passwords.into_iter().position(|x| x.name == name) {
Some(p) => {
if
Expand All @@ -316,9 +326,11 @@ extern "C" fn sample_main() {
{
passwords.remove(p);
comm.reply_ok();
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Password deleted").show(true);
} else {
comm.reply(Error::NoConsent);
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Operation rejected").show(false);
}
}
Expand Down Expand Up @@ -359,13 +371,16 @@ extern "C" fn sample_main() {
if validate(&[], &[&"Are you sure?"], &[&"Confirm"], &[&"Cancel"])
{
passwords.clear();
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("All password are removed").show(true);
StatusWords::Ok.into()
} else {
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Operation rejected").show(false);
Error::NoConsent.into()
}
} else {
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Operation rejected").show(false);
Error::NoConsent.into()
},
Expand All @@ -379,7 +394,7 @@ extern "C" fn sample_main() {
},
// HasName
Instruction::HasName => {
let name = ArrayString::<32>::from_bytes(comm.get(5, 5 + 32));
let name = ArrayString::<64>::from_bytes(comm.get(5, 5 + 64));
match passwords.into_iter().find(|&&x| x.name == name) {
Some(_) => {
comm.append(&[1]);
Expand All @@ -390,6 +405,36 @@ extern "C" fn sample_main() {
}
comm.reply_ok();
},
#[cfg(any(target_os = "stax", target_os = "flex"))]
Instruction::ListPasswordsOnScreen => {
if validate(&[], &[&"List all passwords"], &[&"Confirm"], &[&"Cancel"]) {
let mut index = 0;
let mut review: NbglGenericReview = NbglGenericReview::new();
loop {
match passwords.get(index) {
Some(password) => {
let my_example_fields = [
Field {
name: password.name.as_str(),
value: password.login.as_str(),
},
];
let infos_list = InfosList::new(&my_example_fields);
review = review.add_content(NbglPageContent::InfosList(infos_list));
}
None => {
break;
}
}
index += 1;
}
review.show("Quit");

comm.reply_ok();
} else {
comm.reply(Error::NoConsent);
}
},
}
};
}
Expand Down Expand Up @@ -567,7 +612,7 @@ fn generate_random_password(dest: &mut [u8], size: usize) {
/// * `pass` - New password. If None, a password is generated automatically.
fn set_password(
passwords: &mut nvm::Collection<PasswordItem, 128>,
name: &ArrayString<32>,
name: &ArrayString<64>,
login: &ArrayString<32>,
pass: &Option<ArrayString<32>>,
) -> Result<(), Error> {
Expand All @@ -590,10 +635,12 @@ fn set_password(
// A password with this name already exists.
if !validate(&[name.as_str()], &[&"Update password"], &[&"Confirm"], &[&"Cancel"])
{
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Operation rejected").show(false);
return Err(Error::NoConsent);
}
passwords.remove(index);
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("").show(true);
match passwords.add(&new_item) {
Ok(()) => Ok(()),
Expand All @@ -605,9 +652,11 @@ fn set_password(
// Ask user confirmation
if !validate(&[name.as_str()], &[&"Create password"], &[&"Confirm"], &[&"Cancel"])
{
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("Operation rejected").show(false);
return Err(Error::NoConsent);
}
#[cfg(any(target_os = "stax", target_os = "flex"))]
NbglStatus::new().text("").show(true);
match passwords.add(&new_item) {
Ok(()) => Ok(()),
Expand Down Expand Up @@ -766,9 +815,9 @@ fn import(
buffer.len() as u32,
);
}
new_item.name = ArrayString::<32>::from_bytes(&buffer[..32]);
new_item.login = ArrayString::<32>::from_bytes(&buffer[32..64]);
new_item.pass = ArrayString::<32>::from_bytes(&buffer[64..96]);
new_item.name = ArrayString::<64>::from_bytes(&buffer[..64]);
new_item.login = ArrayString::<32>::from_bytes(&buffer[64..96]);
new_item.pass = ArrayString::<32>::from_bytes(&buffer[96..128]);
// Verify the MAC
buffer.clear();
buffer
Expand All @@ -791,8 +840,8 @@ fn import(
decrypt_failed = received_mac != expected_mac;
} else {
let mut offset = 5;
new_item.name = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32));
offset += 32;
new_item.name = ArrayString::<64>::from_bytes(comm.get(offset, offset + 64));
offset += 64;
new_item.login = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32));
offset += 32;
new_item.pass = ArrayString::<32>::from_bytes(comm.get(offset, offset + 32));
Expand Down
2 changes: 1 addition & 1 deletion src/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<const N: usize> Eq for ArrayString<N> {}
/// with zeros. This is not null terminated, and UTF8 is allowed.
#[derive(Clone, Copy)]
pub struct PasswordItem {
pub name: ArrayString<32>,
pub name: ArrayString<64>,
pub login: ArrayString<32>,
pub pass: ArrayString<32>,
}
Expand Down