-
Notifications
You must be signed in to change notification settings - Fork 30
Feature/ledger signer #1944
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
base: master
Are you sure you want to change the base?
Feature/ledger signer #1944
Conversation
ecf51c0 to
abe8a88
Compare
59bd742 to
9099e90
Compare
abe8a88 to
89285d1
Compare
5f20a4f to
4c64407
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't dug into the code much yet, will continue next week.
Tests assume the ledger app repo is cloned next to this one with name ledger-mintlayer
To be honest, I'm not a huge fan of this approach. And also of the fact that the emulator is always started automatically. E.g. in the Trezor case it was sometimes useful to see the emulator logs to understand what went wrong.
Was there any particular reason to do it this way instead of expecting the emuator to be running?
f21e82b to
75ff652
Compare
61292c5 to
eeb6484
Compare
75ff652 to
96016cf
Compare
5d3edf0 to
d6fd484
Compare
39d3e58 to
d6fd484
Compare
| pub async fn get_app_name<L: Exchange>(ledger: &mut L) -> Result<Vec<u8>, ledger_lib::Error> { | ||
| let msg_buf = [CLA, Ins::APP_NAME, 0, P2::DONE]; | ||
| ledger.exchange(&msg_buf, Duration::from_millis(100)).await | ||
| } | ||
|
|
||
| #[allow(dead_code)] | ||
| pub async fn check_current_app<L: Exchange>(ledger: &mut L) -> SignerResult<()> { | ||
| let resp = get_app_name(ledger) | ||
| .await | ||
| .map_err(|err| LedgerError::DeviceError(err.to_string()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good way to check for our app, because the INS values are app-specific.
There is a standard way to obtain the app name via CLA=0xB0 and INS=1. E.g. here it's handled by the SDK on the device - https://github.com/LedgerHQ/ledger-device-rust-sdk/blob/4262899a325b9b2fe10f2524d8e4b2f9fec38b83/ledger_device_sdk/src/io_legacy.rs#L330-L331
(The INS is processed inside the handle_bolos_apdu function).
Also, ledger-proto contains something called AppInfoReq which mentions CLA 0xB0 and INS 1, so I guess you don't have to construct the APDU by hand and parse the request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason it always returns "app" for app name and the OS version instead of the opened app. So, I kept our own instructions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason it always returns "app" for app name and the OS version instead of the opened app. So, I kept our own instructions.
Well, I tried sending [b0, 1, 0, 0] to my NanoSPlus and got "Mintlayer"/"0.1.0" for our app and "Ethereum"/"1.18.0" for Ethereum.
What device are you using? Can you double check?
In any case, this has to be investigated further. If b0/01 doesn't work indeed, we must at least document that fact, mentioning the particular situations.
d6fd484 to
2d5300b
Compare
2d5300b to
fbbfd75
Compare
fbbfd75 to
1ae1ecc
Compare
| Err(ledger_lib::Error::Timeout) => { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good idea to ignore timeouts indefinitely. Perhaps we should only allow a few timeouts and then fail?
But why do we even need to handle a timeout differently that other connection errors that a re handled below?
| } | ||
| } | ||
|
|
||
| /// Calls initialize on the device with the current session_id. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is neither initialize nor session_id in the Ledger case.
| let mut client = self.client.lock().await; | ||
| operation(&mut client) | ||
| .await | ||
| .map_err(|e| LedgerError::DeviceError(e.to_string()).into()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
operation returns SignerError, not an error from the device directly.
Plz remove these explicit calls of to_string and implement From<ledger_lib::Error> for LedgerError instead
|
|
||
| ensure!( | ||
| response_status == OK_RESPONSE, | ||
| LedgerError::ErrorResponse(response_status) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's parse the status code (e.g. using ledger-proto's StatusCode, which implements TryFromPrimitive) and provide a more informative error instead of this ErrorResponse(u16). E.g. currently I'm getting "Ledger error: Received an error response from the Ledger device: 21781" when trying to connect to a locked device, this is not very informative.
| .await | ||
| .map_err(|err| LedgerError::DeviceError(err.to_string()))?; | ||
|
|
||
| let device = devices.pop().ok_or(LedgerError::NoDeviceFound)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On my Linux machine I get the following here:
devices: [LedgerInfo { model: NanoSPlus, conn: Usb(UsbInfo { vid: 11415, pid: 20480, path: Some("3-2:1.0") }) }, LedgerInfo { model: NanoSPlus, conn: Usb(UsbInfo { vid: 11415, pid: 20480, path: Some("3-2:1.1") }) }]
They are 2 separate HID interfaces to the same actual device, one of them is the APDU interface and another - the FIDO/U2F one (according to ChatGPT).
In my case your code selects the non-APDU interface and I get empty responses from it.
So,
- What are you getting on Windows here? Also, what device(s) are you using?
- Relying on a particular interface having a particular index in the returned vector won't work in general. According to ChatGPT, we should prefer the one with
interface_number == 0, but rust-ledger doesn't exposeinterface_number. I.e. this needs further investigation after which we should either make a PR to rust-ledger with the corresponding improvement or fork it.
| pub async fn get_app_name<L: Exchange>(ledger: &mut L) -> Result<Vec<u8>, ledger_lib::Error> { | ||
| let msg_buf = [CLA, Ins::APP_NAME, 0, P2::DONE]; | ||
| ledger.exchange(&msg_buf, Duration::from_millis(100)).await | ||
| } | ||
|
|
||
| #[allow(dead_code)] | ||
| pub async fn check_current_app<L: Exchange>(ledger: &mut L) -> SignerResult<()> { | ||
| let resp = get_app_name(ledger) | ||
| .await | ||
| .map_err(|err| LedgerError::DeviceError(err.to_string()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason it always returns "app" for app name and the OS version instead of the opened app. So, I kept our own instructions.
Well, I tried sending [b0, 1, 0, 0] to my NanoSPlus and got "Mintlayer"/"0.1.0" for our app and "Ethereum"/"1.18.0" for Ethereum.
What device are you using? Can you double check?
In any case, this has to be investigated further. If b0/01 doesn't work indeed, we must at least document that fact, mentioning the particular situations.
Tests assume the ledger app is already running in the emulator same as Trezor tests