docs: move docs to root
@ -1,4 +1,4 @@
|
||||
# Contribute to Trezor Core
|
||||
# Contribute to Trezor Firmware
|
||||
|
||||
Please read the general instructions you can find on our [wiki](https://wiki.trezor.io/Developers_guide:Contributing).
|
||||
|
19
README.md
@ -21,20 +21,17 @@
|
||||
|
||||
## Contribute
|
||||
|
||||
Inspired by [GitLab Contributing Guide](https://docs.gitlab.com/ee/development/contributing/)
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
Make sure to check out general [contribution guidelines](https://wiki.trezor.io/Developers_guide:Contributing) on the Trezor Wiki. If you are contributing to Trezor Core
|
||||
(the Trezor T firmware), make sure to check out [Trezor Core contribution guidelines](core/docs/CONTRIBUTING.md) as well.
|
||||
Also please have a look at the docs, either in the `docs` folder or at [docs.trezor.io](https://docs.trezor.io) before contributing. The [misc](docs/misc/index.md) chapter should be read in particular because it contains some useful assorted knowledge.
|
||||
|
||||
Some useful [assorted knowledge](docs/random.md) can be found in the `docs` subdirectory.
|
||||
|
||||
### Security vulnerability disclosure
|
||||
## Security vulnerability disclosure
|
||||
|
||||
Please report suspected security vulnerabilities in private to [security@satoshilabs.com](mailto:security@satoshilabs.com), also see [the disclosure section on the Trezor.io website](https://trezor.io/security/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
|
||||
|
||||
### Issue Labels
|
||||
## Issue Labels
|
||||
|
||||
#### Priority
|
||||
### Priority
|
||||
|
||||
Label | Meaning (SLA)
|
||||
----------|--------------
|
||||
@ -52,6 +49,10 @@ S2 Critical | Broken feature, workaround too complex & unacceptable
|
||||
S3 Major | Broken feature, workaround acceptable
|
||||
S4 Low | Functionality inconvenience or cosmetic issue
|
||||
|
||||
### CI
|
||||
## CI
|
||||
|
||||
The complete test suite is running on a public [GitLab CI](https://gitlab.com/satoshilabs/trezor/trezor-firmware). If you are an external contributor, we also have a [Travis instance](https://travis-ci.org/trezor/trezor-firmware) where a small subset of tests is running as well - mostly style and easy fast checks, which are quite common to fail for new contributors.
|
||||
|
||||
## Documentation
|
||||
|
||||
See the `docs` folder or visit [docs.trezor.io](https://docs.trezor.io).
|
||||
|
@ -1,11 +1,8 @@
|
||||
# trezor-common
|
||||
# Trezor Common
|
||||
|
||||
[![Build Status](https://travis-ci.org/trezor/trezor-common.svg?branch=master)](https://travis-ci.org/trezor/trezor-common)
|
||||
[![Gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community)
|
||||
|
||||
Common files shared among Trezor repositories.
|
||||
|
||||
This repo is meant to be included as submodule to others using:
|
||||
This project contains files shared among Trezor projects. All changes are happening inside the [Trezor Firmware repository](https://github.com/trezor/trezor-firmware).
|
||||
|
||||
We also export this project to the [trezor-common repository](https://github.com/trezor/trezor-common) as a read-only copy so third parties may depend on that instead of the whole monorepo. It is meant to be used as a submodule using:
|
||||
|
||||
```
|
||||
git submodule add https://github.com/trezor/trezor-common.git trezor-common
|
||||
|
@ -1,18 +1,5 @@
|
||||
# Trezor Core
|
||||
|
||||
![Trezor Logo](docs/logo.png)
|
||||
Firmware currently running on Model T.
|
||||
|
||||
[![Build Status](https://travis-ci.org/trezor/trezor-core.svg?branch=master)](https://travis-ci.org/trezor/trezor-core)
|
||||
[![Gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community)
|
||||
|
||||
This is the source code for 2nd generation of Trezor called Trezor model T.
|
||||
|
||||
It runs both inside of the device and also in the Trezor Emulator.
|
||||
|
||||
![emulator](docs/emulator/emulator.jpg)
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation can be found in the `docs` folder. See [SUMMARY.md](docs/SUMMARY.md) for a list of topics.
|
||||
|
||||
The documentation is also automatically built into a HTML format using [mdBook](https://github.com/rust-lang-nursery/mdBook) and deployed to [https://docs.trezor.io](https://docs.trezor.io/trezor-firmware/core/), where you can read it in a well arranged way.
|
||||
See `docs/core` for more info.
|
||||
|
2
core/docs/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
book
|
||||
!build/
|
@ -1,18 +0,0 @@
|
||||
# Summary
|
||||
|
||||
- [Introduction](intro.md)
|
||||
- [Build](build/index.md)
|
||||
- [Embedded](build/embedded.md)
|
||||
- [Emulator](build/emulator.md)
|
||||
- [Emulator](emulator/index.md)
|
||||
- [Hardware](hardware/index.md)
|
||||
- [Hardware](hardware/hardware.md)
|
||||
- [Boot stages](hardware/boot.md)
|
||||
- [Memory layout](hardware/memory.md)
|
||||
- [Trezor Core](src/index.md)
|
||||
- [Communication](src/communication.md)
|
||||
- [Apps](src/apps.md)
|
||||
- [Tests](tests/index.md)
|
||||
- [Miscellaneous](misc/index.md)
|
||||
- [Coins' BIP-44 Paths](misc/coins-bip44-paths.md)
|
||||
- [TOIF Image Format](misc/toif.md)
|
@ -1,7 +0,0 @@
|
||||
# Hardware
|
||||
|
||||
Currently, only Trezor T runs Trezor Core. However, we plan to port Core to Trezor One as well ([#24](https://github.com/trezor/trezor-firmware/issues/24)).
|
||||
|
||||
That being said, Core and Model T are tightly coupled together. That means, Core contains many T-only codebase and documentation.
|
||||
|
||||
This section is not concern with Model One whatsoever. Please see the Legacy documentation for that.
|
@ -1,8 +0,0 @@
|
||||
# Communication
|
||||
|
||||
We use [Protobuf v2](https://developers.google.com/protocol-buffers/) for host-device communication. The communication cycle is very simple, Trezor receives a message, acts on it and responds with another message. Trezor on its own is incapable of initiating the communication.
|
||||
|
||||
|
||||
## Definitions
|
||||
|
||||
Protobuf messages are defined in the [Common](https://github.com/trezor/trezor-firmware/tree/master/common) project, which is part of this monorepo. This repository is also exported to [trezor/trezor-common](https://github.com/trezor/trezor-common) to be used by third parties, which prefer not to include the whole monorepo. This copy is read-only and all changes are happening in this monorepo.
|
1
docs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
mdbook
|
30
docs/SUMMARY.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Summary
|
||||
|
||||
- [Introduction](index.md)
|
||||
- [Core](core/index.md)
|
||||
- [Build](core/build/index.md)
|
||||
- [Embedded](core/build/embedded.md)
|
||||
- [Emulator](core/build/emulator.md)
|
||||
- [Emulator](core/emulator/index.md)
|
||||
- [Apps](core/src/apps.md)
|
||||
- [Tests](core/tests/index.md)
|
||||
- [Legacy](legacy/index.md)
|
||||
- [Python](python/index.md)
|
||||
- [Hardware](hardware/index.md)
|
||||
- [Model T](hardware/model-t/index.md)
|
||||
- [Boot stages](hardware/model-t/boot.md)
|
||||
- [Memory layout](hardware/model-t/memory.md)
|
||||
- [Model One](hardware/model-one/index.md)
|
||||
- [Common](common/index.md)
|
||||
- [Communication](common/communication.md)
|
||||
- [Storage](storage/index.md)
|
||||
- [Tests](tests/index.md)
|
||||
- [CI](tests/ci.md)
|
||||
- [Device Tests](tests/device-tests.md)
|
||||
- [Upgrade Tests](tests/upgrade-tests.md)
|
||||
- [Miscellaneous](misc/index.md)
|
||||
- [Coins' BIP-44 Paths](misc/coins-bip44-paths.md)
|
||||
- [Generated Files](misc/generated-files.md)
|
||||
- [Git Hooks](misc/git-hooks.md)
|
||||
- [Monorepo Notes](misc/monorepo.md)
|
||||
- [TOIF Image Format](misc/toif.md)
|
@ -4,5 +4,5 @@ description = "Documentation of Trezor Core firmware."
|
||||
src = "."
|
||||
|
||||
[build]
|
||||
build-dir = "../build/docs"
|
||||
build-dir = "mdbook"
|
||||
create-missing = false
|
5
docs/common/communication.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Communication
|
||||
|
||||
We use [Protobuf v2](https://developers.google.com/protocol-buffers/) for host-device communication. The communication cycle is very simple, Trezor receives a message, acts on it and responds with another message. Trezor on its own is incapable of initiating the communication.
|
||||
|
||||
The Protobuf definitions can be found in `common/protob`.
|
15
docs/common/index.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Trezor Common
|
||||
|
||||
Common contains files shared among Trezor projects.
|
||||
|
||||
## Coin Definitions
|
||||
|
||||
JSON coin definitions and support tables.
|
||||
|
||||
## Protobuf Definitions
|
||||
|
||||
Common Protobuf definitions for the Trezor protocol.
|
||||
|
||||
## Tools
|
||||
|
||||
Tools for managing coin definitions and related data.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
@ -1,5 +1,5 @@
|
||||
# Introduction
|
||||
# Trezor Core
|
||||
|
||||
Trezor Core is the second-gen firmware running on Trezor devices. It currently runs on Trezor T only, but it will probably be used on Trezor One in future as well (see issue [#24](https://github.com/trezor/trezor-firmware/issues/24)).
|
||||
|
||||
Trezor Core is part of the trezor-firmware monorepo to be found on [GitHub](https://github.com/trezor/trezor-firmware), in the `core` subdirectory. You can read more on the monorepo structure in the root `docs` folder.
|
||||
Trezor Core is part of the trezor-firmware monorepo to be found on [GitHub](https://github.com/trezor/trezor-firmware), in the `core` subdirectory.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
@ -11,4 +11,4 @@ See the `core/tests/` directory.
|
||||
|
||||
## Common tests
|
||||
|
||||
See the [README](../../../tests/README.md) file in `tests/`.
|
||||
See the [tests](../../tests/index.md) section.
|
@ -1,7 +0,0 @@
|
||||
# Git hooks
|
||||
|
||||
## Install
|
||||
|
||||
Copy these files to `.git/hooks/` to activate them. Run in root:
|
||||
|
||||
`cp docs/git/hooks/* .git/hooks/`
|
1
docs/hardware/index.md
Normal file
@ -0,0 +1 @@
|
||||
# Hardware
|
5
docs/hardware/model-one/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Model One
|
||||
|
||||
To be documented.
|
||||
|
||||
In the meantime check out these great write-ups from @mcudev: https://mcudev.github.io.
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 518 KiB After Width: | Height: | Size: 518 KiB |
32
docs/index.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Trezor Firmware documentation
|
||||
|
||||
_This documentation can also be found at [docs.trezor.io](https://docs.trezor.io) where it is available in a HTML-built version compiled using [mdBook](https://github.com/rust-lang/mdBook)._
|
||||
|
||||
Welcome to the Trezor Firmware repository. This repository is so called _monorepo_, it contains several different yet very related projects that together form the Trezor Firmware ecosystem.
|
||||
|
||||
## Repository Structure
|
||||
|
||||
* **[`ci`](https://github.com/trezor/trezor-firmware/tree/master/ci/)**: [Gitlab CI](https://gitlab.com/satoshilabs/trezor/trezor-firmware) configuration files
|
||||
* **[`common/defs`](https://github.com/trezor/trezor-firmware/tree/master/common/defs/)**: JSON coin definitions and support tables
|
||||
* **[`common/protob`](https://github.com/trezor/trezor-firmware/tree/master/common/protob/)**: Common protobuf definitions for the Trezor protocol
|
||||
* **[`common/tools`](https://github.com/trezor/trezor-firmware/tree/master/common/tools/)**: Tools for managing coin definitions and related data
|
||||
* **[`core`](https://github.com/trezor/trezor-firmware/tree/master/core/)**: Trezor Core, firmware implementation for Trezor T
|
||||
* **[`crypto`](https://github.com/trezor/trezor-firmware/tree/master/crypto/)**: Stand-alone cryptography library used by both Trezor Core and the Trezor One firmware
|
||||
* **[`docs`](https://github.com/trezor/trezor-firmware/tree/master/docs/)**: Assorted documentation
|
||||
* **[`legacy`](https://github.com/trezor/trezor-firmware/tree/master/legacy/)**: Trezor One firmware implementation
|
||||
* **[`python`](https://github.com/trezor/trezor-firmware/tree/master/python/)**: Python [client library](https://pypi.org/project/trezor) and the `trezorctl` command
|
||||
* **[`storage`](https://github.com/trezor/trezor-firmware/tree/master/storage/)**: NORCOW storage implementation used by both Trezor Core and the Trezor One firmware
|
||||
* **[`tests`](https://github.com/trezor/trezor-firmware/tree/master/tests/)**: Firmware unit test suite
|
||||
* **[`tools`](https://github.com/trezor/trezor-firmware/tree/master/tools/)**: Miscellaneous build and helper scripts
|
||||
* **[`vendor`](https://github.com/trezor/trezor-firmware/tree/master/vendor/)**: Submodules for external dependencies
|
||||
|
||||
|
||||
## Contribute
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/trezor/trezor-firmware/tree/master/CONTRIBUTING.md).
|
||||
|
||||
Also please have a look at the docs, either in the `docs` folder or at [docs.trezor.io](https://docs.trezor.io) before contributing. The [misc](misc/index.md) chapter should be read in particular because it contains some useful assorted knowledge.
|
||||
|
||||
## Security vulnerability disclosure
|
||||
|
||||
Please report suspected security vulnerabilities in private to [security@satoshilabs.com](mailto:security@satoshilabs.com), also see [the disclosure section on the Trezor.io website](https://trezor.io/security/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
|
76
docs/legacy/index.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Trezor One Bootloader and Firmware
|
||||
|
||||
**TODO: this is currently outdated, see [#658](https://github.com/trezor/trezor-firmware/issues/658).**
|
||||
|
||||
[![Build Status](https://travis-ci.org/trezor/trezor-mcu.svg?branch=master)](https://travis-ci.org/trezor/trezor-mcu) [![gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community)
|
||||
|
||||
## How to build the Trezor bootloader, firmware and emulator
|
||||
|
||||
Ensure that you have Docker installed. You can follow [Docker's installation instructions](https://docs.docker.com/engine/installation/).
|
||||
|
||||
Clone this repository:
|
||||
```sh
|
||||
git clone https://github.com/trezor/trezor-mcu.git`
|
||||
cd trezor-mcu
|
||||
```
|
||||
|
||||
Use the `build.sh` command to build the images.
|
||||
|
||||
* to build bootloader 1.6.0 and firmware 1.7.0:
|
||||
```sh
|
||||
./build.sh bl1.6.0 v1.7.0
|
||||
```
|
||||
* to build latest firmware from master:
|
||||
```sh
|
||||
./build.sh
|
||||
```
|
||||
* to build the emulator from master:
|
||||
```sh
|
||||
./build.sh EMU
|
||||
```
|
||||
* to build the emulator for version 1.7.0:
|
||||
```sh
|
||||
./build.sh EMU v1.7.0
|
||||
```
|
||||
|
||||
Build results are stored in the `build/` directory. File `bootloader-<tag>.bin` represents
|
||||
the bootloader, `trezor-<tag>.bin` is the firmware image, and `trezor-emulator-<tag>.elf`
|
||||
is the emulator executable.
|
||||
|
||||
You can use `TREZOR_OLED_SCALE` environment variable to make emulator screen bigger.
|
||||
|
||||
## How to get fingerprint of firmware signed and distributed by SatoshiLabs?
|
||||
|
||||
1. Pick version of firmware binary listed on https://wallet.trezor.io/data/firmware/1/releases.json
|
||||
2. Download it: `wget -O trezor.signed.bin https://wallet.trezor.io/data/firmware/1/trezor-1.6.1.bin`
|
||||
3. Compute fingerprint: `tail -c +257 trezor.signed.bin | sha256sum`
|
||||
|
||||
Step 3 should produce the same sha256 fingerprint like your local build (for the same version tag). Firmware has a special header (of length 256 bytes) holding signatures themselves, which must be avoided while calculating the fingerprint, that's why tail command has to be used.
|
||||
|
||||
## How to install custom built firmware?
|
||||
|
||||
**WARNING: This will erase the recovery seed stored on the device! You should never do this on Trezor that contains coins!**
|
||||
|
||||
1. Install python-trezor: `pip install trezor` ([more info](https://github.com/trezor/python-trezor))
|
||||
2. `trezorctl firmware_update -f build/trezor-TAG.bin`
|
||||
|
||||
*Note: if your device is on the latest bootloader version and you flash custom firmware then you will receive a hard fault warning when booting the firmware image. To avoid this issue, just remove the code in startup.s added by [this commit](https://github.com/trezor/trezor-firmware/commit/222c9ea46c7574cb52d4713c481438a32b85e692#diff-178d0ab7c4debbcf430a0fad8fa06a5c).*
|
||||
|
||||
## Building for development
|
||||
|
||||
If you want to build device firmware, make sure you have the
|
||||
[GNU ARM Embedded toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) installed.
|
||||
You will also need Python 3.5 or later and [pipenv](https://pipenv.readthedocs.io/en/latest/install/).
|
||||
|
||||
* If you want to build the emulator instead of the firmware, run `export EMULATOR=1`
|
||||
* If you want to build with the debug link, run `export DEBUG_LINK=1`. Use this if you want to run the device tests.
|
||||
* When you change these variables, use `script/setup` to clean the repository
|
||||
|
||||
1. To initialize the repository, run `script/setup`
|
||||
2. To initialize a Python environment, run `pipenv install`
|
||||
3. To build the firmware or emulator, run `pipenv run script/cibuild`
|
||||
|
||||
If you are building device firmware, the firmware will be in `firmware/trezor.bin`.
|
||||
|
||||
You can launch the emulator using `firmware/trezor.elf`. To use `trezorctl` with the emulator, use
|
||||
`trezorctl -p udp` (for example, `trezorctl -p udp get_features`).
|
@ -1,14 +1,4 @@
|
||||
# Assorted knowledge
|
||||
|
||||
This file serves as a dumping ground for important knowledge tidbits that do not clearly
|
||||
fit in any particular location. Please add any information that you think should be
|
||||
written down.
|
||||
|
||||
At any time, information stored here might be restructured or moved to a different
|
||||
location, so as to ensure that the documentation is well structured overall.
|
||||
|
||||
|
||||
### Generated files
|
||||
# Generated files
|
||||
|
||||
Certain files in the repository are auto-generated from other sources, but the generated
|
||||
content is stored in Git. The command `make gen_check`, run from CI, ensures that the
|
||||
@ -17,7 +7,7 @@ files.
|
||||
|
||||
In general, generated files are not compatible between branches. After rebasing or
|
||||
merging a different branch, you should immediately run `make gen` and make sure the
|
||||
result is commited.
|
||||
result is committed.
|
||||
|
||||
**Do not fix merge conflicts in generated files**. Instead, run `make gen` and commit
|
||||
the result.
|
7
docs/misc/git-hooks.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Git Hooks
|
||||
|
||||
## Install
|
||||
|
||||
Copy `docs/git/hooks/` files to `.git/hooks/` to activate them. Run in root:
|
||||
|
||||
`cp docs/git/hooks/* .git/hooks/`
|
8
docs/misc/index.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Assorted knowledge
|
||||
|
||||
This file serves as a dumping ground for important knowledge tidbits that do not clearly
|
||||
fit in any particular location. Please add any information that you think should be
|
||||
written down.
|
||||
|
||||
At any time, information stored here might be restructured or moved to a different
|
||||
location, so as to ensure that the documentation is well structured overall.
|
5
docs/python/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Python
|
||||
|
||||
To be documented by @matejcik, see [#229](https://github.com/trezor/trezor-firmware/issues/229).
|
||||
|
||||
In the meantime see `python/docs` and `python/README.md`.
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
291
docs/storage/index.md
Normal file
@ -0,0 +1,291 @@
|
||||
# Trezor Storage
|
||||
|
||||
The `storage` folder contains the implementation of Trezor's internal storage, which is common for both Legacy (Trezor One) and Core (Trezor T). This README also contains a detailed description of the cryptographic design.
|
||||
|
||||
All tests are located in the `tests` subdirectory, which also includes a Python implementation to run tests against this C production version and the Python one.
|
||||
|
||||
## Summary
|
||||
|
||||
The PIN is no longer stored in the flash storage. A new entry is added to the flash storage consisting of a 256-bit encrypted data encryption key (EDEK) followed by a 128-bit encrypted storage authentication key (ESAK) and a 64-bit PIN verification code (PVC). The PIN is used to decrypt the EDEK and ESAK and the PVC is be used to verify that the correct PIN was used. The resulting data encryption key (DEK) is then used to encrypt/decrypt protected entries in the flash storage. We use Chacha20Poly1305 as defined in [RFC 7539](https://tools.ietf.org/html/rfc7539) to encrypt the EDEK and the protected entries. The storage authentication key (SAK) is used to authenticate the list of (APP, KEY) values for all protected entries that have been set in the storage. This prevents an attacker from erasing or adding entries to the storage.
|
||||
|
||||
## Storage format
|
||||
|
||||
Entries fall into three categories:
|
||||
|
||||
| Category | Condition | Read | Write |
|
||||
|-----------|-----------------|--------------------|--------------------|
|
||||
| Private | APP = 0 | Never | Never |
|
||||
| Protected | 1 ≤ APP ≤ 127 | Only when unlocked | Only when unlocked |
|
||||
| Public | 128 ≤ APP ≤ 255 | Always | Only when unlocked |
|
||||
|
||||
The format of public entries has remained unchanged, that is:
|
||||
|
||||
| Data | KEY | APP | LEN | DATA |
|
||||
|----------------|-----|-----|-----|------|
|
||||
| Length (bytes) | 1 | 1 | 2 | LEN |
|
||||
|
||||
Private values are used to store storage-specific information and cannot be directly accessed through the storage interface. Protected entries have the following new format:
|
||||
|
||||
| Data | KEY | APP | LEN | IV | TAG | ENCRDATA |
|
||||
|----------------|-----|-----|-----|----|-----|----------|
|
||||
| Length (bytes) | 1 | 1 | 2 | 12 | 16 | LEN - 28 |
|
||||
|
||||
The LEN value thus indicates the total length of IV, TAG and ENCRDATA.
|
||||
|
||||
The random salt (32 bits), EDEK (256 bits), ESAK (128 bits) and PVC (64 bits) is stored in a single entry under APP=0, KEY=2:
|
||||
|
||||
| Data | KEY | APP | LEN | SALT | EDEK | ESAK | PVC |
|
||||
|----------------|-----|-----|-------|------|------|------|-----|
|
||||
| Length (bytes) | 1 | 1 | 2 | 4 | 32 | 16 | 8 |
|
||||
| Value | 02 | 00 | 3C 00 | | | | |
|
||||
|
||||
The storage authentication tag (128 bits) is stored in a single entry under APP=0, KEY=5:
|
||||
|
||||
| Data | KEY | APP | LEN | TAG |
|
||||
|----------------|-----|-----|-------|-----|
|
||||
| Length (bytes) | 1 | 1 | 2 | 16 |
|
||||
| Value | 05 | 00 | 20 00 | |
|
||||
|
||||
Furthermore, if any entry is overwritten, the old entry is erased, i.e., overwritten with 0. We are also using APP=0, KEY=0 as marker that the entry is erased (this was formerly used for the PIN entry, which is not needed anymore).
|
||||
|
||||
## PIN verification and decryption of protected entries in flash storage
|
||||
|
||||
1. From the flash storage read the entry containing the random salt, EDEK and PVC.
|
||||
|
||||
2. Gather constant data from various system resources such as the ProcessorID (aka Unique device ID) and any hardware serial numbers that are available. The concatenation of this data with the random salt will be referred to as *salt*.
|
||||
3. Prompt the user to enter the PIN. Prefix the entered PIN with a "1" digit in base 10 and convert the integer to 4 bytes in little endian byte order. Then compute:
|
||||
|
||||
`PBKDF2(PRF = HMAC-SHA256, Password = pin, Salt = salt, iterations = 10000, dkLen = 352 bits)`
|
||||
|
||||
The first 256 bits of the output will be used as the key encryption key (KEK) and the remaining 96 bits will be used as the key encryption initialization vector (KEIV).
|
||||
|
||||
*Note: Since two blocks of output need to be produced in PBKDF2 the total number of iterations is 20000.*
|
||||
|
||||
4. Compute:
|
||||
|
||||
`(dek, tag) = ChaCha20Poly1305Decrypt(kek, keiv, edek)`
|
||||
|
||||
5. Compare the PVC read from the flash storage with the first 64 bits of the computed tag value. If there is a mismatch, then fail. Otherwise store the DEK in a global variable.
|
||||
|
||||
6. When a protected entry needs to be decrypted, load the IV, ENCRDATA and TAG of the entry and compute:
|
||||
|
||||
`(data, tag) = ChaCha20Poly1305Decrypt(dek, iv, (key || app), encrdata)`
|
||||
|
||||
where the APP and KEY of the entry is used as two bytes of associated data. Compare the TAG read from the flash storage with the computed tag value. If there is a mismatch, then fail.
|
||||
|
||||
![summary](assets/key-derivation.svg)
|
||||
|
||||
## Initializing the EDEK
|
||||
|
||||
1. When the storage is initialized, generate the 32 bit random salt and 256 bit DEK using a cryptographically secure random number generator.
|
||||
|
||||
2. Set a boolean value in the storage denoting that the PIN has not been set. Use an empty PIN to derive the KEK and KEIV as described above.
|
||||
|
||||
3. Encrypt the DEK using the derived KEK and KEIV:
|
||||
|
||||
`(edek, tag) = ChaCha20Poly1305Encrypt(kek, keiv, dek)`
|
||||
|
||||
4. Store the random salt, EDEK value and the first 64 bits of the tag as the PVC.
|
||||
|
||||
## Setting a new PIN
|
||||
|
||||
1. If the PIN has already been set, then prompt the user to enter the old PIN value, check the PVC and compute the DEK as described above in steps 1-4.
|
||||
|
||||
2. Generate a new 32 bit random salt and prompt the user to enter the new PIN value. Use these values to derive the new KEK and KEIV as described above.
|
||||
|
||||
3. Encrypt the DEK using the new KEK and KEIV:
|
||||
|
||||
`(edek, tag) = ChaCha20Poly1305Encrypt(kek, keiv, dek)`
|
||||
|
||||
4. Store the new EDEK value and the first 64 bits of the tag as the new PVC. This operation should be atomic, i.e. either both values should be stored or neither. Overwrite the old values of the EDEK and PVC with zeros.
|
||||
|
||||
## Encryption of protected entries in flash storage
|
||||
|
||||
Whenever the value of an entry needs to be updated, a fresh IV is generated using a cryptographically secure random number generator and the data is encrypted as `(encrdata, tag) = ChaCha20Poly1305Encrypt(dek, iv, (key || app), data)`.
|
||||
|
||||
## Storage authentication
|
||||
|
||||
The storage authentication key (SAK) will be used to generate a storage authentication tag (SAT) for the list of all (APP, KEY) values of protected entries (1 ≤ APP ≤ 127) that have been set in the storage. The SAT will be checked during every get operation. When a new protected entry is added to the storage or when a protected entry is deleted from the storage, the value of the SAT will be updated. The value of the SAT is defined as the first 16 bytes of
|
||||
|
||||
`HMAC-SHA-256(SAK, ⨁i HMAC-SHA-256(SAK, KEY_i || APP_i))`
|
||||
|
||||
where `⨁` denotes the n-ary bitwise XOR operation and KEY_i || APP_i is a two-byte encoding of the value of the *i*-th (APP, KEY) such that 1 ≤ APP ≤ 127.
|
||||
|
||||
## Design rationale
|
||||
|
||||
- The purpose of the PBKDF2 function is to thwart brute-force attacks in case the attacker is able to circumvent the PIN entry counter mechanism but does not have full access to the contents of the flash storage of the device, e.g. fault injection attacks. For an attacker that would be able to read the flash storage and obtain the salt, the PBKDF2 with 20000 iterations and a 4- to 9-digit PIN would not pose an obstacle.
|
||||
|
||||
- The reason why we use a separate data encryption key rather than using the output of PBKDF2 directly to encrypt the sensitive entries is so that when the user decides to change their PIN, only the EDEK needs to be reencrypted, but the remaining entries do not need to be updated.
|
||||
|
||||
- We se ChaCha20 for encryption, because as a stream cipher it has no padding overhead and its implementation is readily available in trezor-crypto. A possible alternative to using ChaCha20Poly1305 for DEK encryption is to use AES-CTR with HMAC in an encrypt-then-MAC scheme. A possible alternative to using ChaCha20 for encryption of other data entries is to use AES-XTS (XEX-based tweaked-codebook mode with ciphertext stealing), which was designed specifically for disk-encryption. The APP || KEY value would be used as the tweak.
|
||||
- Advantages of AES-XTS:
|
||||
- Does not require an initialization vector.
|
||||
- Ensures better diffusion than a stream cipher, which eliminates the above concerns about malleability and fault injection attacks.
|
||||
- Disadvantages of AES-XTS:
|
||||
- Not implemented in trezor-crypto.
|
||||
- Requires two keys of length at least 128 bits.
|
||||
|
||||
- A 32-bit PVC would be sufficient to verify the PIN value, since there would be less than a 1 in 4 chance that there exists a false PIN, which has the same PVC as the correct PIN. Nevertheless, we decided to go with a 64-bit PVC to achieve a larger security margin. The chance that there exists a false PIN, which has the same PVC as the correct PIN, then drops below 1 in 10^10. The existence of a false PIN does not appear to pose a security weakness, since the false PIN cannot be used to decrypt the protected entries.
|
||||
|
||||
- Instead of using separate IVs for each entry we considered using a single IV for the entire sector. Upon sector compaction a new IV would have to be generated and the encrypted data would have to be reencrypted under the new IV. A possible issue with this approach is that compaction cannot happen without the DEK, i.e. generally data could not be written to the flash storage without knowing the PIN. This property might not always be desirable.
|
||||
|
||||
## New measures for PIN entry counter protection
|
||||
|
||||
The former implementation of the PIN entry counter was vulnerable to fault injection attacks.
|
||||
|
||||
Under the former implementation the PIN counter storage entry consisted of 32 words initialized to 0xFFFFFFFF. The first non-zero word in this area was the current PIN failure counter. Before verifying the PIN the lowest bit with value 1 was set to 0, i.e. a value of FFFFFFFC indicated two PIN entries. Upon successful PIN entry, the word was set to 0x00000000, indicating that the next word was the PIN failure counter. Allegedly, by manipulating the voltage on the USB input an attacker could convince the device to read the PIN entry counter as 0xFFFFFFFF even if some of the bits had been set to 0.
|
||||
|
||||
### Design goals
|
||||
|
||||
- Make it easy to decrement the counter by changing a 1 bit to 0.
|
||||
- Make it hard to reset the counter by a fault injection, i.e. counter values should not have an overly simple binary representation like 0xFFFFFFFF.
|
||||
- If possible, use two or more different methods of checking the counter value so that an attacker has to mount different fault injection attacks to succeed.
|
||||
- Optimize the format for successful PIN entry.
|
||||
- Minimize the number of branching operations. Avoid loops, instead utilize bitwise and arithmetic operations when processing the PIN counter data.
|
||||
|
||||
### Proposal summary
|
||||
|
||||
Under the former implementation, for every unsuccessful PIN entry we discarded one bit from the counter, while for every successful PIN entry we discard an entire word. In the new implementation we optimize the counter operations for successful PIN entry.
|
||||
|
||||
The basic idea is that there are two binary logs stored in the flash storage, e.g.:
|
||||
|
||||
```
|
||||
...0001111111111111... pin_success_log
|
||||
...0000001111111111... pin_entry_log
|
||||
```
|
||||
|
||||
Before every PIN verification the highest 1-bit in the pin_entry_log is set to 0. If the verification succeeds, then the corresponding bit in the pin_success_log is also set to 0. The example above shows the status of the logs when the last three PIN entries were not successful.
|
||||
|
||||
In actual fact the logs are not written to the flash storage exactly as shown above, but they are stored in a form that should protect them against fault injection attacks. Only half of the stored bits carry information, the other half acts as "guard bits". So a stored value `...001110...` could look like `...0g0gg1g11g0g...`, where g denotes a guard bit. The positions and the values of the guard bits are determined by a guard key. The guard_key is a randomly generated uint32 value stored as an entry in the flash memory in cleartext. The assumption behind this is that an attacker attempting to reset or decrement the PIN counter by a fault injection is not able to read the flash storage. However, the value of guard_key also needs to be protected against fault injection, so the set of valid guard_key values should be limited by some condition which is easy to verify, such as guard_key mod M == C, where M and C a suitably chosen constants. The constants should be chosen so that the binary representation of any valid guard_key value has Hamming weight between 8 and 24. These conditions are discussed below.
|
||||
|
||||
### Storage format
|
||||
|
||||
The PIN log has APP = 0 and KEY = 1. The DATA part of the entry consists of 33 words (132 bytes, assuming 32-bit words):
|
||||
|
||||
- guard_key (1 word)
|
||||
- pin_success_log (16 words)
|
||||
- pin_entry_log (16 words)
|
||||
|
||||
Each log is stored in big-endian word order. The byte order of each word is platform dependent.
|
||||
|
||||
### Guard key validation
|
||||
|
||||
The guard_key is said to be valid if the following three conditions hold true:
|
||||
|
||||
1. Each byte of the binary representation of the guard_key has a balanced number of zeros and ones at the positions corresponding to the guard values (that is those bits in the mask 0xAAAAAAAA).
|
||||
2. The guard_key binary representation does not contain a run of 5 (or more) zeros or ones.
|
||||
3. The guard_key integer representation is congruent to 15 modulo 6311.
|
||||
|
||||
Key validity can be checked with this function:
|
||||
|
||||
```c
|
||||
int key_validity(uint32_t guard_key)
|
||||
{
|
||||
uint32_t count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222);
|
||||
count = count + (count >> 4);
|
||||
|
||||
uint32_t zero_runs = ~guard_key;
|
||||
zero_runs = zero_runs & (zero_runs >> 2);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
uint32_t one_runs = guard_key;
|
||||
one_runs = one_runs & (one_runs >> 2);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
|
||||
return ((count & 0x0e0e0e0e) == 0x04040404) & (one_runs == 0) & (zero_runs == 0) & (guard_key % 6311 == 15);
|
||||
}
|
||||
```
|
||||
|
||||
### Key generation
|
||||
|
||||
The guard_key may be generated in the following way:
|
||||
|
||||
1. Generate a random integer *r* in such that 0 ≤ *r* ≤ 680552 with uniform probability.
|
||||
2. Set *r* = *r* * 6311 + 15.
|
||||
3. If *key_validity(r)* is not true go back to the step 1.
|
||||
|
||||
Note that on average steps 1 to 3 are repeated about one hundred times.
|
||||
|
||||
### Key expansion
|
||||
|
||||
The guard_key is read from storage, its value is checked for validity and used to compute the guard_mask (indicating the positions of the guard bits) and guard value (indicating the values of the guard bits on their actual positions):
|
||||
|
||||
```c
|
||||
LOW_MASK = 0x55555555
|
||||
guard_mask = ((guard_key & LOW_MASK) << 1) |
|
||||
((~guard_key) & LOW_MASK)
|
||||
guard = (((guard_key & LOW_MASK) << 1) & guard_key) |
|
||||
(((~guard_key) & LOW_MASK) & (guard_key >> 1))
|
||||
```
|
||||
|
||||
**Explanation**:
|
||||
|
||||
The guard_key contains two pieces of information. The position of the guard bits but also their corresponding values. The bitwise format of the guard_key is `vpvpvp...vp`. The bits labelled `p` indicate the position of each guard bit and the bits labelled `v` indicate its value.
|
||||
|
||||
The guard_mask is derived from the guard_key and has the form `xyxyxy...xy` where x+y = 1 (in other words, there is exactly one 1 bit in each pair xy). First, we set the `x` bits:
|
||||
|
||||
`(guard_key & LOW_MASK) << 1`
|
||||
|
||||
and the `y` bits to its corresponding complement:
|
||||
|
||||
`(~guard_key) & LOW_MASK`
|
||||
|
||||
That ensures that only one 1 bit is present in each pair `xy`. The guard value is equal to the bits labelled `v` in the guard_key but only at the positions indicated by the guard_mask. The guard value is therefore equal to:
|
||||
|
||||
```
|
||||
-------- x bits mask --------- & -- guard_key --
|
||||
guard = (((guard_key & LOW_MASK) << 1) & guard_key) |
|
||||
----- y bits mask ---- & - guard_key shifted to v bits
|
||||
(((~guard_key) & LOW_MASK) & (guard_key >> 1))
|
||||
```
|
||||
|
||||
### Log initialization
|
||||
|
||||
Each log is stored as 16 consecutive words each initialized to:
|
||||
|
||||
`guard | ~guard_mask`
|
||||
|
||||
### Removing and adding guard bits
|
||||
|
||||
After reading a word from the flash storage we verify the format by checking the condition:
|
||||
|
||||
`(word & guard_mask) == guard`
|
||||
|
||||
and then remove the guard bits as follows:
|
||||
|
||||
```
|
||||
word = word & ~guard_mask
|
||||
word = ((word >> 1) | word ) & LOW_MASK
|
||||
word = word | (word << 1)
|
||||
```
|
||||
|
||||
This operation replaces each guard bit with the value of its neighbouring bit, e.g. `…0g0gg1g11g0g…` is converted to `…000011111100…` Thus each non-guard bit is duplicated.
|
||||
|
||||
The guard bits can be added back as follows:
|
||||
|
||||
`word = (word & ~guard_mask) | guard`
|
||||
|
||||
### Determining the number of PIN failures
|
||||
|
||||
Remove the guard bits from the words of the pin_entry_log using the operations described above and verify that the result has form 0\*1\* by checking the condition:
|
||||
|
||||
`word & (word + 1) == 0`
|
||||
|
||||
Then verify that the pin_entry_log and pin_success_log are in sync by checking the condition:
|
||||
|
||||
`pin_entry_log & pin_success_log == pin_entry_log`
|
||||
|
||||
Finally, determine the current number of PIN failures by counting the number of set bits in the evaluation of the following expression:
|
||||
|
||||
`pin_success_log xor pin_entry_log`
|
||||
|
||||
Note that the number of set bits in a word can be counted using bitwise and arithmetic operations. For a 32-bit word the following can be used:
|
||||
|
||||
```c
|
||||
count = word - ((word >> 1) & 0x55555555)
|
||||
count = (count & 0x33333333) + ((count >> 2) & 0x33333333)
|
||||
count = (count + (count >> 4)) & 0x0F0F0F0F
|
||||
count = count + (count >> 8)
|
||||
count = (count + (count >> 16)) & 0x3F
|
||||
```
|
5
docs/tests/ci.md
Normal file
@ -0,0 +1,5 @@
|
||||
# CI
|
||||
|
||||
The complete test suite is running on a public [GitLab CI](https://gitlab.com/satoshilabs/trezor/trezor-firmware). If you are an external contributor, we also have a [Travis instance](https://travis-ci.org/trezor/trezor-firmware) where a small subset of tests is running as well - mostly style and easy fast checks, which are quite common to fail for new contributors.
|
||||
|
||||
The CI folder contains all the .yml GitLab files that are included in the main `.gitlab.yml` to provide some basic structure. All GitLab CI Jobs run inside a docker image, which is built using the present `Dockerfile`. This image is stored in the GitLab registry. On any changes to the `Dockerfile` the CI Job "environment" must be **manually** triggered to build and upload the new version of the image.
|
75
docs/tests/device-tests.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Running device tests
|
||||
|
||||
## 1. Running the full test suite
|
||||
|
||||
_Note: You need Pipenv, as mentioned in the core's [documentation](https://docs.trezor.io/trezor-firmware/core/) section._
|
||||
|
||||
In the `trezor-firmware` checkout, in the root of the monorepo, install the environment:
|
||||
|
||||
```sh
|
||||
pipenv sync
|
||||
```
|
||||
|
||||
And run the automated tests:
|
||||
|
||||
```sh
|
||||
pipenv run make -C core test_emu
|
||||
```
|
||||
|
||||
## 2. Running tests manually
|
||||
|
||||
Install the pipenv environment as outlined above. Then switch to a shell inside the
|
||||
environment:
|
||||
|
||||
```sh
|
||||
pipenv shell
|
||||
```
|
||||
|
||||
If you want to test against the emulator, run it in a separate terminal from the `core`
|
||||
subdirectory:
|
||||
```sh
|
||||
PYOPT=0 ./emu.sh
|
||||
```
|
||||
|
||||
Now you can run the test suite with `pytest` from the root directory:
|
||||
```sh
|
||||
pytest tests/device_tests
|
||||
```
|
||||
|
||||
### Useful Tips
|
||||
|
||||
The tests are randomized using the [pytest-random-order] plugin. The random seed is printed in the header of the tests output, in case you need to run the tests in the same order.
|
||||
|
||||
If you only want to run a particular test, pick it with `-k <keyword>` or `-m <marker>`:
|
||||
|
||||
```sh
|
||||
pytest -k nem # only runs tests that have "nem" in the name
|
||||
pytest -m stellar # only runs tests marked with @pytest.mark.stellar
|
||||
```
|
||||
|
||||
If you want to see debugging information and protocol dumps, run with `-v`.
|
||||
|
||||
If you would like to interact with the device (i.e. press the buttons yourself), just prefix pytest with `INTERACT=1`:
|
||||
|
||||
```sh
|
||||
INTERACT=1 pytest tests/device_tests
|
||||
```
|
||||
|
||||
## 3. Using markers
|
||||
|
||||
When you're developing a new currency, you should mark all tests that belong to that
|
||||
currency. For example, if your currency is called NewCoin, your device tests should have
|
||||
the following marker:
|
||||
|
||||
```python
|
||||
@pytest.mark.newcoin
|
||||
```
|
||||
|
||||
This marker must be registered in [REGISTERED_MARKERS] file.
|
||||
|
||||
If you wish to run a test only on TT, mark it with `@pytest.mark.skip_t1`.
|
||||
If the test should only run on T1, mark it with `@pytest.mark.skip_t2`.
|
||||
You must not use both on the same test.
|
||||
|
||||
[pytest-random-order]: https://pypi.org/project/pytest-random-order/
|
||||
[REGISTERED_MARKERS]: ../REGISTERED_MARKERS
|
34
docs/tests/index.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Tests
|
||||
|
||||
## Burn tests
|
||||
|
||||
These tests are doing a simple read/write operations on the device to see if the hardware can endure high number of flash writes. Meant to be run on the device directly for a long period of time.
|
||||
|
||||
## Device tests
|
||||
|
||||
Device tests are integration tests that can be run against either emulator or on an actual device.
|
||||
You are responsible to provide either an emulator or a device with Debug mode present.
|
||||
|
||||
### Device tests
|
||||
|
||||
The original version of device tests. These tests can be run against both Model One and Model T.
|
||||
|
||||
See [device-tests.md](device-tests.md) for instructions how to run it.
|
||||
|
||||
### Click tests
|
||||
|
||||
Click tests are a next-generation of the Device tests. The tests are quite similar, but they are capable of imitating user's interaction with the screen.
|
||||
|
||||
## Fido tests
|
||||
|
||||
Implement U2F/FIDO2 tests.
|
||||
|
||||
## Upgrade tests
|
||||
|
||||
These tests test upgrade from one firmware version to another. They initialize an emulator on some specific version and then pass its storage to another version to see if the firmware operates as expected. They use fixtures from https://firmware.corp.sldev.cz/upgrade_tests/ which can be downloaded using the `download_emulators.sh` script.
|
||||
|
||||
See the [upgrade-tests.md](upgrade-tests.md) for instructions how to run it.
|
||||
|
||||
## Persistence tests
|
||||
|
||||
These tests test the Persistence mode, which is currently used in the device recovery. These tests launch the emulator themselves and they are capable of restarting or stopping it simulating user's plugging in or plugging out the device.
|
27
docs/tests/upgrade-tests.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Running Upgrade Tests
|
||||
|
||||
1. As always, use pipenv environment:
|
||||
|
||||
```sh
|
||||
pipenv shell
|
||||
```
|
||||
|
||||
2. Download the emulators, if you have not already:
|
||||
|
||||
```sh
|
||||
tests/download_emulators.sh
|
||||
```
|
||||
|
||||
3. And run the tests using pytest:
|
||||
|
||||
```sh
|
||||
pytest tests/upgrade_tests
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
You can use `TREZOR_UPGRADE_TEST` environment variable if you would like to run core or legacy upgrade tests exclusively. This will run `core` only:
|
||||
|
||||
```sh
|
||||
TREZOR_UPGRADE_TEST="core" pytest tests/upgrade_tests
|
||||
```
|
@ -1,5 +0,0 @@
|
||||
# Contribute to Trezor MCU
|
||||
|
||||
Please read the general instructions you can find on our [wiki](https://wiki.trezor.io/Developers_guide:Contributing).
|
||||
|
||||
If you are working on a new feature, you probably want to contribute this to the [trezor-core](https://github.com/trezor/trezor-core) repository instead.
|
@ -1,76 +1,5 @@
|
||||
# Trezor One Bootloader and Firmware
|
||||
# Trezor Legacy
|
||||
|
||||
[![Build Status](https://travis-ci.org/trezor/trezor-mcu.svg?branch=master)](https://travis-ci.org/trezor/trezor-mcu) [![gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community)
|
||||
Firmware currently running on Model One. If you are working on a new feature, you probably want to contribute this to Core.
|
||||
|
||||
https://trezor.io/
|
||||
|
||||
## How to build the Trezor bootloader, firmware and emulator
|
||||
|
||||
Ensure that you have Docker installed. You can follow [Docker's installation instructions](https://docs.docker.com/engine/installation/).
|
||||
|
||||
Clone this repository:
|
||||
```sh
|
||||
git clone https://github.com/trezor/trezor-mcu.git`
|
||||
cd trezor-mcu
|
||||
```
|
||||
|
||||
Use the `build.sh` command to build the images.
|
||||
|
||||
* to build bootloader 1.6.0 and firmware 1.7.0:
|
||||
```sh
|
||||
./build.sh bl1.6.0 v1.7.0
|
||||
```
|
||||
* to build latest firmware from master:
|
||||
```sh
|
||||
./build.sh
|
||||
```
|
||||
* to build the emulator from master:
|
||||
```sh
|
||||
./build.sh EMU
|
||||
```
|
||||
* to build the emulator for version 1.7.0:
|
||||
```sh
|
||||
./build.sh EMU v1.7.0
|
||||
```
|
||||
|
||||
Build results are stored in the `build/` directory. File `bootloader-<tag>.bin` represents
|
||||
the bootloader, `trezor-<tag>.bin` is the firmware image, and `trezor-emulator-<tag>.elf`
|
||||
is the emulator executable.
|
||||
|
||||
You can use `TREZOR_OLED_SCALE` environment variable to make emulator screen bigger.
|
||||
|
||||
## How to get fingerprint of firmware signed and distributed by SatoshiLabs?
|
||||
|
||||
1. Pick version of firmware binary listed on https://wallet.trezor.io/data/firmware/1/releases.json
|
||||
2. Download it: `wget -O trezor.signed.bin https://wallet.trezor.io/data/firmware/1/trezor-1.6.1.bin`
|
||||
3. Compute fingerprint: `tail -c +257 trezor.signed.bin | sha256sum`
|
||||
|
||||
Step 3 should produce the same sha256 fingerprint like your local build (for the same version tag). Firmware has a special header (of length 256 bytes) holding signatures themselves, which must be avoided while calculating the fingerprint, that's why tail command has to be used.
|
||||
|
||||
## How to install custom built firmware?
|
||||
|
||||
**WARNING: This will erase the recovery seed stored on the device! You should never do this on Trezor that contains coins!**
|
||||
|
||||
1. Install python-trezor: `pip install trezor` ([more info](https://github.com/trezor/python-trezor))
|
||||
2. `trezorctl firmware_update -f build/trezor-TAG.bin`
|
||||
|
||||
*Note: if your device is on the latest bootloader version and you flash custom firmware then you will receive a hard fault warning when booting the firmware image. To avoid this issue, just remove the code in startup.s added by [this commit](https://github.com/trezor/trezor-firmware/commit/222c9ea46c7574cb52d4713c481438a32b85e692#diff-178d0ab7c4debbcf430a0fad8fa06a5c).*
|
||||
|
||||
## Building for development
|
||||
|
||||
If you want to build device firmware, make sure you have the
|
||||
[GNU ARM Embedded toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) installed.
|
||||
You will also need Python 3.5 or later and [pipenv](https://pipenv.readthedocs.io/en/latest/install/).
|
||||
|
||||
* If you want to build the emulator instead of the firmware, run `export EMULATOR=1`
|
||||
* If you want to build with the debug link, run `export DEBUG_LINK=1`. Use this if you want to run the device tests.
|
||||
* When you change these variables, use `script/setup` to clean the repository
|
||||
|
||||
1. To initialize the repository, run `script/setup`
|
||||
2. To initialize a Python environment, run `pipenv install`
|
||||
3. To build the firmware or emulator, run `pipenv run script/cibuild`
|
||||
|
||||
If you are building device firmware, the firmware will be in `firmware/trezor.bin`.
|
||||
|
||||
You can launch the emulator using `firmware/trezor.elf`. To use `trezorctl` with the emulator, use
|
||||
`trezorctl -p udp` (for example, `trezorctl -p udp get_features`).
|
||||
See `docs/legacy` for more info.
|
||||
|
@ -1,291 +0,0 @@
|
||||
# Trezor Storage
|
||||
|
||||
This repository contains the implementation of Trezor's internal storage, which is common for both trezor-mcu (Trezor One) and trezor-core (Trezor T). This README also contains a detailed description of the cryptographic design.
|
||||
|
||||
All tests are located in the `tests` subdirectory, which also includes a Python implementation to run tests against this C production version and the Python one.
|
||||
|
||||
## Summary
|
||||
|
||||
The PIN is no longer stored in the flash storage. A new entry is added to the flash storage consisting of a 256-bit encrypted data encryption key (EDEK) followed by a 128-bit encrypted storage authentication key (ESAK) and a 64-bit PIN verification code (PVC). The PIN is used to decrypt the EDEK and ESAK and the PVC is be used to verify that the correct PIN was used. The resulting data encryption key (DEK) is then used to encrypt/decrypt protected entries in the flash storage. We use Chacha20Poly1305 as defined in [RFC 7539](https://tools.ietf.org/html/rfc7539) to encrypt the EDEK and the protected entries. The storage authentication key (SAK) is used to authenticate the list of (APP, KEY) values for all protected entries that have been set in the storage. This prevents an attacker from erasing or adding entries to the storage.
|
||||
|
||||
## Storage format
|
||||
|
||||
Entries fall into three categories:
|
||||
|
||||
| Category | Condition | Read | Write |
|
||||
|-----------|-----------------|--------------------|--------------------|
|
||||
| Private | APP = 0 | Never | Never |
|
||||
| Protected | 1 ≤ APP ≤ 127 | Only when unlocked | Only when unlocked |
|
||||
| Public | 128 ≤ APP ≤ 255 | Always | Only when unlocked |
|
||||
|
||||
The format of public entries has remained unchanged, that is:
|
||||
|
||||
| Data | KEY | APP | LEN | DATA |
|
||||
|----------------|-----|-----|-----|------|
|
||||
| Length (bytes) | 1 | 1 | 2 | LEN |
|
||||
|
||||
Private values are used to store storage-specific information and cannot be directly accessed through the storage interface. Protected entries have the following new format:
|
||||
|
||||
| Data | KEY | APP | LEN | IV | TAG | ENCRDATA |
|
||||
|----------------|-----|-----|-----|----|-----|----------|
|
||||
| Length (bytes) | 1 | 1 | 2 | 12 | 16 | LEN - 28 |
|
||||
|
||||
The LEN value thus indicates the total length of IV, TAG and ENCRDATA.
|
||||
|
||||
The random salt (32 bits), EDEK (256 bits), ESAK (128 bits) and PVC (64 bits) is stored in a single entry under APP=0, KEY=2:
|
||||
|
||||
| Data | KEY | APP | LEN | SALT | EDEK | ESAK | PVC |
|
||||
|----------------|-----|-----|-------|------|------|------|-----|
|
||||
| Length (bytes) | 1 | 1 | 2 | 4 | 32 | 16 | 8 |
|
||||
| Value | 02 | 00 | 3C 00 | | | | |
|
||||
|
||||
The storage authentication tag (128 bits) is stored in a single entry under APP=0, KEY=5:
|
||||
|
||||
| Data | KEY | APP | LEN | TAG |
|
||||
|----------------|-----|-----|-------|-----|
|
||||
| Length (bytes) | 1 | 1 | 2 | 16 |
|
||||
| Value | 05 | 00 | 20 00 | |
|
||||
|
||||
Furthermore, if any entry is overwritten, the old entry is erased, i.e., overwritten with 0. We are also using APP=0, KEY=0 as marker that the entry is erased (this was formerly used for the PIN entry, which is not needed anymore).
|
||||
|
||||
## PIN verification and decryption of protected entries in flash storage
|
||||
|
||||
1. From the flash storage read the entry containing the random salt, EDEK and PVC.
|
||||
|
||||
2. Gather constant data from various system resources such as the ProcessorID (aka Unique device ID) and any hardware serial numbers that are available. The concatenation of this data with the random salt will be referred to as *salt*.
|
||||
3. Prompt the user to enter the PIN. Prefix the entered PIN with a "1" digit in base 10 and convert the integer to 4 bytes in little endian byte order. Then compute:
|
||||
|
||||
`PBKDF2(PRF = HMAC-SHA256, Password = pin, Salt = salt, iterations = 10000, dkLen = 352 bits)`
|
||||
|
||||
The first 256 bits of the output will be used as the key encryption key (KEK) and the remaining 96 bits will be used as the key encryption initialization vector (KEIV).
|
||||
|
||||
*Note: Since two blocks of output need to be produced in PBKDF2 the total number of iterations is 20000.*
|
||||
|
||||
4. Compute:
|
||||
|
||||
`(dek, tag) = ChaCha20Poly1305Decrypt(kek, keiv, edek)`
|
||||
|
||||
5. Compare the PVC read from the flash storage with the first 64 bits of the computed tag value. If there is a mismatch, then fail. Otherwise store the DEK in a global variable.
|
||||
|
||||
6. When a protected entry needs to be decrypted, load the IV, ENCRDATA and TAG of the entry and compute:
|
||||
|
||||
`(data, tag) = ChaCha20Poly1305Decrypt(dek, iv, (key || app), encrdata)`
|
||||
|
||||
where the APP and KEY of the entry is used as two bytes of associated data. Compare the TAG read from the flash storage with the computed tag value. If there is a mismatch, then fail.
|
||||
|
||||
![summary](docs/key-derivation.svg)
|
||||
|
||||
## Initializing the EDEK
|
||||
|
||||
1. When the storage is initialized, generate the 32 bit random salt and 256 bit DEK using a cryptographically secure random number generator.
|
||||
|
||||
2. Set a boolean value in the storage denoting that the PIN has not been set. Use an empty PIN to derive the KEK and KEIV as described above.
|
||||
|
||||
3. Encrypt the DEK using the derived KEK and KEIV:
|
||||
|
||||
`(edek, tag) = ChaCha20Poly1305Encrypt(kek, keiv, dek)`
|
||||
|
||||
4. Store the random salt, EDEK value and the first 64 bits of the tag as the PVC.
|
||||
|
||||
## Setting a new PIN
|
||||
|
||||
1. If the PIN has already been set, then prompt the user to enter the old PIN value, check the PVC and compute the DEK as described above in steps 1-4.
|
||||
|
||||
2. Generate a new 32 bit random salt and prompt the user to enter the new PIN value. Use these values to derive the new KEK and KEIV as described above.
|
||||
|
||||
3. Encrypt the DEK using the new KEK and KEIV:
|
||||
|
||||
`(edek, tag) = ChaCha20Poly1305Encrypt(kek, keiv, dek)`
|
||||
|
||||
4. Store the new EDEK value and the first 64 bits of the tag as the new PVC. This operation should be atomic, i.e. either both values should be stored or neither. Overwrite the old values of the EDEK and PVC with zeros.
|
||||
|
||||
## Encryption of protected entries in flash storage
|
||||
|
||||
Whenever the value of an entry needs to be updated, a fresh IV is generated using a cryptographically secure random number generator and the data is encrypted as `(encrdata, tag) = ChaCha20Poly1305Encrypt(dek, iv, (key || app), data)`.
|
||||
|
||||
## Storage authentication
|
||||
|
||||
The storage authentication key (SAK) will be used to generate a storage authentication tag (SAT) for the list of all (APP, KEY) values of protected entries (1 ≤ APP ≤ 127) that have been set in the storage. The SAT will be checked during every get operation. When a new protected entry is added to the storage or when a protected entry is deleted from the storage, the value of the SAT will be updated. The value of the SAT is defined as the first 16 bytes of
|
||||
|
||||
`HMAC-SHA-256(SAK, ⨁i HMAC-SHA-256(SAK, KEY_i || APP_i))`
|
||||
|
||||
where `⨁` denotes the n-ary bitwise XOR operation and KEY_i || APP_i is a two-byte encoding of the value of the *i*-th (APP, KEY) such that 1 ≤ APP ≤ 127.
|
||||
|
||||
## Design rationale
|
||||
|
||||
- The purpose of the PBKDF2 function is to thwart brute-force attacks in case the attacker is able to circumvent the PIN entry counter mechanism but does not have full access to the contents of the flash storage of the device, e.g. fault injection attacks. For an attacker that would be able to read the flash storage and obtain the salt, the PBKDF2 with 20000 iterations and a 4- to 9-digit PIN would not pose an obstacle.
|
||||
|
||||
- The reason why we use a separate data encryption key rather than using the output of PBKDF2 directly to encrypt the sensitive entries is so that when the user decides to change their PIN, only the EDEK needs to be reencrypted, but the remaining entries do not need to be updated.
|
||||
|
||||
- We se ChaCha20 for encryption, because as a stream cipher it has no padding overhead and its implementation is readily available in trezor-crypto. A possible alternative to using ChaCha20Poly1305 for DEK encryption is to use AES-CTR with HMAC in an encrypt-then-MAC scheme. A possible alternative to using ChaCha20 for encryption of other data entries is to use AES-XTS (XEX-based tweaked-codebook mode with ciphertext stealing), which was designed specifically for disk-encryption. The APP || KEY value would be used as the tweak.
|
||||
- Advantages of AES-XTS:
|
||||
- Does not require an initialization vector.
|
||||
- Ensures better diffusion than a stream cipher, which eliminates the above concerns about malleability and fault injection attacks.
|
||||
- Disadvantages of AES-XTS:
|
||||
- Not implemented in trezor-crypto.
|
||||
- Requires two keys of length at least 128 bits.
|
||||
|
||||
- A 32-bit PVC would be sufficient to verify the PIN value, since there would be less than a 1 in 4 chance that there exists a false PIN, which has the same PVC as the correct PIN. Nevertheless, we decided to go with a 64-bit PVC to achieve a larger security margin. The chance that there exists a false PIN, which has the same PVC as the correct PIN, then drops below 1 in 10^10. The existence of a false PIN does not appear to pose a security weakness, since the false PIN cannot be used to decrypt the protected entries.
|
||||
|
||||
- Instead of using separate IVs for each entry we considered using a single IV for the entire sector. Upon sector compaction a new IV would have to be generated and the encrypted data would have to be reencrypted under the new IV. A possible issue with this approach is that compaction cannot happen without the DEK, i.e. generally data could not be written to the flash storage without knowing the PIN. This property might not always be desirable.
|
||||
|
||||
## New measures for PIN entry counter protection
|
||||
|
||||
The former implementation of the PIN entry counter was vulnerable to fault injection attacks.
|
||||
|
||||
Under the former implementation the PIN counter storage entry consisted of 32 words initialized to 0xFFFFFFFF. The first non-zero word in this area was the current PIN failure counter. Before verifying the PIN the lowest bit with value 1 was set to 0, i.e. a value of FFFFFFFC indicated two PIN entries. Upon successful PIN entry, the word was set to 0x00000000, indicating that the next word was the PIN failure counter. Allegedly, by manipulating the voltage on the USB input an attacker could convince the device to read the PIN entry counter as 0xFFFFFFFF even if some of the bits had been set to 0.
|
||||
|
||||
### Design goals
|
||||
|
||||
- Make it easy to decrement the counter by changing a 1 bit to 0.
|
||||
- Make it hard to reset the counter by a fault injection, i.e. counter values should not have an overly simple binary representation like 0xFFFFFFFF.
|
||||
- If possible, use two or more different methods of checking the counter value so that an attacker has to mount different fault injection attacks to succeed.
|
||||
- Optimize the format for successful PIN entry.
|
||||
- Minimize the number of branching operations. Avoid loops, instead utilize bitwise and arithmetic operations when processing the PIN counter data.
|
||||
|
||||
### Proposal summary
|
||||
|
||||
Under the former implementation, for every unsuccessful PIN entry we discarded one bit from the counter, while for every successful PIN entry we discard an entire word. In the new implementation we optimize the counter operations for successful PIN entry.
|
||||
|
||||
The basic idea is that there are two binary logs stored in the flash storage, e.g.:
|
||||
|
||||
```
|
||||
...0001111111111111... pin_success_log
|
||||
...0000001111111111... pin_entry_log
|
||||
```
|
||||
|
||||
Before every PIN verification the highest 1-bit in the pin_entry_log is set to 0. If the verification succeeds, then the corresponding bit in the pin_success_log is also set to 0. The example above shows the status of the logs when the last three PIN entries were not successful.
|
||||
|
||||
In actual fact the logs are not written to the flash storage exactly as shown above, but they are stored in a form that should protect them against fault injection attacks. Only half of the stored bits carry information, the other half acts as "guard bits". So a stored value `...001110...` could look like `...0g0gg1g11g0g...`, where g denotes a guard bit. The positions and the values of the guard bits are determined by a guard key. The guard_key is a randomly generated uint32 value stored as an entry in the flash memory in cleartext. The assumption behind this is that an attacker attempting to reset or decrement the PIN counter by a fault injection is not able to read the flash storage. However, the value of guard_key also needs to be protected against fault injection, so the set of valid guard_key values should be limited by some condition which is easy to verify, such as guard_key mod M == C, where M and C a suitably chosen constants. The constants should be chosen so that the binary representation of any valid guard_key value has Hamming weight between 8 and 24. These conditions are discussed below.
|
||||
|
||||
### Storage format
|
||||
|
||||
The PIN log has APP = 0 and KEY = 1. The DATA part of the entry consists of 33 words (132 bytes, assuming 32-bit words):
|
||||
|
||||
- guard_key (1 word)
|
||||
- pin_success_log (16 words)
|
||||
- pin_entry_log (16 words)
|
||||
|
||||
Each log is stored in big-endian word order. The byte order of each word is platform dependent.
|
||||
|
||||
### Guard key validation
|
||||
|
||||
The guard_key is said to be valid if the following three conditions hold true:
|
||||
|
||||
1. Each byte of the binary representation of the guard_key has a balanced number of zeros and ones at the positions corresponding to the guard values (that is those bits in the mask 0xAAAAAAAA).
|
||||
2. The guard_key binary representation does not contain a run of 5 (or more) zeros or ones.
|
||||
3. The guard_key integer representation is congruent to 15 modulo 6311.
|
||||
|
||||
Key validity can be checked with this function:
|
||||
|
||||
```c
|
||||
int key_validity(uint32_t guard_key)
|
||||
{
|
||||
uint32_t count = (guard_key & 0x22222222) + ((guard_key >> 2) & 0x22222222);
|
||||
count = count + (count >> 4);
|
||||
|
||||
uint32_t zero_runs = ~guard_key;
|
||||
zero_runs = zero_runs & (zero_runs >> 2);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
zero_runs = zero_runs & (zero_runs >> 1);
|
||||
uint32_t one_runs = guard_key;
|
||||
one_runs = one_runs & (one_runs >> 2);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
one_runs = one_runs & (one_runs >> 1);
|
||||
|
||||
return ((count & 0x0e0e0e0e) == 0x04040404) & (one_runs == 0) & (zero_runs == 0) & (guard_key % 6311 == 15);
|
||||
}
|
||||
```
|
||||
|
||||
### Key generation
|
||||
|
||||
The guard_key may be generated in the following way:
|
||||
|
||||
1. Generate a random integer *r* in such that 0 ≤ *r* ≤ 680552 with uniform probability.
|
||||
2. Set *r* = *r* * 6311 + 15.
|
||||
3. If *key_validity(r)* is not true go back to the step 1.
|
||||
|
||||
Note that on average steps 1 to 3 are repeated about one hundred times.
|
||||
|
||||
### Key expansion
|
||||
|
||||
The guard_key is read from storage, its value is checked for validity and used to compute the guard_mask (indicating the positions of the guard bits) and guard value (indicating the values of the guard bits on their actual positions):
|
||||
|
||||
```c
|
||||
LOW_MASK = 0x55555555
|
||||
guard_mask = ((guard_key & LOW_MASK) << 1) |
|
||||
((~guard_key) & LOW_MASK)
|
||||
guard = (((guard_key & LOW_MASK) << 1) & guard_key) |
|
||||
(((~guard_key) & LOW_MASK) & (guard_key >> 1))
|
||||
```
|
||||
|
||||
**Explanation**:
|
||||
|
||||
The guard_key contains two pieces of information. The position of the guard bits but also their corresponding values. The bitwise format of the guard_key is `vpvpvp...vp`. The bits labelled `p` indicate the position of each guard bit and the bits labelled `v` indicate its value.
|
||||
|
||||
The guard_mask is derived from the guard_key and has the form `xyxyxy...xy` where x+y = 1 (in other words, there is exactly one 1 bit in each pair xy). First, we set the `x` bits:
|
||||
|
||||
`(guard_key & LOW_MASK) << 1`
|
||||
|
||||
and the `y` bits to its corresponding complement:
|
||||
|
||||
`(~guard_key) & LOW_MASK`
|
||||
|
||||
That ensures that only one 1 bit is present in each pair `xy`. The guard value is equal to the bits labelled `v` in the guard_key but only at the positions indicated by the guard_mask. The guard value is therefore equal to:
|
||||
|
||||
```
|
||||
-------- x bits mask --------- & -- guard_key --
|
||||
guard = (((guard_key & LOW_MASK) << 1) & guard_key) |
|
||||
----- y bits mask ---- & - guard_key shifted to v bits
|
||||
(((~guard_key) & LOW_MASK) & (guard_key >> 1))
|
||||
```
|
||||
|
||||
### Log initialization
|
||||
|
||||
Each log is stored as 16 consecutive words each initialized to:
|
||||
|
||||
`guard | ~guard_mask`
|
||||
|
||||
### Removing and adding guard bits
|
||||
|
||||
After reading a word from the flash storage we verify the format by checking the condition:
|
||||
|
||||
`(word & guard_mask) == guard`
|
||||
|
||||
and then remove the guard bits as follows:
|
||||
|
||||
```
|
||||
word = word & ~guard_mask
|
||||
word = ((word >> 1) | word ) & LOW_MASK
|
||||
word = word | (word << 1)
|
||||
```
|
||||
|
||||
This operation replaces each guard bit with the value of its neighbouring bit, e.g. `…0g0gg1g11g0g…` is converted to `…000011111100…` Thus each non-guard bit is duplicated.
|
||||
|
||||
The guard bits can be added back as follows:
|
||||
|
||||
`word = (word & ~guard_mask) | guard`
|
||||
|
||||
### Determining the number of PIN failures
|
||||
|
||||
Remove the guard bits from the words of the pin_entry_log using the operations described above and verify that the result has form 0\*1\* by checking the condition:
|
||||
|
||||
`word & (word + 1) == 0`
|
||||
|
||||
Then verify that the pin_entry_log and pin_success_log are in sync by checking the condition:
|
||||
|
||||
`pin_entry_log & pin_success_log == pin_entry_log`
|
||||
|
||||
Finally, determine the current number of PIN failures by counting the number of set bits in the evaluation of the following expression:
|
||||
|
||||
`pin_success_log xor pin_entry_log`
|
||||
|
||||
Note that the number of set bits in a word can be counted using bitwise and arithmetic operations. For a 32-bit word the following can be used:
|
||||
|
||||
```c
|
||||
count = word - ((word >> 1) & 0x55555555)
|
||||
count = (count & 0x33333333) + ((count >> 2) & 0x33333333)
|
||||
count = (count + (count >> 4)) & 0x0F0F0F0F
|
||||
count = count + (count >> 8)
|
||||
count = (count + (count >> 16)) & 0x3F
|
||||
```
|
1
storage/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../docs/storage/index.md
|
@ -1,34 +0,0 @@
|
||||
# Tests
|
||||
|
||||
## Burn tests
|
||||
|
||||
These tests are doing a simple read/write operations on the device to see if the hardware can endure high number of flash writes. Meant to be run on the device directly for a long period of time.
|
||||
|
||||
## Device tests
|
||||
|
||||
Device tests are integration tests that can be run against either emulator or on an actual device.
|
||||
You are responsible to provide either an emulator or a device with Debug mode present.
|
||||
|
||||
### Device tests
|
||||
|
||||
The original version of device tests. These tests can be run against both Model One and Model T.
|
||||
|
||||
See the [README](device_tests/README.md) for instructions how to run it.
|
||||
|
||||
### Click tests
|
||||
|
||||
Click tests are a next-generation of the Device tests. The tests are quite similar, but they are capable of imitating user's interaction with the screen.
|
||||
|
||||
## Fido tests
|
||||
|
||||
Implement U2F/FIDO2 tests.
|
||||
|
||||
## Upgrade tests
|
||||
|
||||
These tests test upgrade from one firmware version to another. They initialize an emulator on some specific version and then pass its storage to another version to see if the firmware operates as expected. They use fixtures from https://firmware.corp.sldev.cz/upgrade_tests/ which can be downloaded using the `download_emulators.sh` script.
|
||||
|
||||
See the [README](upgrade_tests/README.md) for instructions how to run it.
|
||||
|
||||
## Persistence tests
|
||||
|
||||
These tests test the Persistence mode, which is currently used in the device recovery. These tests launch the emulator themselves and they are capable of restarting or stopping it simulating user's plugging in or plugging out the device.
|
1
tests/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../docs/tests/index.md
|
@ -1,75 +0,0 @@
|
||||
# Running device tests
|
||||
|
||||
## 1. Running the full test suite
|
||||
|
||||
_Note: You need Pipenv, as mentioned in the core's [documentation](https://docs.trezor.io/trezor-firmware/core/) section._
|
||||
|
||||
In the `trezor-firmware` checkout, in the root of the monorepo, install the environment:
|
||||
|
||||
```sh
|
||||
pipenv sync
|
||||
```
|
||||
|
||||
And run the automated tests:
|
||||
|
||||
```sh
|
||||
pipenv run make -C core test_emu
|
||||
```
|
||||
|
||||
## 2. Running tests manually
|
||||
|
||||
Install the pipenv environment as outlined above. Then switch to a shell inside the
|
||||
environment:
|
||||
|
||||
```sh
|
||||
pipenv shell
|
||||
```
|
||||
|
||||
If you want to test against the emulator, run it in a separate terminal from the `core`
|
||||
subdirectory:
|
||||
```sh
|
||||
PYOPT=0 ./emu.sh
|
||||
```
|
||||
|
||||
Now you can run the test suite with `pytest` from the root directory:
|
||||
```sh
|
||||
pytest tests/device_tests
|
||||
```
|
||||
|
||||
### Useful Tips
|
||||
|
||||
The tests are randomized using the [pytest-random-order] plugin. The random seed is printed in the header of the tests output, in case you need to run the tests in the same order.
|
||||
|
||||
If you only want to run a particular test, pick it with `-k <keyword>` or `-m <marker>`:
|
||||
|
||||
```sh
|
||||
pytest -k nem # only runs tests that have "nem" in the name
|
||||
pytest -m stellar # only runs tests marked with @pytest.mark.stellar
|
||||
```
|
||||
|
||||
If you want to see debugging information and protocol dumps, run with `-v`.
|
||||
|
||||
If you would like to interact with the device (i.e. press the buttons yourself), just prefix pytest with `INTERACT=1`:
|
||||
|
||||
```sh
|
||||
INTERACT=1 pytest tests/device_tests
|
||||
```
|
||||
|
||||
## 3. Using markers
|
||||
|
||||
When you're developing a new currency, you should mark all tests that belong to that
|
||||
currency. For example, if your currency is called NewCoin, your device tests should have
|
||||
the following marker:
|
||||
|
||||
```python
|
||||
@pytest.mark.newcoin
|
||||
```
|
||||
|
||||
This marker must be registered in [REGISTERED_MARKERS] file.
|
||||
|
||||
If you wish to run a test only on TT, mark it with `@pytest.mark.skip_t1`.
|
||||
If the test should only run on T1, mark it with `@pytest.mark.skip_t2`.
|
||||
You must not use both on the same test.
|
||||
|
||||
[pytest-random-order]: https://pypi.org/project/pytest-random-order/
|
||||
[REGISTERED_MARKERS]: ../REGISTERED_MARKERS
|
1
tests/device_tests/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../../docs/tests/device-tests.md
|
@ -1,27 +0,0 @@
|
||||
# Running Upgrade Tests
|
||||
|
||||
1. As always, use pipenv environment:
|
||||
|
||||
```sh
|
||||
pipenv shell
|
||||
```
|
||||
|
||||
2. Download the emulators, if you have not already:
|
||||
|
||||
```sh
|
||||
tests/download_emulators.sh
|
||||
```
|
||||
|
||||
3. And run the tests using pytest:
|
||||
|
||||
```sh
|
||||
pytest tests/upgrade_tests
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
You can use `TREZOR_UPGRADE_TEST` environment variable if you would like to run core or legacy upgrade tests exclusively. This will run `core` only:
|
||||
|
||||
```sh
|
||||
TREZOR_UPGRADE_TEST="core" pytest tests/upgrade_tests
|
||||
```
|
1
tests/upgrade_tests/README.md
Symbolic link
@ -0,0 +1 @@
|
||||
../../docs/tests/upgrade-tests.md
|