Each official app will be signed by a Ledger HSM. Given an ELF file, the HSM generates a .zip archive where the app's manifest is signed by the Ledger HSM using ECDSA. The manifest contains the SHA-256 hash of the app (code_start || code_end || data_start || data_end || code.bin || data.bin
) as well as ELF addresses and the initial merkletree.
$ python host/hsm.py --elf-path app-eth.elf --app-path /tmp/app.zip
$ unzip -l /tmp/app.zip
Archive: /tmp/app.zip
Length Date Time Name
--------- ---------- ----- ----
160 2022-04-11 10:20 manifest.bin
71 2022-04-11 10:20 manifest.hsm.sig
45824 2022-04-11 10:20 code.bin
4864 2022-04-11 10:20 data.bin
--------- -------
50919 4 files
$ python host/app.py --show-manifest --app-path /tmp/app.zip
manifest_version = 1
name = u'Ethereum' (total 8)
version = u'0.1' (total 3)
app_hash = unhexlify('b89f8f1b30fbeec3c098c763868e9931158ddc8aad3e1f883e119e18f6412492')
entrypoint = 0x000160B4
bss = 0x0001D500
code_start = 0x00010000
code_end = 0x0001B300
stack_start = 0x7FFF0000
stack_end = 0x80000000
data_start = 0x0001C200
data_end = 0x0002D500
mt_root_hash = unhexlify('010d84b42c7f8ed7b99942d13bb3bb8601538c990f5b775355f54430a037010d')
mt_size = 19
mt_last_entry = unhexlify('00d4010000000000')
Note that code.bin
and data.bin
aren't encrypted.
This .zip archive is generated once and available publicly for download by clients such as Ledger Live.
The VM dones't allow apps to be streamed until they are signed by the device itself. An app can be signed by the device if its manifest is signed by a Ledger HSM, as described previously (the Ledger HSM public key is embedded in the VM). Once the app is signed by the device, 3 files are added to the .zip archive: the code pages HMACs, the data pages HMACs and the ECDSA signature of the manifest.
Host Device
──────────────────────────────────────────────
Send the manifest signed by
Ledger HSMs along its signature
──────────────────────────────►
Ack
◄──────────────────────────────
Send the 1st code page
──────────────────────────────►
Receive encrypted HMAC
◄──────────────────────────────
Send the 2nd code page
──────────────────────────────►
Receive encrypted HMAC
◄──────────────────────────────
...
Send the 1st data page
──────────────────────────────►
Receive encrypted HMAC
◄──────────────────────────────
Send the 2nd data page
──────────────────────────────►
Receive encrypted HMAC
◄──────────────────────────────
...
Receive the manifest signature
along TmpAES
◄──────────────────────────────
──────────────────────────────────────────────
In order to guarantee the uniqueness of keys used to authenticate each apps, 2 random seeds of 32 bytes are generated during the first launch of the VM by the device:
ECDSASeed
: derives the private ECDSA key (DevPrivKey
) to sign the manifestAppHMACSeed
: derives theKeyHMAC1
key
Secrets are derived from these seeds using SHA256(seed || app_hash)
where app_hash
is the hash of the application.
This derivation mechanism allows the VM to derive the same set of keys for a given app_hash
.
Once the device has verified that the manifest's signature (generated by a Ledger HSM) is valid, a random AES key (TmpAES
) is generated. This key is used to encrypt data sent to the host and will eventually be transmitted to the host if and only if the signature over the app manifest and the pages is valid.
Computing HMACs of code and data pages is done through the following steps. For each code and each data page:
- The host sends the page;
- The VM computes the HMAC-SHA256
page || addr || counter
where the key isKeyHMAC1
andcounter
is0
; - The VM encrypts this HMAC using AES-256-CBC with the key
TmpAES
. The first iv is0
. - The VM sends the encrypted HMAC to the host.
- The VM updates the SHA-256 context used to update
app_hash
.
Once each page has been received, the VM is able to compute app_hash
. If the SHA-256 is equal to the one in the manifest, it means that the code and data pages sent by the host are valid. The VM sends the AES key TmpAES
to the host, which eventually decrypt every HMAC.
The VM finally signs the manifest using DevPrivKey
and sends the signature to the host.
The host is now able do decrypt all HMACs received from the device and generate the final .zip archive. As shown below, the .zip archive has now 3 additional files in the device
folder: manifest.device.sig
, code.mac.bin
and data.mac.bin
.
$ python host/app.py --speculos --app-path /tmp/app.zip
$ unzip -l app.zip
Length Date Time Name
--------- ---------- ----- ----
160 2022-04-11 10:22 manifest.bin
71 2022-04-11 10:22 manifest.hsm.sig
45824 2022-04-11 10:22 code.bin
4864 2022-04-11 10:22 data.bin
71 2022-04-11 10:22 device/manifest.device.sig
5728 2022-04-11 10:22 device/code.mac.bin
608 2022-04-11 10:22 device/data.mac.bin
--------- -------
57326 7 files
The app can now be streamed to the device as long as the VM isn't reinstalled (since it would lead to the generation of new seeds).