This is a key management application for the Handshake Protocol. With support for both the Ledger Nano S and Ledger Nano X, it allows users to create extended public keys, addresses, and signatures for valid Handshake transactions. It can be used with the hsd-ledger client library to interact with wallet software.
This document serves as an overview of the relevant technical and licensing details for this application. For more general information on developing for Ledger personal security devices please read the official Ledger developer documentation here.
For a walk-through of a Ledger application, check out the nanos-app-sia project. The Nebulous Inc. developers have done a wonderful job of documenting both the high-level architecture and low-level implementation details of Nano S app development.
To load the app on your Ledger Nanos S without using Docker:
- Follow the setup instructions here.
- Run
make loadin the root of this git repo. - Follow the terminal and on-device instructions (this process takes a while).
Once installation is complete, install the client library.
Note: macOS and Windows are not supported. If you are not running Linux, please follow the Docker instructions below.
If you are running Linux, the only dependency is Docker. If you are running macOS or Windows you will also need to setup a Python virtualenv and install developer tools provided by Ledger. Detailed instructions for each host environment are below.
Once installation is complete, install the client library.
Note: all example commands begin with the $ symbol. This symbol signifies a shell command prompt. The command prompt in your shell may be different. Do not copy the $ symbol when using the commands in your shell.
First, be sure to have Docker installed and running on your machine.
Clone the git repo:
$ git clone https://github.com/handshake-org/ledger-app-hns.git
$ cd ledger-app-hns- Connect the Ledger Nano S to your machine via USB.
- Unlock the device.
- Navigate to the device's main menu.
If your device is running firmware v1.6.0, run:
$ docker build --build-arg CACHE_BUST="$(date)" -f Dockerfile.build -t ledger-app-hns-build .
$ docker run --rm --privileged ledger-app-hns-build make loadIf your device is running firmware v1.5.5, run:
$ docker build --build-arg GIT_REF="nanos-1553" --build-arg CACHE_BUST="$(date)" -f Dockerfile.build -t ledger-app-hns-build .
$ docker run --rm --privileged ledger-app-hns-build make loadNote: the above
docker runcommand uses the--privilegedflag, which gives the docker container full access to your host machine. If you know the location of your connected Ledger Nano S you can >replace the--privilegedtag with--device=/path/to/usb/device.
- Follow the terminal and on-device instructions (this process takes a while).
If you are using a Nano X, run:
$ docker build --build-arg GIT_NAME="nanox-secure-sdk" --build-arg GIT_REF="1.2.4-5.1" \
--build-arg CACHE_BUST="$(date)" -f Dockerfile.build -t ledger-app-hns-build .
$ docker run --rm --privileged ledger-app-hns-build make loadFirst, be sure to have the following development dependencies installed on your machine:
- Docker
- Python 2.7 development environment (
python2,python2-dev,virtualenv) makegccg++git
Next, create a directory to store the Nano S Secure SDK and the Python
virtual environment for the Python Loader tool. The SDK is used in
ledger-app-hns to interact with the device's operating system and crypto
libraries. The loader tool loads compiled applications onto your Ledger Nano S.
It does not matter where you store the SDK or the virtual environment.
For simplicity, this guide stores them together in a directory called
ledger-tools.
To begin, open a terminal shell and run:
$ mkdir ledger-tools
$ cd ledger-toolsBefore installing the SDK, you will need to know what firmware version is
running on your Ledger Nano S. If you do not know how to check your firmware
version, follow the instructions here. This application supports
v1.6.0 and v1.5.5`.
Clone the git repo:
$ git clone https://github.com/ledgerhq/nanos-secure-sdk.gitIf your device is running firmware v1.6.0 checkout the nanos-1612 branch:
$ git checkout nanos-1612If your device is running firmware v1.5.5 checkout the nanos-1553 branch:
$ git checkout nanos-1553Set the BOLOS_SDK environment variable to the absolute path
of the SDK repo:
$ cd nanos-secure-sdk
$ export BOLOS_SDK=`pwd`
$ cd ..Now, you will need to install the Python loader tool. This process
will involve: navigating back to the root of the ledger-tools directory,
creating and activating a Python virtual environment, and installing the
ledgerblue loader tools.
Run:
$ virtualenv ledger
$ source ledger/bin/activate
$ pip install ledgerblue
$ pip install PillowWe are ready to install the application! This process will involve: cloning
the git repo into the ledger-tools directory, checking out the branch that
corresponds to your device's firmware version, compiling the application, and
loading it onto your device. The last two steps are triggered by one
make command.
Clone the git repo:
$ git clone https://github.com/handshake-org/ledger-app-hns.git
$ cd ledger-app-hns- Start Docker.
- Connect the Ledger Nano S to your machine via USB.
- Unlock the device.
- Navigate to the device's main menu.
If your device is running firmware v1.6.0, run:
$ make docker-loadIf your device is running firmware v1.5.5, run:
$ GIT_REF="nanos-1553" make docker-load- Follow the terminal and on-device instructions (this process takes a while).
To build both Nano S and Nano X apps using Docker:
$ make docker-build-allThis will run the build in a docker container and copy the final builds to
/bin/hns-nanos.elf and /bin/hns-nanox.elf
These two binaries can be executed inside the Speculos emulator. For example, to run the built Nano S app in the emulator from docker:
docker run -v \
/path/to/ledger-app-hns/bin:/speculos/apps \
--publish 5900:5900 \
-it \
ledgerhq/speculos \
--display headless \
--vnc-port 5900 \
--vnc-password=xyz \
apps/hns-nanos.elf \
--model nanos
The command is explained in detail in the Speculos docs. Just note the path
to the /bin directory and model argument. The emulated device can be
accessed using a VNC client. On OSX, the --vnc-password argument is required
and will be requested by the VNC client.
A suite of tests have been added to the client library. They include
device tests with mocked transaction data, and end-to-end tests involving an
active, hsd node. All tests require a Ledger Nano S configured with the
following test seed:
abandon abandon abandon abandon abandon abandon
abandon abandon abandon abandon abandon about
This application interacts with a computer host through the APDU communication
protocol. This specification describes the APDU command interface for
ledger-app-hns.
The basic structure of an APDU command consists of a 5 byte header followed by a variable amount of command input data. The header for an HNS Ledger application command takes the following structure:
NOTE: For the remainder of this document all lengths are represented in bytes. All data is represented as little-endian bytestrings except where noted.
| Field | Len | Purpose |
|---|---|---|
| CLA | 1 | Instruction class - the type of command (always 0xe0) |
| INS | 1 | Instruction code - the specific command |
| P1 | 1 | Instruction param #1 |
| P2 | 1 | Instruction param #2 |
| LC | 1 | Length of command input data |
*The above description is unique to this application. Specifically, the APDU protocol allows for a larger LC field. A more general description of the APDU message protocol can be found here.
This command returns the application's version number.
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 0xe0 | 0x40 | 0x00 | 0x00 | 0x00 |
None
| Field | Len |
|---|---|
| major version | 1 |
| minor version | 1 |
| patch version | 1 |
This command returns a public key for the given BIP32 path. Using the APDU parameter fields it can also return a Bech32 encoded address for the public key, or the details needed reconstruct the extended public key at the specified level in the HD tree.
The first instruction param (P1) can be used to require on-device confirmation by setting the least significant bit. The next two lowest bits are used to signify the network. The network flag is only used for xpub confirmation. Address generation will parse the network from the derivation path. If an unknown coin type is passed during address generation, an error will be returned.
The second instruction param indicates which, if any, additional details to return, i.e. extended public details and/or address. If confirmation is turned on, and an address is generated, the address will be displayed on screen. The next precedence will be given to extended public key details. Otherwise, the public key will be displayed for confirmation.
NOTE: an on-device warning will be displayed for non-hardened derivation at the BIP44 account level or above. It will also be displayed for derivations past the address index level.
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 0xe0 | 0x42 | *var | **var | var |
* P1:
- 0x00 = No confimation
- 0x01 = Require confirmation
0x06 is used as a mask to check second and third least significant bits.
- 0x00 = Mainnet
- 0x02 = Testnet
- 0x04 = Regtest
- 0x06 = Simnet
** P2:
- 0x00 = Public key only
- 0x01 = Public key + chain code + parent fingerprint
- 0x02 = Public key + address
- 0x03 = Public key + chain code + parent fingerprint + address
| Field | Len |
|---|---|
| # of derivations (max 5) | 1 |
| First derivation index (big-endian) | 4 |
| ... | 4 |
| Last derivation index (big-endian) | 4 |
| Field | Len |
|---|---|
| public key | 33 |
| chain code length | 1 |
| chain code | var |
| parent fingerprint length | 1 |
| parent fingerprint (big-endian) | var |
| address length | 1 |
| address | var |
This command handles the entire input signature creation process. It operates in two modes: parse and sign. When engaged in parse mode, transaction details are sent to the device where they are parsed, cached, and prepared for signing. Once all tx details have been parsed, the user can send signature requests for each input. The first signature request for a particular tx requires on-device confirmation of the txid.
Both modes may require multiple message exchanges between the client and the device. The first instruction param (P1) indicates if a message is the initial one. An initial parse message clears any cached transaction details from memory and restarts the signing process. The initial message in a signature request should pass the path of the signing key, the sighash type, the input, and the first 182 bytes of the input script (including the varint script size). If the entire script does not fit into the first message, additional messages will be necessary to send the rest of the script. The subsequent messages should only include the remaining script bytes.
The second instruction param (P2) indicates the operation mode.
NOTE: Signature requests for non-standard BIP44 address paths will be rejected.
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 0xe0 | 0x44 | *var | 0x00 | var |
* P1:
- 0x01 = Initial message
- 0x00 = Following message
NOTE: The transaction details should be sent in packets of up to 255 bytes. This is because the APDU command data length is represented as a uint8_t.
| Field | Len |
|---|---|
| version | 4 |
| locktime | var |
| # of inputs | 1 |
| # of outputs | 1 |
| *change flag | 1 |
| change index? | 1 |
| change version? | 1 |
| **change path? | var |
| ***inputs | var |
| ****outputs | var |
* change flag:
- 0x00 = No address.
- 0x01 = P2PKH change address. Address info provided.
- 0x02 = P2SH change address. No info provided. On-device confirmation required.
** A BIP32 derivation path of the transaction's change address. Only one change address is allowed per transaction. If no change address path is provided, only one output is allowed in the transaction. See serialization format above. Non-standard BIP44 address paths will be rejected. If serialized redeem script, an on-device warning will be displayed.
*** Input serialization for parse mode
| Field | Len |
|---|---|
| prevout | 36 |
| sequence | 4 |
| value | 8 |
**** Output serialization
| Field | Len |
|---|---|
| value | 8 |
| *****address | var |
| ******covenant | var |
***** Address serialization
| Field | Len |
|---|---|
| version | 1 |
| hash length | 1 |
| hash | var |
****** Covenant serialization
| Field | Len |
|---|---|
| type | 1 |
| # of items | var |
| items? | var |
| name? | var |
None
| CLA | INS | P1 | P2 | LC |
|---|---|---|---|---|
| 0xe0 | 0x44 | *var | 0x01 | var |
* P1:
- 0x01 = Initial signature request (on-device txid confirmation required)
- 0x00 = Additional signature request
| Field | Len |
|---|---|
| *signing key path | var |
| sighash type | 4 |
| **input | var |
* See serialization format above. Non-standard BIP44 address paths will be rejected.
** Input serialization for sign mode
| Field | Len |
|---|---|
| prevout | 36 |
| value | 8 |
| sequence | 4 |
| script length | var |
| script | var |
NOTE: If the size of the input data is larger than the APDU buffer size, the script must be split into smaller packet sizes and sent in multiple messages. Subsequent messages should only send the remaining script bytes. All other input data i.e., the path, sighash type, prevout, value, sequence, and script length, should not be resent.
| Field | Len |
|---|---|
| signature | var |
NOTE: The application keeps track of the number of script bytes it has parsed and will return a SUCCESS status word, without any response data, if it successfully parsed the input data, but is expecting more bytes. After parsing all script bytes, the signature will be generated and returned.
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. </legalese>
- Copyright (c) 2018, Boyma Fahnbulleh (MIT License).
Parts of this software are based on ledger-app-btc, blue-app-nano, nanos-app-sia, hnsd, and ledger-app-eth-dockerized.
- Copyright (c) 2016, Ledger (Apache License).
- Copyright (c) 2018, Mart Roosmaa (Apache License).
- Copyright (c) 2018, Nebulous Inc. (MIT License).
- Copyright (c) 2018, Christopher Jeffrey (MIT License).
- No License
See LICENSE for more info.