Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
158c8a772b | ||
|
|
5af5a9ae2f | ||
|
|
12e54d379b | ||
|
|
57f9b367d5 | ||
|
|
742ac8e67e | ||
|
|
837d181612 | ||
|
|
4b82668a6a | ||
|
|
1c0fc15ca9 | ||
|
|
c11616d415 | ||
|
|
8df7b6ff3b | ||
|
|
b1a7d2b1b2 | ||
|
|
2c46a22679 | ||
|
|
04d2099724 | ||
|
|
d8dd62732c | ||
|
|
5e2e435583 | ||
|
|
6d8c1ee96a | ||
|
|
3ad4262fcf | ||
|
|
06869c5304 | ||
|
|
99230c185f | ||
|
|
5a21760e2f | ||
|
|
54192f7c50 | ||
|
|
6182bf33d5 | ||
|
|
d293d08f8d | ||
|
|
b80dd72f2d | ||
|
|
74d0a92cbc | ||
|
|
f190edb302 | ||
|
|
4fefcb6dc6 | ||
|
|
b7e345515f | ||
|
|
2cd038c431 | ||
|
|
2bd2b361a8 | ||
|
|
eef6aaf95a | ||
|
|
eb258f4b2a | ||
|
|
d341ed3f57 | ||
|
|
8c519a5bf3 | ||
|
|
a9c402cba3 | ||
|
|
b3480c3ea6 | ||
|
|
db93d769d7 | ||
|
|
408b9d0ef6 | ||
|
|
2f70733007 | ||
|
|
b78b30f0b1 | ||
|
|
05bd7ab39c | ||
|
|
5de8c9672b | ||
|
|
84dda56746 | ||
|
|
336bfbfe08 | ||
|
|
5d69d5c039 | ||
|
|
545b23f1b4 | ||
|
|
ea92a049af | ||
|
|
aa4580adbe | ||
|
|
80106f300e | ||
|
|
422a3a2834 | ||
|
|
ddae5020df | ||
|
|
478c421627 | ||
|
|
ee38dbb28e | ||
|
|
93b3bea43f | ||
|
|
2388efa8d7 | ||
|
|
0c68d1a4f4 | ||
|
|
e6cb8e9e8a | ||
|
|
c2e76b3132 | ||
|
|
202c2cae16 | ||
|
|
8b7c91c949 | ||
|
|
6e134a2c9c | ||
|
|
0dda18b04c | ||
|
|
6a53f3524e | ||
|
|
e6dda17124 | ||
|
|
280aa07d4d | ||
|
|
91197bc90f | ||
|
|
c7936da8b2 | ||
|
|
976772b7be | ||
|
|
3bf5dc319c | ||
|
|
4416c531e9 | ||
|
|
0f43069e6c | ||
|
|
28c0fb4516 | ||
|
|
73fa603200 | ||
|
|
cfe86f5363 | ||
|
|
394edaadbc | ||
|
|
b57cf3b629 | ||
|
|
ccc0ac039c | ||
|
|
fdbfc85fd4 | ||
|
|
35c637804b | ||
|
|
e3212ed1a8 | ||
|
|
7bf89568e6 | ||
|
|
b34a514006 | ||
|
|
731cd11841 | ||
|
|
a293c05a4e | ||
|
|
4414783e1c | ||
|
|
abb1d5c2c3 | ||
|
|
75d30c4229 | ||
|
|
8d810b107f | ||
|
|
3f5e9789b6 | ||
|
|
56a755c6ec | ||
|
|
e1cdafb272 | ||
|
|
ea37bc0b69 | ||
|
|
4247b6427a | ||
|
|
22fd6b4010 | ||
|
|
342af8f78e | ||
|
|
615c0a5064 | ||
|
|
bc18f3dd79 | ||
|
|
931d26962c | ||
|
|
99fc4d3d23 | ||
|
|
c6d6adcf4e | ||
|
|
ecb4804c2f | ||
|
|
4e552fc6c3 | ||
|
|
503641d8bf | ||
|
|
e0615a13f0 | ||
|
|
fa05c28501 | ||
|
|
760ef7f168 | ||
|
|
0f76b4c74a | ||
|
|
c89a8deef7 | ||
|
|
804ec34bd3 | ||
|
|
971f602f7d | ||
|
|
3120267464 | ||
|
|
f1059e0972 | ||
|
|
0c4175aa33 | ||
|
|
56bf11bd3e | ||
|
|
6aa707074c | ||
|
|
c2d0a7f26f | ||
|
|
20b14b7da0 | ||
|
|
91f7aa9b2b | ||
|
|
5de1d72f9f | ||
|
|
74c6dcc7f3 | ||
|
|
3f2ce08f2b | ||
|
|
ba6fd7025e | ||
|
|
0ac7c433e1 | ||
|
|
33753cfebb | ||
|
|
7a6442f96b | ||
|
|
dd3f79dc80 | ||
|
|
14350452f0 | ||
|
|
7a21a37fb9 | ||
|
|
7193e284b2 | ||
|
|
336f5f6e04 | ||
|
|
23ae4cb317 | ||
|
|
9b2019c466 | ||
|
|
1aeb74ada5 | ||
|
|
b35e04be39 | ||
|
|
e3c5b32419 | ||
|
|
2623e1cc88 | ||
|
|
c43cc7130f | ||
|
|
5ea5d3f793 | ||
|
|
6fd2cb86e9 | ||
|
|
594690d187 | ||
|
|
3cd47cdc5c | ||
|
|
15005d307e | ||
|
|
4b1a13a199 | ||
|
|
df4819f0b8 | ||
|
|
950589215b | ||
|
|
9411b4bb96 | ||
|
|
f5d5ee0620 | ||
|
|
2c44856a84 | ||
|
|
487cc78635 | ||
|
|
e0b0b1f51f | ||
|
|
e2876dcc94 | ||
|
|
5023cd4517 | ||
|
|
9401bcd31a | ||
|
|
6f4b4f6363 | ||
|
|
7d855ebe5f | ||
|
|
47d1aac44e | ||
|
|
7e4ecbf9b0 | ||
|
|
7fcfd55d3e | ||
|
|
0f18c1fc59 | ||
|
|
df39a9166a | ||
|
|
321ab3d836 | ||
|
|
95e943a32b | ||
|
|
448fae8bdd | ||
|
|
91c579fadd | ||
|
|
aa7d17bf8f | ||
|
|
b05bd09998 | ||
|
|
a982c86d91 | ||
|
|
25403b017c | ||
|
|
70aa6de8b9 | ||
|
|
824bbbc3b2 | ||
|
|
ebc1832b91 | ||
|
|
306f4d31b8 | ||
|
|
0883d6f347 | ||
|
|
8d7f6dee7a | ||
|
|
79e820fea1 | ||
|
|
0a506ecec6 | ||
|
|
0da4088c01 | ||
|
|
c7762eedf2 | ||
|
|
2749bc00fb |
2
.gitignore
vendored
@ -25,4 +25,4 @@ __pycache__/
|
||||
.tags
|
||||
pp
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
6
.gitmodules
vendored
@ -14,9 +14,3 @@
|
||||
[submodule "stm32/mk4-bootloader/hal"]
|
||||
path = stm32/mk4-bootloader/hal
|
||||
url = https://github.com/STMicroelectronics/STM32CubeL4.git
|
||||
[submodule "misc/gpu/external/stm32c0xx_hal_driver"]
|
||||
path = misc/gpu/external/stm32c0xx_hal_driver
|
||||
url = https://github.com/STMicroelectronics/stm32c0xx_hal_driver.git
|
||||
[submodule "misc/gpu/external/cmsis_device_c0"]
|
||||
path = misc/gpu/external/cmsis_device_c0
|
||||
url = https://github.com/STMicroelectronics/cmsis_device_c0.git
|
||||
|
||||
56
README.md
@ -1,4 +1,4 @@
|
||||
# COLDCARD Hardware Wallet
|
||||
# Coldcard Wallet
|
||||
|
||||
Coldcard is an Affordable, Ultra-secure & Verifiable Hardware Wallet for Bitcoin.
|
||||
Get yours at [Coldcard.com](http://coldcard.com)
|
||||
@ -8,13 +8,7 @@ with the latest updates and security alerts.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Quick Links
|
||||
|
||||
- [Latest firmware changes and updates](releases/ChangeLog.md)
|
||||
- [PGP signature file](releases/signatures.txt)
|
||||
- [Firmware binaries](https://coldcard.com/downloads)
|
||||

|
||||
|
||||
## Reproducible Builds
|
||||
|
||||
@ -24,23 +18,17 @@ has been automated using Docker. Steps are as follows:
|
||||
|
||||
1. Install [Docker](https://www.docker.com) and start it.
|
||||
2. Install [make (GNUMake)](https://www.gnu.org/software/make/) if you don't already have it.
|
||||
3. Checkout a specific version of the code, and start the process.
|
||||
3. Checkout the code, and start the process.
|
||||
|
||||
```shell
|
||||
git clone https://github.com/Coldcard/firmware.git
|
||||
cd firmware
|
||||
# DOWNLOAD https://coldcard.com/downloads
|
||||
# get a copy of binary into ./releases/2026-03-05T2052-v5.5.0-mk-coldcard.dfu
|
||||
git checkout 2026-03-05T2052-v5.5.0
|
||||
cd stm32
|
||||
make -f MK4-Makefile repro
|
||||
cd firmware/stm32
|
||||
make repro
|
||||
```
|
||||
|
||||
4. At the end of the process a clear confirmation message is shown, or the differences.
|
||||
5. Build products can be found `firmware/stm32/built`.
|
||||
6. If you do not trust the results of `make repro` refer to `docs/notes-on-repro.md`
|
||||
which breaks down the process.
|
||||
7. Process for Q firmware is the same, but change `MK4-Makefile` in last step to `Q1-Makefile`
|
||||
6. If you do not trust the results of `make repro` refer to `docs/notes-on-repro.md` which breaks down the process.
|
||||
|
||||
## Long-Lived Branches
|
||||
|
||||
@ -51,18 +39,13 @@ such as Taproot or Miniscript. Our standards for releasing new Edge
|
||||
versions are lower, so we can iterate faster and get these advancements
|
||||
out to other developers.
|
||||
|
||||
Q and Mk series share the same code base. Individual files that are added,
|
||||
or removed, can be see in differences between `shared/manifest_mk4.py`
|
||||
and `shared/manifest_q1.py`. Common files are in `shared/manifest.py`.
|
||||
Firmware built for Mk5, supports the Mk4 without any functional differences.
|
||||
|
||||
|
||||
## Check-out and Setup
|
||||
|
||||
**NOTE** This is the `master` branch and covers the latest hardware (Mk and Q).
|
||||
**NOTE** This is the `master` branch and covers the latest hardware (Mk4).
|
||||
See branch `v4-legacy` for firmware which supports only Mk3/Mk2 and earlier.
|
||||
|
||||
Do a checkout, recursively, to get all the submodules:
|
||||
Do a checkout, recursively to get all the submodules:
|
||||
|
||||
```shell
|
||||
git clone --recursive https://github.com/Coldcard/firmware.git
|
||||
@ -85,7 +68,7 @@ git submodule update --init --recursive
|
||||
```
|
||||
|
||||
Do not use a path with any spaces in it. The Makefiles do not handle
|
||||
that well and we're not planning to fix it.
|
||||
that well, and we're not planning to fix it.
|
||||
|
||||
Keep in mind that python requirements may change between versions,
|
||||
so at the top level, do this command:
|
||||
@ -186,16 +169,7 @@ git clone --recursive https://github.com/Coldcard/firmware.git
|
||||
cd firmware
|
||||
|
||||
# Apply address patch
|
||||
# if unix/linux_addr.patch exists use below command
|
||||
# not needed in current revision
|
||||
# git apply unix/linux_addr.patch
|
||||
|
||||
# * below is needed for ubuntu 24.04
|
||||
pushd external/micropython
|
||||
git apply ../../ubuntu24_mpy.patch
|
||||
popd
|
||||
# *
|
||||
|
||||
git apply unix/linux_addr.patch
|
||||
|
||||
# Create Python virtual environment and activate it
|
||||
python3 -m venv ENV # or virtualenv -p python3 ENV
|
||||
@ -235,8 +209,8 @@ Top-level dirs:
|
||||
|
||||
- shared code between desktop test version and real-deal
|
||||
- expected to be largely in python, and higher-level
|
||||
- code exclusive to the Mk4 or Mk5 will be listed in `manifest_mk4.py`, and
|
||||
to the Q will be listed in `manifest_q1.py`
|
||||
- new code found only on the Mk4 will be listed in `manifest_mk4.py` code exclusive
|
||||
to earlier hardware is in `manifest_mk3.py`
|
||||
|
||||
`unix`
|
||||
|
||||
@ -266,14 +240,13 @@ Top-level dirs:
|
||||
- however, you can inspect what code is on your coldcard and compare to this.
|
||||
|
||||
`stm32/mk4-bootloader`
|
||||
`stm32/q1-bootloader`
|
||||
|
||||
- 128k of factory-set code that you cannot change
|
||||
- 128k of factory-set code that you cannot change for Mk4
|
||||
- however, you can inspect what code is on your coldcard and compare to this.
|
||||
|
||||
`hardware`
|
||||
|
||||
- schematic and bill of materials for the Coldcard, all versions.
|
||||
- schematic and bill of materials for the Coldcard
|
||||
|
||||
`unix/work/...`
|
||||
|
||||
@ -286,4 +259,3 @@ Top-level dirs:
|
||||
## Support
|
||||
|
||||
Found a bug? Email: support@coinkite.com
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
../stm32/sigheader.py
|
||||
../stm32/bootloader/sigheader.py
|
||||
@ -208,9 +208,7 @@ def readback(fname):
|
||||
if v & MK_2_OK: d.append('Mk2')
|
||||
if v & MK_3_OK: d.append('Mk3')
|
||||
if v & MK_4_OK: d.append('Mk4')
|
||||
if v & MK_5_OK: d.append('Mk5')
|
||||
if v & MK_Q1_OK: d.append('Q1')
|
||||
if v & ~(MK_1_OK | MK_2_OK | MK_3_OK | MK_4_OK | MK_5_OK | MK_Q1_OK):
|
||||
if v & ~(MK_1_OK | MK_2_OK | MK_3_OK | MK_4_OK):
|
||||
d.append('?other?')
|
||||
v = nv + '+'.join(d)
|
||||
elif fld == 'timestamp':
|
||||
@ -246,7 +244,7 @@ def readback(fname):
|
||||
@click.option('--pubkey-num', '-k', type=int, help='Which key # to use for signing', default=0)
|
||||
@click.option('--high_water', '-h', is_flag=True, help='Mark version as new highwater mark (no downgrades below this version)')
|
||||
@click.option('--verbose', '-v', default=False, is_flag=True, help='Show numbers related to signature')
|
||||
@click.option('--hw-compat', '-m', type=str, metavar='mk', help="Set HW compat field (hw_label value)")
|
||||
@click.option('--hw-compat', '-m', type=int, metavar='BITMASK', help="Set HW compat field (mk number)")
|
||||
@click.option('--backdate', type=int, metavar='DAYS',
|
||||
help='Make downgrade attack test version', default=0)
|
||||
@click.option('--build_dir', '-b', default='l-port/build-COLDCARD')
|
||||
@ -279,12 +277,9 @@ def doit(keydir, outfn=None, build_dir=None, high_water=False,
|
||||
vectors = open(build_dir + '/firmware0.bin', 'rb').read()
|
||||
body = open(build_dir + '/firmware1.bin', 'rb').read()
|
||||
|
||||
if hw_compat in { 'mk4', '4', 'mk5', '5', 'mk' }:
|
||||
# Mk4 and 5 can run the same firmware, once Mk5 support was added
|
||||
hw_compat = MK_4_OK | MK_5_OK
|
||||
elif hw_compat == 'q1':
|
||||
hw_compat = MK_Q1_OK
|
||||
elif hw_compat in { 'mk3', '3'}:
|
||||
if hw_compat == 4:
|
||||
hw_compat = MK_4_OK
|
||||
elif hw_compat in {3, None}:
|
||||
hw_compat = MK_2_OK | MK_3_OK
|
||||
else:
|
||||
assert not "known"
|
||||
@ -321,15 +316,14 @@ def doit(keydir, outfn=None, build_dir=None, high_water=False,
|
||||
pubkey_num=pubkey_num,
|
||||
timestamp=timestamp(backdate) )
|
||||
|
||||
assert FW_MIN_LENGTH <= hdr.firmware_length <= FW_MAX_LENGTH, hdr.firmware_length
|
||||
|
||||
if hw_compat & MK_3_OK:
|
||||
# actual file length limited by size of SPI flash area reserved to txn data/uploads
|
||||
assert FW_MIN_LENGTH <= hdr.firmware_length <= FW_MAX_LENGTH, hdr.firmware_length
|
||||
USB_MAX_LEN = (786432-128)
|
||||
else:
|
||||
# new value for Mk4 and later: limited only by final binary size, not SPI flash
|
||||
assert FW_MIN_LENGTH <= hdr.firmware_length <= FW_MAX_LENGTH_MK4, hdr.firmware_length
|
||||
if hw_compat & MK_4_OK:
|
||||
# new value for Mk4: limited only by final binary size, not SPI flash
|
||||
USB_MAX_LEN = 1472 * 1024
|
||||
else:
|
||||
# actual file length limited by size of SPI flash area reserved to txn data/uploads
|
||||
USB_MAX_LEN = (786432-128)
|
||||
|
||||
assert hdr.firmware_length <= USB_MAX_LEN, \
|
||||
"too big for our USB upgrades: %d = %d bytes too big" % (
|
||||
|
||||
@ -3,32 +3,14 @@
|
||||
These docs are meant for you hackers out there... but also for anyone who
|
||||
wants to understand why it's safe to put your moneys into Coldcard.
|
||||
|
||||
- [`security-model.md`](security-model.md) The COLDCARD Mk4/Mk5/Q security model.
|
||||
- [`pin-entry.md`](pin-entry.md) Huge and detailed discussion of PIN codes and the security element that holds the secrets.
|
||||
- [`secure-elements.md`](secure-elements.md) How the dual secure elements work together.
|
||||
- [`dev-access.md`](dev-access.md) How developers can modify Coldcard to extend it.
|
||||
- [`memory-map.md`](memory-map.md) Memory map highlights
|
||||
- [`notes-on-repro.md`](notes-on-repro.md) Detailed breakdown of the reproducible build process.
|
||||
- [`upgrade-recovery.md`](upgrade-recovery.md) Firmware upgrade and recovery process.
|
||||
- [`backup-files.md`](backup-files.md) Some details of our encrypted backup files.
|
||||
- [`temporary-seeds.md`](temporary-seeds.md) Temporary (ephemeral) seeds and the Seed Vault.
|
||||
- [`seed-xor.md`](seed-xor.md) More about _Seed XOR_ feature, including fully worked Seed XOR example, and useful XOR lookup chart.
|
||||
- [`key-teleport.md`](key-teleport.md) Key Teleport: encrypted transfer of seeds and secrets between Q devices.
|
||||
- [`spending-policy.md`](spending-policy.md) Spending policy: autonomous signing with configurable limits.
|
||||
- [`microsd-2fa.md`](microsd-2fa.md) Using a MicroSD card as a second factor for login.
|
||||
- [`web2fa.md`](web2fa.md) Web 2FA authentication.
|
||||
- [`bip85-passwords.md`](bip85-passwords.md) Deriving deterministic passwords via BIP-85.
|
||||
- [`msg-signing.md`](msg-signing.md) COLDCARD message signing.
|
||||
- [`proof-of-reserves-bip-322.md`](proof-of-reserves-bip-322.md) BIP-322 generic signed message format and proof of reserves.
|
||||
- [`generic-wallet-export.md`](generic-wallet-export.md) Generic JSON wallet export file format.
|
||||
- [`bip-21-extensions.md`](bip-21-extensions.md) Coldcard's BIP-21 URI extensions, including multisig ownership address check.
|
||||
- [`nfc-coldcard.md`](nfc-coldcard.md) NFC support on Coldcard Mk4 and Q.
|
||||
- [`nfc-pushtx.md`](nfc-pushtx.md) NFC Push Transaction: broadcast a signed transaction via your phone.
|
||||
- [`usb-batteries.md`](usb-batteries.md) Using USB battery packs with Coldcard.
|
||||
- [`electrum-usage.md`](electrum-usage.md) Importing seed words into Electrum for funds usage (and other tips).
|
||||
- [`bitcoin-core-usage.md`](bitcoin-core-usage.md) How to use with Bitcoin Core.
|
||||
- [`bitcoin-core2of2desc.md`](bitcoin-core2of2desc.md) Airgapped 2-of-2 multisig with Bitcoin Core using descriptors.
|
||||
- [`limitations.md`](limitations.md) Documented limitations, policy choices, and TODO items.
|
||||
- [`paperwallet.pdf`](paperwallet.pdf) Example paper wallet template file.
|
||||
- [`seed-xor.md`](seed-xor.md) More about _Seed XOR_ feature, including fully worked Seed XOR example, and useful XOR lookup chart.
|
||||
- [`menu-tree.txt`](menu-tree.txt) Dump of the menu system. Incomplete, may be out of date.
|
||||
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
## Multisig Ownership address check: "wallet"
|
||||
|
||||
If the name of the multisig wallet related to an address is provided, address search
|
||||
can be greatly accelerated. Just provide `wallet=name` parameter in a standard
|
||||
[BIP-21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) URL
|
||||
shown in QR code or NFC record. If omitted, search will continue across
|
||||
all multisig wallets known by COLDCARD.
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
tb1q4d67p7stxml3kdudrgkg5mgaxsrgzcqzjrrj4gg62nxtvnsnvqjsxjkej0?wallet=goldmine
|
||||
|
||||
bitcoin:mtHSVByP9EYZmB26jASDdPVm19gvpecb5R?label=coldcard_purchase&amount=50&wallet=Haystack%20Four
|
||||
```
|
||||
@ -5,13 +5,13 @@ according to [BIP-85 PWD BASE64](https://github.com/bitcoin/bips/blob/master/bip
|
||||
Generated passwords can be sent as keystrokes via USB to the host computer,
|
||||
effectively using Coldcard as specialized password manager.
|
||||
|
||||
In addition to deriving up to 10,000 distinct secure passwords, the Coldcard
|
||||
In addition to deriving up to 10,000 distinct secure passwords, the Coldcard Mk4
|
||||
can also type them into a computer by emulating a USB keyboard, and simulating the
|
||||
keystrokes needed to type the password.
|
||||
|
||||
#### Requirements
|
||||
|
||||
* Coldcard Mk4 or Mk5 (firmware 5.0.5 or newer), or any Q
|
||||
* Coldcard Mk4 with version 5.0.5 or newer
|
||||
* USB-C with data link (won't work with power only cable from Coinkite)
|
||||
|
||||
## Type Passwords over USB
|
||||
@ -32,13 +32,11 @@ to exit. Exiting from "Type Passwords" will cause Coldcard to turn off keyboard
|
||||
1. Go to Advanced/Tools -> Derive Seed B85 -> Passwords
|
||||
2. Choose "Password/Index number" (BIP-85 index) and press OK to generate password.
|
||||
3. Screen shows generated password, path, and entropy from which password was derived
|
||||
4. A few different options are available at this point (on Mk; on Q the NFC and
|
||||
QR buttons are used instead of (3)/(4)):
|
||||
1. press (1) to save password backup file on MicroSD card (cleartext!)
|
||||
2. press (2) to save to Virtual Disk (only when available)
|
||||
3. press (3) to send over NFC (only appears when NFC is enabled)
|
||||
4. press (4) to view password as QR code
|
||||
5. press (6) to send keystrokes over USB (this enables keyboard emulation, sends keystrokes + enter, then disables keyboard emulation)
|
||||
4. A few different options are available at this point:
|
||||
1. press 1 to save password backup file on MicroSD card (cleartext!)
|
||||
2. press 2 to send keystrokes (this will first of all enable keyboard emulation, then send keystrokes + enter, and finally disables keyboard emulation)
|
||||
3. press 3 to view password as QR code
|
||||
4. press 4 to send over NFC (only appears when NFC is enabled)
|
||||
|
||||
## Keyboard language settings
|
||||
|
||||
|
||||
@ -5,12 +5,9 @@ wallet systems, but we also have a file format for general purpose
|
||||
exports, which we hope future wallet makers will leverage.
|
||||
|
||||
It contains master XPUB, XFP for that, and derived values for the top hardened
|
||||
position of the single-signature schemes BIP44, BIP49 and BIP84, plus the
|
||||
multisig schemes BIP48 (`bip48_1` = `.../1h` P2SH-P2WSH and `bip48_2` = `.../2h` P2WSH).
|
||||
When the account number is zero, a BIP45 (`m/45h`) multisig section is also included
|
||||
(it is omitted for non-zero accounts, as in the example below).
|
||||
position of BIP44, BIP84 and BIP49.
|
||||
|
||||
The feature can be found here: _Advanced/Tools > Export Wallet > Generic JSON_
|
||||
The feature can be found here: _Advanced > MicroSD > Export Wallet > Generic JSON_
|
||||
|
||||
Please contact us (or better yet, make a pull request), if you need something
|
||||
more in this file.
|
||||
@ -21,51 +18,32 @@ Here is an example, produced by the Simulator for account number 123.
|
||||
|
||||
```javascript
|
||||
{
|
||||
"chain": "BTC",
|
||||
"chain": "XTN",
|
||||
"xfp": "0F056943",
|
||||
"xpub": "tpubD6NzVbkrYhZ4XzL5Dhayo67Gorv1YMS7j8pRUvVMd5odC2LBPLAygka9p7748JtSq82FNGPppFEz5xxZUdasBRCqJqXvUHq6xpnsMcYJzeh",
|
||||
"account": 123,
|
||||
"xpub": "xpub661MyMwAqRbcGC9DmWbtbAmuUjpMYxw4BWE88NSDHB3jSjfUK7KtYJuKa52GbowD3DVLkgsxH9QwPnTx5mjdHykYFEncnmAsNsCTbWzBhA7",
|
||||
"bip44": {
|
||||
"deriv": "m/44'/1'/123'",
|
||||
"first": "n44vs1Rv7T8SANrg2PFGQhzVkhr5Q6jMMD",
|
||||
"name": "p2pkh",
|
||||
"xfp": "5F898064",
|
||||
"deriv": "m/44h/0h/123h",
|
||||
"xpub": "xpub6DStQXfAgHuLbMpCf86ruVkF4yT9pSLyWsFiqQTWY9osuinq8Dyee4W5jCjMfyku5LNkRB9oFinrY5ufn9XXEn8Vvzc2jnifKMaQCNV7RBZ",
|
||||
"desc": "pkh([0f056943/44h/0h/123h]xpub6DStQXfAgHuLbMpCf86ruVkF4yT9pSLyWsFiqQTWY9osuinq8Dyee4W5jCjMfyku5LNkRB9oFinrY5ufn9XXEn8Vvzc2jnifKMaQCNV7RBZ/<0;1>/*)#4tl8jryn",
|
||||
"first": "1GTNtzG5xX2UhdD5e3Nu7i1WPxFdjxQMJt"
|
||||
"xfp": "B7908B26",
|
||||
"xpub": "tpubDCiHGUNYdRRGoSH22j8YnruUKgguCK1CC2NFQUf9PApeZh8ewAJJWGMUrhggDNK73iCTanWXv1RN5FYemUH8UrVUBjqDb8WF2VoKmDh9UTo"
|
||||
},
|
||||
"bip49": {
|
||||
"name": "p2sh-p2wpkh",
|
||||
"xfp": "A748B1FC",
|
||||
"deriv": "m/49h/0h/123h",
|
||||
"xpub": "xpub6DDm8WzH5a9qjKkttzqSB3uGofNohU9D3n3UG8WMxkUZzJEMPTYiQRf1dvTFCQR82MjGW4LUMVuTtnW4hF17RpzCqVwhf6Z2fnJPWtjG164",
|
||||
"desc": "sh(wpkh([0f056943/49h/0h/123h]xpub6DDm8WzH5a9qjKkttzqSB3uGofNohU9D3n3UG8WMxkUZzJEMPTYiQRf1dvTFCQR82MjGW4LUMVuTtnW4hF17RpzCqVwhf6Z2fnJPWtjG164/<0;1>/*))#5j7t2n2u",
|
||||
"_pub": "ypub6Y42SBfCEFhKacx1jMd4P8zmydXFe68hxtZh3XQFLkrT3Q3ae7iH2VK9f8QqCK53Rzr5FXw2pAG1n57dQwR8E4fohqe8F1NWwWN2uVRfBry",
|
||||
"first": "3CeBRbJKCpg7BpJME2vM8ZxhCjBnhG4toy"
|
||||
"_pub": "upub5DMRSsh6mNak9KbcVjJ7xAgHJvbE3Nx22CBTier5C35kv8j7g2q58ywxskBe6JCcAE2VH86CE2aL4MifJyKbRw8Gj9ay7SWvUBkp2DJ7y52",
|
||||
"deriv": "m/49'/1'/123'",
|
||||
"first": "2N87V39riUUCd4vmXfDjMWAu9gUCiBji5jB",
|
||||
"name": "p2wpkh-p2sh",
|
||||
"xfp": "CEE1D809",
|
||||
"xpub": "tpubDCDqt7XXvhAdy1MpSze5nMJA9x8DrdRaKALRRPasfxyHpiqWWEAr9cbDBQ9BcX7cB3up98Pk97U2QQ3xrvQsi5dNPmRYYhdcsKY9wwEY87T"
|
||||
},
|
||||
"bip84": {
|
||||
"_pub": "vpub5Y5a91QvDT45EnXQaKeuvJupVvX8f9BiywDcadSTtaeJ1VgJPPXMitnYsqd9k7GnEqh44FKJ5McJfu6KrihFXhAmvSWgm7BAVVK8Gupu4fL",
|
||||
"deriv": "m/84'/1'/123'",
|
||||
"first": "tb1qc58ys2dphtphg6yuugdf3d0kufmk0tye044g3l",
|
||||
"name": "p2wpkh",
|
||||
"xfp": "2C5207AA",
|
||||
"deriv": "m/84h/0h/123h",
|
||||
"xpub": "xpub6CaWStGvcXqSW9BzU2vpCoP7aWjz9VfR5DS2nuYWVvKV2nug2dESg3HdFsaWHeoZaxuAhNcPB3TH2gq8MugS3JX1yGuhB4QbC2BneaYqB16",
|
||||
"desc": "wpkh([0f056943/84h/0h/123h]xpub6CaWStGvcXqSW9BzU2vpCoP7aWjz9VfR5DS2nuYWVvKV2nug2dESg3HdFsaWHeoZaxuAhNcPB3TH2gq8MugS3JX1yGuhB4QbC2BneaYqB16/<0;1>/*)#yk84tprf",
|
||||
"_pub": "zpub6rF34DckutvQCjaE8kW4cya7vT2t2jeQuSUUMhLHFw5F8zY8XwZZvAbuJHVgHU7QQF8nCKoW6NANoG4FoJWTdmtDhxJYLt3ZjUK5RqUSMdF",
|
||||
"first": "bc1qhj6avwmp5lhpgqwm6dgxrf3v5lf67rjm99a8an"
|
||||
},
|
||||
"bip48_1": {
|
||||
"name": "p2sh-p2wsh",
|
||||
"xfp": "845A3542",
|
||||
"deriv": "m/48h/0h/123h/1h",
|
||||
"xpub": "xpub6EkcQSTygvxVnBP2X2fM6HY5D7wv46tWbBc54ADaypuCr47vQh1GPdPAZFdx81ou5Rp4vBnzeJT5MDWDZstzijxkHfrofXRycpt1ASfg1La",
|
||||
"desc": "sh(wsh(sortedmulti(M,[0f056943/48h/0h/123h/1h]xpub6EkcQSTygvxVnBP2X2fM6HY5D7wv46tWbBc54ADaypuCr47vQh1GPdPAZFdx81ou5Rp4vBnzeJT5MDWDZstzijxkHfrofXRycpt1ASfg1La/0/*,...)))",
|
||||
"_pub": "Ypub6kUxqLsLQa4M43jXJ3ux8SyP6t8dD5ZbpZmxkpP1jc7VXLW4RkZ76ouEPAZ1gMgiiXzrYFPfzBC8MfjYaoTxfTm1zUfdeqiTnHDX8raCfeg"
|
||||
},
|
||||
"bip48_2": {
|
||||
"name": "p2wsh",
|
||||
"xfp": "2A01C6B0",
|
||||
"deriv": "m/48h/0h/123h/2h",
|
||||
"xpub": "xpub6EkcQSTygvxVneXmk3ywiS2PFhBdiPxeMxYf6RFxHCHH36NxdcN7DjUpudCppAAxs58CG6DQLjtqZNmyC3MpgVob6wpdeATjpZZ1woX92EF",
|
||||
"desc": "wsh(sortedmulti(M,[0f056943/48h/0h/123h/2h]xpub6EkcQSTygvxVneXmk3ywiS2PFhBdiPxeMxYf6RFxHCHH36NxdcN7DjUpudCppAAxs58CG6DQLjtqZNmyC3MpgVob6wpdeATjpZZ1woX92EF/0/*,...))",
|
||||
"_pub": "Zpub75KE91YFZFbpup5PMS2AxgZCKRWnozdEWTEmaUKGQysSmUaKuL5WYyf2kk5UNQhhupRnddQe9GzST7crvfLoRTHTg6KtDPZiFjxBJobzcUz"
|
||||
"xfp": "78CF94E5",
|
||||
"xpub": "tpubDC7jGaaSE66VDB6VhEDFYQSCAyugXmfnMnrMVyHNzW9wryyTxvha7TmfAHd7GRXrr2TaAn2HXn9T8ep4gyNX1bzGiieqcTUNcu2poyntrET"
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -73,23 +51,16 @@ Here is an example, produced by the Simulator for account number 123.
|
||||
## Notes
|
||||
|
||||
1. The `first` address is formed by added `/0/0` onto the given derivation, and is assumed
|
||||
to be the first (non-change) receive address for the wallet. It is only present on the
|
||||
single-signature sections (`bip44`, `bip49`, `bip84`); multisig sections omit it.
|
||||
|
||||
1a. Each section includes a `desc` field: a ready-to-import Bitcoin output descriptor
|
||||
(with `#checksum`). Single-sig descriptors use the `<0;1>/*` multipath form. Multisig
|
||||
sections (`bip48_1`, `bip48_2`, and `bip45` when present) emit a `sortedmulti(...)`
|
||||
template with `M` and a trailing `...` as placeholders, to be completed with your
|
||||
threshold and the other co-signers' keys.
|
||||
to be the first (non-change) receive address for the wallet.
|
||||
|
||||
2. The user may specify any value (up to 9999) for the account number, and it's meant to
|
||||
segregate funds into sub-wallets. Don't assume it's zero.
|
||||
|
||||
3. When making your PSBT files to spend these amounts, remember that the XFP of the master
|
||||
(`0F056943` in this example) is the root of the subkey paths found in the file, and
|
||||
(`0F056943` in this example) is is the root of the subkey paths found in the file, and
|
||||
you must include the full derivation path from master. So based on this example,
|
||||
to spend a UTXO on `bc1qhj6avwmp5lhpgqwm6dgxrf3v5lf67rjm99a8an`, the input section
|
||||
of your PSBT would need to specify `(m=0F056943)/84'/0'/123'/0/0`.
|
||||
to spend a UTXO on `tb1qc58ys2dphtphg6yuugdf3d0kufmk0tye044g3l`, the input section
|
||||
of your PSBT would need to specify `(m=0F056943)/84'/1'/123'/0/0`.
|
||||
|
||||
4. The `_pub` value is the [SLIP-132](https://github.com/satoshilabs/slips/blob/master/slip-0132.md) style "ypub/zpub/etc" which some systems might want. It implies
|
||||
a specific address format.
|
||||
|
||||
@ -1,224 +0,0 @@
|
||||
|
||||
# Key Teleport
|
||||
|
||||
Purpose: Send a small quantity of very secret data between two COLDCARD Q systems, with
|
||||
no risk of anything in the middle learning the secret.
|
||||
|
||||
Method: ECDH and AES-256-CTR plus an extra wrapping layer, transmitted over a mixture of
|
||||
NFC, passive websites, and QR/BBQr codes.
|
||||
|
||||
# Protocol Overview
|
||||
|
||||
## Steps
|
||||
|
||||
- Receiver picks an EC keypair, stores it in settings, and publishes the pubkey via a QR/NFC
|
||||
- The pubkey is encrypted by a short 8-digit numeric code, which should be
|
||||
sent by a different channel.
|
||||
- Sender gets QR and numeric code, picks own keypair, and does ECDH to arrive at a
|
||||
shared session key
|
||||
- Sender picks a human-readable secret which is independent of anything else (P key)
|
||||
- The secret data (perhaps a seed phrase, XPRV, secure note, full backup, etc) is
|
||||
AES-256-CTR encrypted with P key, then encrypted + MAC added with session key
|
||||
- Data packet is sent to receiver (via BBQr), who can reconstruct the session key via ECDH
|
||||
- Prompt user for the P key to finish decoding
|
||||
- Decoded secret value is saved to Seed Vault or secure notes as appropriate
|
||||
- Receiver destroys EC keypair used in transfer
|
||||
|
||||
### When used for PSBT Multisig
|
||||
|
||||
- No action required on receiver
|
||||
- Sender uses the pubkey derived from pre-shared XPUB involved in the multisig wallet.
|
||||
- Same steps, but drops immediately into signing process when decoded correctly
|
||||
|
||||
## Notes and Limitations
|
||||
|
||||
- max 4k (after encoding) of data is possible due to HTTP limitations
|
||||
- all transfers are "data typed" and decode only on COLDCARD
|
||||
- Q model is required due to the use of QR codes to ultimately get data into the COLDCARD
|
||||
|
||||
|
||||
# Details
|
||||
|
||||
## Data Type Codes
|
||||
|
||||
The first byte encodes what the package contents (under all the encryption).
|
||||
|
||||
- `s` - 12/18/24 words/raw master/xprv - 17-72 bytes follow, encoded in an internal format
|
||||
- `x` - XPRV mode, full details - 4 bytes (XPRV) + base58 *decoded* binary-XPRV follows
|
||||
- `n` - one or many notes export (JSON array)
|
||||
- `v` - seed vault export (JSON: one secret key but includes name, source of key)
|
||||
- `p` - binary PSBT to be signed, perhaps multisig but not required.
|
||||
- `b` - complete system backup file (text lines, internal format)
|
||||
|
||||
## QR details
|
||||
|
||||
BBQr is always used for the QR's involved in this process, even if
|
||||
they are short enough for a normal QR code. Because the BBQr is
|
||||
being generated by the COLDCARD embedded firmware, it will not be
|
||||
compressed and will always be Base32 encoded.
|
||||
|
||||
New type codes for BBQr are defined for the purposes of this application:
|
||||
|
||||
- `R` contains `(pubkey)` ... begins the process from receiver; compressed pubkey is 33 bytes
|
||||
- `S` contains `(pubkey)(data)` ... data from sender; first 33 bytes are sender's pubkey
|
||||
- `E` for Multisig PSBT: `(randint)(data)` ... randint (4 byte nonce) indicates which
|
||||
derived subkey from pre-shared xpub associated with receiver
|
||||
|
||||
All the data is encrypted with the exception randint. Keep in mind
|
||||
this is a nonce value picked uniquely for each transfer. The
|
||||
receiver's pubkey is only weakly encrypted by the 8-digit numeric
|
||||
password, but is also a nonce effectively.
|
||||
|
||||
### PSBT Key Selection
|
||||
|
||||
When sending PSBT data, a nonce is picked at random by the sender
|
||||
in range: `0..(2^28)`
|
||||
|
||||
This nonce is called `randint`. The receiver's pubkey will be
|
||||
|
||||
.../20250317/(randint)
|
||||
|
||||
where `...` is the derivation used in the multisig wallet for the co-signer who will
|
||||
receive the package. The sender's keypair has the same sub key path assuming all
|
||||
co-signers have same derivation path from root (not required).
|
||||
|
||||
Because both the sender and receiver already have each other's XPUB they can derive
|
||||
the appropriate pubkeys (and privkey for their side) without communicating
|
||||
more than `randint`. The sending COLDCARD will pick a new random value each time.
|
||||
|
||||
When receiving a multisig PSBT encrypted this way, the receiver does not need
|
||||
to do any setup (nor numeric password) and can receive a QR code at any time.
|
||||
This works because the shared multisig wallet is already setup. Receiver will
|
||||
take the nonce value (randint) and seach all pre-defined multisig wallets for
|
||||
any pubkey that can decrypt the package successfully (based on checksum inside
|
||||
first layer of ECDH encryption).
|
||||
|
||||
The next layer of encryption (paranoid password) is unchanged.
|
||||
|
||||
## Encryption Details
|
||||
|
||||
AES-256-CTR is used exclusively. Session key is picked via ECDH with final
|
||||
key value being the SHA256 over 64 bytes of coordinate X (concat) Y.
|
||||
|
||||
While ECDH is enough to assure privacy from men in the middle, we
|
||||
add an additional layer of encryption. We call this the "paranoid key" internally
|
||||
and in the UX it is called "Teleport Password".
|
||||
|
||||
The user sees a random 8-character password, generated as a random 40-bit value, but
|
||||
shown in Base32 (8 chars) for the human to enter. We apply PBKDF2-SHA512 with
|
||||
an iteration count of 5000 to stretch that to 512 bits, of which we use half.
|
||||
The session key is used as the key for the KDF, and the entered value as salt.
|
||||
|
||||
- ECDH arrives at session key
|
||||
- decrypt (AES-256-CTR) the binary body of message
|
||||
- verify checksum:
|
||||
- final 2 bytes should be `== SHA256(decrypted body[0:-2])[-2:]`
|
||||
- if not, corruption, truncation, or wrong keys
|
||||
- if that decryption is correct, then prompt user for the paranoid key (8 chars)
|
||||
- stretch that value using session key and 5000 iterations of PBKDF2-SHA512
|
||||
- use upper 256 bits and run AES-256-CTR again
|
||||
- same checksum of 2 bytes of SHA256 are found inside after decryption
|
||||
|
||||
Encryption adds 4 bytes of overhead because of these MAC values,
|
||||
but should catch truncation and bitrot. There are no other
|
||||
protections against truncation as length data is not transmitted.
|
||||
|
||||
# Receiver Password
|
||||
|
||||
When the teleport process is started, the receiver shares his pubkey
|
||||
as QR. However, we also show an 8-digit numeric password. The
|
||||
purpose of this is force the receiver to share this separately from
|
||||
the pubkey QR on another channel. The code is randomly picked, but
|
||||
only represents about 26 bits of entropy and is stretched with
|
||||
a single round of SHA256 before being used as a AES-256-CTR key
|
||||
to decrypt the pubkey. No checksum verifies correct
|
||||
decryption, so any code is accepted, and will with near-50% odds,
|
||||
decrypt to a valid pubkey.
|
||||
|
||||
When the sender is given the receiver's pubkey via QR code, it
|
||||
prompts for the numeric code and uses it to decrypt the pubkey.
|
||||
Thus a MiTM who injects their pubkey will be detected and blocked.
|
||||
|
||||
The "paranoid key" serves the same role in the other direction but
|
||||
it is Base32 character set, so it will not look similar or be
|
||||
confusing as to its purpose.
|
||||
|
||||
# Web Component
|
||||
|
||||
In order to "teleport" the contents of a QR code over NFC, we will
|
||||
publish a static website directly from an open Github repository.
|
||||
The single-page website contains javascript code which looks at the
|
||||
"hash" part of the incoming URL (`window.location.hash`) and if it
|
||||
meets the requirements, renders a large QR. The QR data must look like
|
||||
a correctly-encoded BBQr with one of the 3 type-codes above (`R` `S` or `E`).
|
||||
Otherwise the website could render any QR, which we don't want to
|
||||
support.
|
||||
|
||||
The page will offer "copy to clipboard" features for the data inside
|
||||
the QR as a URL (ie. same URL as shown) and as an image and of course,
|
||||
the COLDCARD Q can scan from the web browser screen itself.
|
||||
|
||||
When the BBQr data is larger than comfortable for a single QR, the
|
||||
website can split into a multi-frame BBQr. The website can
|
||||
do this without understanding the contents of the BBQr data (all
|
||||
of which is encrypted). Download options will be provided for
|
||||
single-frame QR, animated PNG, and "stacked BBQr" (a single tall
|
||||
PNG with each QR frame stacked).
|
||||
|
||||
On the COLDCARD side, when NFC is tapped, it will offer a long URL
|
||||
to this site with the data to be transferred "after the hash". This
|
||||
is optional since the QR can be shown on the Q itself, and would
|
||||
pass the same data.
|
||||
|
||||
Since the website is running on Github, Coinkite does not have
|
||||
access to IP addresses or other access log details. Because the data for
|
||||
teleport is "after the hash" it is never sent to Github's servers
|
||||
but remains in the browser only. All JS resources referenced by the
|
||||
webpage will have content hashes applied to prevent interference,
|
||||
and the site will be served over SSL.
|
||||
|
||||
Visit [keyteleport.com](https://keyteleport.com/), or an
|
||||
[example small QR](https://keyteleport.com/#B$2R0100VHT2AGUUH7KUZUUSTOWOIWHJX3XM7GA2N4BHQOXDFHXLVHVA7K6ZO)
|
||||
and [view source code](https://github.com/coinkite/keyteleport.com).
|
||||
|
||||
# UX Details
|
||||
|
||||
- When the receive process is started by the user, a pubkey is picked
|
||||
and stored, so that they can come back later (after a power cycle)
|
||||
and make use of the data encoded by the sender. However once a package
|
||||
is decoded successfully, that key is deleted.
|
||||
|
||||
- Sender must start by scanning the QR from a receiver. Then can pick what
|
||||
to send, from secure notes to seeds and so on.
|
||||
|
||||
- For PSBT multisig, user must pick a single co-signer (who hasn't already
|
||||
signed) and the QR is prepared for that receiver. Because we
|
||||
cannot do arbitary combining, it's best if the next signer continues
|
||||
to teleport the updated PSBT to further signers. In other words,
|
||||
a daisy-chain pattern is prefered to a star pattern. The signer
|
||||
who completes the Mth (of N) signature will be able to finalize
|
||||
the transaction, and ideally with PushTx feature, broadcast it.
|
||||
|
||||
# Security Comments
|
||||
|
||||
## Such short passwords?
|
||||
|
||||
We are using 8-character passwords because we want them to be
|
||||
practical to share over non-digital channels such as a voice phone
|
||||
call, or hand-written note.
|
||||
|
||||
It is very important to remind users that the passwords should be sent
|
||||
by a different channel from the QR itself. Best is to call up your
|
||||
other party and say the letters to them directly.
|
||||
|
||||
## Is it safe to save image of QR to cloud?
|
||||
|
||||
Yes, this seems safe. Of course, if you can control it, perhaps not
|
||||
a risk to accept... but the QR is encrypted via ECDH using a key
|
||||
that is forgotten after the transfer, so forward privacy is protected.
|
||||
Also your cloud service (or photo roll, chat app log, etc) will not
|
||||
have the 8-character password which is also required unpack the secrets.
|
||||
|
||||
The QR codes themselves are fully random and do not reveal the
|
||||
identity of your COLDCARD, your on chain funds or anything linked
|
||||
to you.
|
||||
@ -1,12 +1,12 @@
|
||||
# BIP-39 Import
|
||||
# BIP39 Import
|
||||
|
||||
- there must be 12, 18 or 24 words in your mnemonic
|
||||
- we have only the English word list
|
||||
- we support BIP-39 passwords during import
|
||||
- we support BIP39 passwords during import
|
||||
|
||||
# XPRV Import
|
||||
|
||||
- we can import a BIP-32 HD-wallet root private key in XPRV format
|
||||
- we can import a BIP32 HD-wallet root private key in XPRV format
|
||||
- it's assumed to be top-level, and we don't store the parent fingerprint or depth value
|
||||
- SLIP-132 format HD-wallet keys are also supported (xprv/yprv/zprv), but we strip
|
||||
the implied address format
|
||||
@ -14,12 +14,11 @@
|
||||
# PIN Codes
|
||||
|
||||
- 2-2 through 6-6 in size, numeric digits only
|
||||
- pin code 999999-999999 was reserved (meaning 'clear pin'), but now available again
|
||||
- pin code 999999-999999 is reserved (means 'clear pin')
|
||||
|
||||
# Backup Files
|
||||
|
||||
- we don't know what day it is, so meta data on files will not have correct date/time
|
||||
- release date of the firmware version that made the file is used instead of true date
|
||||
- encrypted files produced cannot be changed, and we don't support other tools making them
|
||||
|
||||
# Micro SD
|
||||
@ -33,7 +32,7 @@
|
||||
- with Electrum, we support classic payment addresses (p2pkh), Bech32 Segwit and P2SH/Segwit
|
||||
- however, each wallet must be of a single address type; cannot be mixed (their limitation)
|
||||
- the same Coldcard could be used in each of the three modes (we don't care about address format)
|
||||
- with Bitcoin Core (version 0.17+), we can do PSBT transactions, which support all address types
|
||||
- with Bitcoin Core (version 0.17?), we can do PSBT transactions, which support all address types
|
||||
- we don't support signing coinbase transactions, so don't mine directly into a Coldcard wallet
|
||||
|
||||
# Max Transaction Size
|
||||
@ -42,7 +41,7 @@
|
||||
- we support transactions up to 384k-bytes in size when serialized into PSBT format
|
||||
- we can handle transactions with up to 20 inputs to be signed at one time.
|
||||
- a maximum of 250 outputs per transaction is supported (will attempt more if memory allows)
|
||||
- mk4/Q:
|
||||
- mk4:
|
||||
- we support PSBT files up to 2M bytes in size.
|
||||
- any number of inputs and outputs are supported, limited only by final transaction size (100k)
|
||||
- tested with: 250 inputs, 2000 outputs
|
||||
@ -56,36 +55,27 @@
|
||||
|
||||
- only one signature will be added per input. However, if needed the partly-signed
|
||||
PSBT can be given again, and the "next" leg will be signed.
|
||||
- finalizing of multisig transactions involving P2SH signatures:
|
||||
* SD/Vdisk signing exports both signed PSBT and finalized txn ready for broadcast (if txn is complete)
|
||||
* QR/NFC outputs finalized txn ready for broadcast if txn is complete otherwise signed PSBT only
|
||||
* USB signing requires `--finalize` parameter (as for standard single signature wallets)
|
||||
|
||||
- we do not support PSBT combining or finalizing of transactions involving
|
||||
P2SH signatures (so the combine step must be off-device)
|
||||
- we can sign for P2SH and P2WSH addresses that represent multisig (M of N) but
|
||||
we cannot sign for non-standard scripts because we don't know how to present
|
||||
that to the user for approval.
|
||||
- during USB "show address" for multisig, we limit subkey paths to
|
||||
16 levels deep (including master fingerprint)
|
||||
- max of 15 co-signers due to 1650 byte `scriptSig` limitation in policy with classic P2SH (same limit applies to segwit even though consensus allows up to 20 co-signers).
|
||||
note: the consensus layer sets an upper bound of 520 bytes for the length of each stack element
|
||||
- max of 15 co-signers due to 520 byte script limitation in consensus layer with classic P2SH
|
||||
- (mk3) we have space for up to 8 M-of-3 wallets, or a single M-of-15 wallet. YMMV
|
||||
- only a single multisig wallet can be involved in a PSBT; can't sign inputs from two different
|
||||
multisig wallets at the same time.
|
||||
- we always store xpubs in BIP-32 format, although we can read SLIP132 format (Ypub/Zpub/etc)
|
||||
- we always store xpubs in BIP32 format, although we can read SLIP132 format (Ypub/Zpub/etc)
|
||||
- change outputs (indicated with paths, scripts in output section) must correspond to
|
||||
the active multisig wallet, and cannot be used to describe an unrelated (multisig) wallet.
|
||||
- derivation path for each cosigner must be known and consistent with PSBT
|
||||
- XFP values (fingerprints) MUST be unique for each of the co-signers
|
||||
- multisig wallet `name` can only contain printable ASCII characters `range(32, 127)`
|
||||
|
||||
### BIP-67
|
||||
|
||||
- importing multisig from PSBT can ONLY create `sortedmulti(...)` multisig according to BIP-67, DO NOT use with `multi(...)`
|
||||
- creating airgapped multisig using COLDCARD as coordinator always produces `sortedmulti(...)` multisig according to BIP-67
|
||||
- COLDCARD import/export [format](https://coldcard.com/docs/multisig/#configuration-text-file-for-multisig) only supports `sortedmulti(...)` multisig according to BIP-67. To import multisig wallet with `multi(...)` use descriptor import [format](https://github.com/bitcoin/bips/blob/master/bip-0383.mediawiki)
|
||||
- encrypted COLDCARD backups that contains multisig wallets with `multi(...)` MUST only be restored on firmware versions with `multi(...)` support
|
||||
- all imported `multi(...)` must differ in keys (same as `sortedmulti(...)`). If `wsh(multi(2,A,B))` is already registered, `wsh(multi(2,B,A))` will be rejected upon import as duplicate, even thought they are actually different script/wallet.
|
||||
- just BIP67 difference is also treated as duplicate. If `wsh(multi(2,A,B)` is registered, `wsh(sortedmulti(2,A,B))` will be rejected as duplicate and vice-versa.
|
||||
# Taproot
|
||||
- more background and detail in `docs/taproot.md`
|
||||
|
||||
|
||||
# SIGHASH types
|
||||
|
||||
@ -119,7 +109,7 @@
|
||||
|
||||
We will summarize transaction outputs as "change" back into same wallet, however:
|
||||
|
||||
- PSBT must specify BIP-32 path in corresponding output section for us to treat as change
|
||||
- PSBT must specify BIP32 path in corresponding output section for us to treat as change
|
||||
- for p2sh-wrapped segwit outputs, redeem script must be provided when needed
|
||||
- any incorrect values here are assumed to be fraud attempts, and are highlighted to user
|
||||
- the _redeemScript_ for `p2wsh-p2sh` is optional, but if provided must be
|
||||
@ -134,31 +124,29 @@ We will summarize transaction outputs as "change" back into same wallet, however
|
||||
- `p2wsh-p2sh`: _redeemScript_ (which is: `0x00 + 0x20 + sha256(witnessScript)`), and
|
||||
_witnessScript_ (which contains the multisig script)
|
||||
- `p2wsh`: only _witnessScript_ (which contains the actual multisig script)
|
||||
- `p2tr`(keypath singlesig): no _redeemScript_, no _witnessScript_ and output key MUST commit to an unspendable script path as follows `Q = P + int(hashTapTweak(bytes(P)))G`
|
||||
- `p2tr`(scriptpath multisig): _taproot_merkle_root_ and _leaf_script_ more info in docs/taproot.md
|
||||
|
||||
|
||||
# Derivation Paths
|
||||
|
||||
- key derivatation paths must be 12 or less in depth (`MAX_PATH_DEPTH`)
|
||||
|
||||
# Pay-to-Pubkey
|
||||
|
||||
- although we have some code for "pay to pubkey" (P2PK not P2PKH), it is untested
|
||||
and unused since this style of payment address is obsolete and largely unused today
|
||||
|
||||
# NFC Feature
|
||||
# NFC Feature (Mk4)
|
||||
|
||||
- can share up to 8000 bytes of PSBT or signed transaction data.
|
||||
- NFC-V (ISO-15693) radio/modulation is common on mobile phones but very rare on desktops
|
||||
|
||||
# Fast Wipe
|
||||
# Fast Wipe (Mk4)
|
||||
|
||||
- each use of "fast wipe" feature consumes a MCU key slot, of which there are 256.
|
||||
- use _Advanced > Danger Zone > MCU Key Slots_ to view usage
|
||||
|
||||
# Trick Pins
|
||||
# Trick Pins (Mk4)
|
||||
|
||||
- "deltamode" PIN must be same length as true pin, and differ only in final 4 positions.
|
||||
- there are 14 trick "slots", but on mk4, we avoid slot 10, so 13 available. (Q: 14)
|
||||
- there are 14 trick "slots", but we avoid slot 10, so 13 available.
|
||||
- duress wallets consume 2 slots (or 3 slots for legacy duress wallet) which must be contiguous
|
||||
- when restoring trick pins from backup files, "forgotten" pins are not restored,
|
||||
and any trick pin which matches the true PIN of the restored system will be dropped
|
||||
@ -166,58 +154,9 @@ We will summarize transaction outputs as "change" back into same wallet, however
|
||||
is not compatible, the deltamode trick PIN is dropped and not restored
|
||||
- duress wallets are supported when derived from 24- or 12-word seed phrases
|
||||
|
||||
# Debug Serial Port
|
||||
# Debug Serial Port (Mk4)
|
||||
|
||||
- virtual USB serial port disabled completely by default, and even if enabled
|
||||
in Danger Zone, only echos output, and does not accept any input
|
||||
- use hardware serial port for interactive REPL access (3.3v TTL levels)
|
||||
|
||||
# BBQr Scanning (Q)
|
||||
|
||||
- files up to 2MiB in size can be accepted
|
||||
- when 14 or less parts, we display status of each part, if more, just percent complete
|
||||
|
||||
# QR Scanning (Q)
|
||||
|
||||
- if not BBQr (or sent as unicode inside BBQr) we auto detect these data types:
|
||||
- PSBT in Base64 or hex
|
||||
- Wire Transaction in Base64 or hex
|
||||
- XPRV, XPUB, bare payment addresses, BIP-21 invoices
|
||||
- seed words (truncated, or full)
|
||||
- SeedQR (but not Compact SeedQR)
|
||||
- for Base58, Bech32 encoded values, if checksum is wrong then it is shown as text
|
||||
- binary QR codes are not supported
|
||||
- when pasting into a secure note, if the QR's data is > 60 chars long, we assume done
|
||||
|
||||
# Secure Notes & Passwords (Q)
|
||||
|
||||
- when Q picks a password for you (using F1 thru F5), the entropy is 126 bits or more,
|
||||
except F3 which makes an easier to type password of around 48 bits entropy.
|
||||
- Title, Sitename and Username fields are limited to 32 characters in length.
|
||||
- Passwords are limited to 128 characters.
|
||||
- Note text is unlimited, but storing very large notes may affect performance system-wide.
|
||||
- Q Backup files restored onto Mk4 will lose their notes, since notes feature is not supported.
|
||||
|
||||
# Address Ownership
|
||||
|
||||
- only the first 1528 addresses (764 each from internal and external (change/not) paths)
|
||||
are considered
|
||||
- if you have used an sub-account, without ever exploring it in the Address Explorer, nor
|
||||
signing a PSBT with those account numbers, we do not search it.
|
||||
- does not search Seed Vault, you'll need to load each of those and re-search
|
||||
- if you have an XFP collision between multiple wallets in SeedVault (ie. two wallets
|
||||
with same descriptors, but different seeds) you will get false negatives
|
||||
|
||||
# Spending Policy
|
||||
|
||||
- (Cosign mode) only 12 or 24 word seeds (not XPRV) are accepted for "key C"
|
||||
- velocity limit:
|
||||
- based on a max magnitude per txn, and a required minimum block height
|
||||
gap, based on previous `nLockTime` value in last-signed PSBT.
|
||||
- if you sign a transaction, but never broadcast it, you will still have to wait out
|
||||
the velocity policy.
|
||||
- PSBT creator must put in `nLockTime` block heights (most already do to avoid fee sniping)
|
||||
- maximum of 25 whitelisted addresses can be stored
|
||||
- Web2FA: any number of mobile devices can be enrolled, but all will have the same shared secret
|
||||
- any warning from the PSBT, such as huge fees, will cause the transaction to be rejected
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ directly from python programs.
|
||||
|
||||
| Start | Size | Notes
|
||||
|---------------|-----------|--------------------------
|
||||
| 0x0800 0000 | 112k | Bootloader code, including reset vector. See `stm32/mk4-bootloader`
|
||||
| 0x0800 0000 | 128k | Bootloader code, including reset vector. See `stm32/mk4-bootloader`
|
||||
| 0x0801 c000 | 8k | Sensitive "pairing secrets" for SE1 and SE2
|
||||
| 0x0801 e000 | 8k | MCU keys, consumable; 256 32-bit write-once slots.
|
||||
| 0x0802 0000 | 16k | Interrupt handlers, file header (Micropython and Coldcard code)
|
||||
|
||||
@ -2,6 +2,24 @@
|
||||
Choose PIN Code
|
||||
Advanced/Tools
|
||||
View Identity
|
||||
Temporary Seed
|
||||
Generate Words
|
||||
12 Words
|
||||
24 Words
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import Words
|
||||
12 Words
|
||||
18 Words
|
||||
24 Words
|
||||
Import via NFC
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Upgrade Firmware
|
||||
Show Version
|
||||
From MicroSD
|
||||
From VirtDisk [IF VIRTDISK ENABLED]
|
||||
Bless Firmware
|
||||
Paper Wallets
|
||||
Perform Selftest
|
||||
Secure Logout
|
||||
@ -13,24 +31,20 @@
|
||||
New Seed Words
|
||||
12 Words
|
||||
24 Words
|
||||
Advanced
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import Existing
|
||||
12 Words
|
||||
[SEED WORD ENTRY]
|
||||
[SEED WORD MENUS]
|
||||
18 Words
|
||||
[SEED WORD ENTRY]
|
||||
[SEED WORD MENUS]
|
||||
24 Words
|
||||
[SEED WORD ENTRY]
|
||||
Scan QR Code [IF QR SCANNER]
|
||||
[SEED WORD MENUS]
|
||||
Restore Backup
|
||||
Clone Coldcard
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Seed XOR
|
||||
Migrate Coldcard
|
||||
Key Teleport (start)
|
||||
Help
|
||||
Advanced/Tools
|
||||
View Identity
|
||||
@ -40,408 +54,42 @@
|
||||
24 Words
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import from QR Scan [IF QR SCANNER]
|
||||
Import Words
|
||||
12 Words
|
||||
18 Words
|
||||
24 Words
|
||||
Import via NFC [IF NFC ENABLED]
|
||||
Import via NFC
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Coldcard Backup
|
||||
Restore Seed XOR
|
||||
Upgrade Firmware [IF NOT TMP SEED]
|
||||
Upgrade Firmware
|
||||
Show Version
|
||||
From MicroSD
|
||||
From VirtDisk [IF VIRTDISK ENABLED]
|
||||
Bless Firmware
|
||||
File Management
|
||||
Verify Backup
|
||||
List Files
|
||||
Verify Sig File
|
||||
NFC File Share [IF NFC ENABLED]
|
||||
BBQr File Share [IF QR SCANNER]
|
||||
QR File Share [IF QR SCANNER]
|
||||
Format SD Card
|
||||
Format RAM Disk [IF VIRTDISK ENABLED]
|
||||
Key Teleport (start)
|
||||
Paper Wallets
|
||||
Perform Selftest
|
||||
I Am Developer.
|
||||
Serial REPL
|
||||
Wipe LFS
|
||||
Warm Reset
|
||||
Restore Bkup
|
||||
Reflash GPU [IF QWERTY KEYBOARD]
|
||||
Restore Txt Bkup
|
||||
Secure Logout
|
||||
Settings
|
||||
Login Settings
|
||||
Change Main PIN
|
||||
Set Nickname
|
||||
Scramble Keys
|
||||
Kill Key
|
||||
Login Countdown
|
||||
Disabled
|
||||
5 minutes
|
||||
15 minutes
|
||||
30 minutes
|
||||
1 hour
|
||||
2 hours
|
||||
4 hours
|
||||
8 hours
|
||||
12 hours
|
||||
24 hours
|
||||
48 hours
|
||||
3 days
|
||||
1 week
|
||||
28 days later
|
||||
MicroSD 2FA [IF SECRET AND NOT TMP SEED]
|
||||
Add Card
|
||||
Check Card
|
||||
Remove Card #1
|
||||
Calculator Login [IF QWERTY KEYBOARD]
|
||||
Default Off
|
||||
Calculator Login
|
||||
Test Login Now
|
||||
Hardware On/Off
|
||||
USB Port
|
||||
Default On
|
||||
Disable USB
|
||||
Virtual Disk
|
||||
Default Off
|
||||
Enable
|
||||
Enable & Auto
|
||||
NFC Sharing
|
||||
Default Off
|
||||
Enable NFC
|
||||
NFC Push Tx
|
||||
coldcard.com
|
||||
mempool.space
|
||||
Custom URL...
|
||||
Disable
|
||||
Display Units
|
||||
BTC
|
||||
mBTC
|
||||
bits
|
||||
sats
|
||||
Max Network Fee
|
||||
10% (default)
|
||||
25%
|
||||
50%
|
||||
no limit
|
||||
Idle Timeout
|
||||
2 minutes
|
||||
5 minutes
|
||||
15 minutes
|
||||
1 hour
|
||||
4 hours
|
||||
8 hours
|
||||
Never
|
||||
Idle Timeout (on battery) [IF BATTERIES]
|
||||
30 seconds
|
||||
60 seconds
|
||||
2 minutes
|
||||
5 minutes
|
||||
10 minutes
|
||||
15 minutes
|
||||
30 minutes
|
||||
1 hour
|
||||
4 hours
|
||||
Never
|
||||
LCD Brightness (on battery) [IF BATTERIES]
|
||||
25%
|
||||
50%
|
||||
60%
|
||||
70%
|
||||
80%
|
||||
90%
|
||||
95% (default)
|
||||
100%
|
||||
Delete PSBTs
|
||||
Default Keep
|
||||
Delete PSBTs
|
||||
Buried Settings
|
||||
Home Menu XFP [IF SECRET AND NOT TMP SEED]
|
||||
Only Tmp
|
||||
Always Show
|
||||
Menu Wrapping
|
||||
Default
|
||||
Always Wrap
|
||||
[QR key shortcut] [IF QR SCANNER]
|
||||
---
|
||||
|
||||
[NORMAL OPERATION]
|
||||
Ready To Sign
|
||||
Passphrase [IF WORD BASED SEED]
|
||||
Restore Saved
|
||||
c*******
|
||||
[3A14F788]
|
||||
Restore
|
||||
Delete
|
||||
Edit Phrase
|
||||
Scan Any QR Code [IF QR SCANNER]
|
||||
Start HSM Mode [IF HSM POLICY]
|
||||
Address Explorer
|
||||
Classic P2PKH
|
||||
↳ mtHSVByP9EYZ⋯Vm19gvpecb5R
|
||||
P2SH-Segwit
|
||||
↳ 2NCAJ5wD4Gvm⋯NphNU8UYoEJv
|
||||
Segwit P2WPKH
|
||||
↳ tb1qupyd58nd⋯vu9jtdyws9n9
|
||||
Applications
|
||||
Samourai
|
||||
Post-mix
|
||||
Pre-mix
|
||||
Wasabi
|
||||
Account Number
|
||||
Custom Path
|
||||
CC-2-of-4
|
||||
Secure Notes & Passwords [IF ENBALED] [MAYBE]
|
||||
1: note0
|
||||
"note0"
|
||||
View Note
|
||||
Edit
|
||||
Delete
|
||||
Export
|
||||
Sign Note Text
|
||||
2: secret-PWD
|
||||
"secret-PWD"
|
||||
↳ satoshi
|
||||
↳ abc.org
|
||||
View Password
|
||||
Send Password [MAYBE]
|
||||
Export
|
||||
Edit Metadata
|
||||
Delete
|
||||
Change Password
|
||||
Sign Note Text
|
||||
New Note
|
||||
New Password
|
||||
Export All
|
||||
Sort By Title
|
||||
Import
|
||||
Type Passwords [MAYBE]
|
||||
Seed Vault [MAYBE]
|
||||
1: [7126EB3C]
|
||||
[7126EB3C]
|
||||
Use This Seed
|
||||
Rename
|
||||
Delete
|
||||
2: [CCEE13B9]
|
||||
[CCEE13B9]
|
||||
Use This Seed
|
||||
Rename
|
||||
Delete
|
||||
3: [03EE9989]
|
||||
[03EE9989]
|
||||
Use This Seed
|
||||
Rename
|
||||
Delete
|
||||
Advanced/Tools
|
||||
Backup
|
||||
Backup System
|
||||
Verify Backup
|
||||
Restore Backup
|
||||
Clone Coldcard
|
||||
Export Wallet
|
||||
Sparrow
|
||||
Cove
|
||||
Bitcoin Core
|
||||
Nunchuk
|
||||
Bull Bitcoin
|
||||
Blue Wallet
|
||||
Electrum Wallet
|
||||
Wasabi Wallet
|
||||
Fully Noded
|
||||
Unchained
|
||||
Theya
|
||||
Bitcoin Safe
|
||||
Zeus
|
||||
Samourai Postmix
|
||||
Samourai Premix
|
||||
Descriptor
|
||||
Generic JSON
|
||||
Export XPUB
|
||||
Segwit (BIP-84)
|
||||
Classic (BIP-44)
|
||||
P2WPKH/P2SH (BIP-49)
|
||||
Master XPUB
|
||||
Current XFP
|
||||
Key Expression
|
||||
Dump Summary
|
||||
Upgrade Firmware [IF NOT TMP SEED]
|
||||
Show Version
|
||||
From MicroSD
|
||||
From VirtDisk [IF VIRTDISK ENABLED]
|
||||
File Management
|
||||
Verify Backup
|
||||
Backup System
|
||||
Export Wallet
|
||||
Sparrow
|
||||
Cove
|
||||
Bitcoin Core
|
||||
Nunchuk
|
||||
Bull Bitcoin
|
||||
Blue Wallet
|
||||
Electrum Wallet
|
||||
Wasabi Wallet
|
||||
Fully Noded
|
||||
Unchained
|
||||
Theya
|
||||
Bitcoin Safe
|
||||
Zeus
|
||||
Samourai Postmix
|
||||
Samourai Premix
|
||||
Descriptor
|
||||
Generic JSON
|
||||
Export XPUB
|
||||
Segwit (BIP-84)
|
||||
Classic (BIP-44)
|
||||
P2WPKH/P2SH (BIP-49)
|
||||
Master XPUB
|
||||
Current XFP
|
||||
Key Expression
|
||||
Dump Summary
|
||||
Sign Text File
|
||||
Batch Sign PSBT
|
||||
Teleport Multisig PSBT
|
||||
List Files
|
||||
Verify Sig File
|
||||
NFC File Share [IF NFC ENABLED]
|
||||
BBQr File Share [IF QR SCANNER]
|
||||
QR File Share [IF QR SCANNER]
|
||||
Clone Coldcard
|
||||
Format SD Card
|
||||
Format RAM Disk [IF VIRTDISK ENABLED]
|
||||
Secure Notes & Passwords [IF QWERTY KEYBOARD]
|
||||
1: note0
|
||||
"note0"
|
||||
View Note
|
||||
Edit
|
||||
Delete
|
||||
Export
|
||||
Sign Note Text
|
||||
2: secret-PWD
|
||||
"secret-PWD"
|
||||
↳ satoshi
|
||||
↳ abc.org
|
||||
View Password
|
||||
Send Password [MAYBE]
|
||||
Export
|
||||
Edit Metadata
|
||||
Delete
|
||||
Change Password
|
||||
Sign Note Text
|
||||
New Note
|
||||
New Password
|
||||
Export All
|
||||
Sort By Title
|
||||
Import
|
||||
Derive Seeds (BIP-85)
|
||||
View Identity
|
||||
Temporary Seed
|
||||
Generate Words
|
||||
12 Words
|
||||
24 Words
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import from QR Scan [IF QR SCANNER]
|
||||
Import Words
|
||||
12 Words
|
||||
18 Words
|
||||
24 Words
|
||||
Import via NFC [IF NFC ENABLED]
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Coldcard Backup
|
||||
Restore Seed XOR
|
||||
Key Teleport (start)
|
||||
Spending Policy [IF SECRET AND NOT TMP SEED]
|
||||
Single-Signer [IF SECRET AND NOT TMP SEED]
|
||||
Co-Sign Multisig (CCC) [IF NOT TMP SEED]
|
||||
HSM Mode [IF HSM AND SECRET]
|
||||
Default Off
|
||||
Enable
|
||||
User Management [MAYBE]
|
||||
Paper Wallets
|
||||
WIF Store
|
||||
NFC Tools [IF NFC ENABLED]
|
||||
Sign PSBT
|
||||
Show Address
|
||||
Sign Message
|
||||
Verify Sig File
|
||||
Verify Address
|
||||
File Share
|
||||
Import Multisig
|
||||
Push Transaction [IF PUSHTX ENABLED]
|
||||
Danger Zone
|
||||
Debug Functions
|
||||
Seed Functions
|
||||
View Seed Words
|
||||
Seed XOR
|
||||
Split Existing [IF WORD BASED SEED]
|
||||
Restore Seed XOR
|
||||
Destroy Seed [IF SECRET AND NOT TMP SEED]
|
||||
Lock Down Seed [MAYBE]
|
||||
Export SeedQR [IF WORD BASED SEED]
|
||||
I Am Developer.
|
||||
Serial REPL
|
||||
Warm Reset
|
||||
Restore Bkup
|
||||
BKPW Override
|
||||
Reflash GPU [IF QWERTY KEYBOARD]
|
||||
Seed Vault [IF SECRET AND NOT TMP SEED]
|
||||
Default Off
|
||||
Enable
|
||||
Perform Selftest
|
||||
Set High-Water
|
||||
Wipe HSM Policy [IF HSM POLICY]
|
||||
Clear OV cache
|
||||
Clear Address cache
|
||||
Sighash Checks
|
||||
Default: Block
|
||||
Warn
|
||||
Testnet Mode
|
||||
Bitcoin
|
||||
Testnet4
|
||||
Regtest
|
||||
AE Start Index
|
||||
Default Off
|
||||
Enable
|
||||
B85 Idx Values
|
||||
Default Off
|
||||
Unlimited
|
||||
Settings Space
|
||||
MCU Key Slots
|
||||
Bless Firmware
|
||||
Wipe LFS
|
||||
Nuke Device
|
||||
Settings
|
||||
Login Settings
|
||||
Change Main PIN
|
||||
Trick PINs [IF SECRET AND NOT TMP SEED]
|
||||
Trick PINs:
|
||||
↳11-11
|
||||
PIN 11-11
|
||||
↳Bricks CC
|
||||
Hide Trick
|
||||
Delete Trick
|
||||
Change PIN
|
||||
↳333-3334
|
||||
PIN 333-3334
|
||||
↳Duress Wallet
|
||||
Activate Wallet
|
||||
Hide Trick
|
||||
Delete Trick
|
||||
Change PIN
|
||||
↳WRONG PIN
|
||||
After 3 wrong:
|
||||
↳Wipes seed
|
||||
↳Reboots
|
||||
Hide Trick
|
||||
Delete Trick
|
||||
Trick PINs
|
||||
Add New Trick
|
||||
Add If Wrong
|
||||
Delete All
|
||||
Set Nickname
|
||||
Scramble Keys
|
||||
Scramble Keypad
|
||||
Kill Key
|
||||
Login Countdown
|
||||
Disabled
|
||||
@ -458,13 +106,10 @@
|
||||
3 days
|
||||
1 week
|
||||
28 days later
|
||||
MicroSD 2FA [IF SECRET AND NOT TMP SEED]
|
||||
MicroSD 2FA [MAYBE]
|
||||
Add Card
|
||||
Check Card
|
||||
Remove Card #1
|
||||
Calculator Login [IF QWERTY KEYBOARD]
|
||||
Default Off
|
||||
Calculator Login
|
||||
Test Login Now
|
||||
Hardware On/Off
|
||||
USB Port
|
||||
@ -478,30 +123,22 @@
|
||||
Default Off
|
||||
Enable NFC
|
||||
Multisig Wallets
|
||||
2/4: CC-2-of-4
|
||||
"CC-2-of-4"
|
||||
2/2: core2of2_native
|
||||
"core2of2_native"
|
||||
View Details
|
||||
Delete
|
||||
Coldcard Export
|
||||
Electrum Wallet
|
||||
Descriptors
|
||||
View Descriptor
|
||||
Export
|
||||
Bitcoin Core
|
||||
Import
|
||||
Electrum Wallet
|
||||
Import from File
|
||||
Import via NFC [MAYBE]
|
||||
Export XPUB
|
||||
Create Airgapped
|
||||
Trust PSBT?
|
||||
Skip Checks?
|
||||
Full Address View?
|
||||
Partly Censor
|
||||
Show Full
|
||||
Unsorted Multisig?
|
||||
NFC Push Tx
|
||||
coldcard.com
|
||||
mempool.space
|
||||
Custom URL...
|
||||
Disable
|
||||
Display Units
|
||||
BTC
|
||||
mBTC
|
||||
@ -520,159 +157,49 @@
|
||||
4 hours
|
||||
8 hours
|
||||
Never
|
||||
Idle Timeout (on battery) [IF BATTERIES]
|
||||
30 seconds
|
||||
60 seconds
|
||||
2 minutes
|
||||
5 minutes
|
||||
10 minutes
|
||||
15 minutes
|
||||
30 minutes
|
||||
1 hour
|
||||
4 hours
|
||||
Never
|
||||
LCD Brightness (on battery) [IF BATTERIES]
|
||||
25%
|
||||
50%
|
||||
60%
|
||||
70%
|
||||
80%
|
||||
90%
|
||||
95% (default)
|
||||
100%
|
||||
Delete PSBTs
|
||||
Default Keep
|
||||
Delete PSBTs
|
||||
Keyboard EMU
|
||||
Menu Wrapping
|
||||
Default Off
|
||||
Enable
|
||||
Buried Settings
|
||||
Home Menu XFP [IF SECRET AND NOT TMP SEED]
|
||||
Only Tmp
|
||||
Always Show
|
||||
Menu Wrapping
|
||||
Default
|
||||
Always Wrap
|
||||
Secure Logout
|
||||
[NFC key shortcut] [IF NFC ENABLED]
|
||||
Sign PSBT
|
||||
Show Address
|
||||
Sign Message
|
||||
Verify Sig File
|
||||
Verify Address
|
||||
File Share
|
||||
Import Multisig
|
||||
Push Transaction [IF PUSHTX ENABLED]
|
||||
---
|
||||
|
||||
[FACTORY MODE]
|
||||
Bag Me Now
|
||||
Version: 5.x.x
|
||||
DFU Upgrade
|
||||
Ship W/O Bag
|
||||
Debug Functions
|
||||
Perform Selftest
|
||||
---
|
||||
|
||||
[SSSP]
|
||||
[NORMAL OPERATION]
|
||||
Ready To Sign
|
||||
Passphrase [IF WORD BASED SEED & SSSP RELATED KEYS ENABLED]
|
||||
Restore Saved
|
||||
c*******
|
||||
[3A14F788]
|
||||
Restore
|
||||
Delete
|
||||
Edit Phrase
|
||||
Scan Any QR Code [IF QR SCANNER]
|
||||
Passphrase
|
||||
Start HSM Mode [IF HSM POLICY]
|
||||
Address Explorer
|
||||
Classic P2PKH
|
||||
↳ mtHSVByP9EYZ⋯Vm19gvpecb5R
|
||||
P2SH-Segwit
|
||||
↳ 2NCAJ5wD4Gvm⋯NphNU8UYoEJv
|
||||
Segwit P2WPKH
|
||||
↳ tb1qupyd58nd⋯vu9jtdyws9n9
|
||||
Applications
|
||||
Samourai
|
||||
Post-mix
|
||||
Pre-mix
|
||||
Wasabi
|
||||
Account Number
|
||||
Custom Path
|
||||
CC-2-of-4
|
||||
Secure Notes & Passwords[IF ENABLED & SSSP ALLOW NOTES]
|
||||
1: note0
|
||||
"note0"
|
||||
View Note
|
||||
Sign Note Text
|
||||
2: secret-PWD
|
||||
"secret-PWD"
|
||||
↳ satoshi
|
||||
↳ abc.org
|
||||
View Password
|
||||
Send Password [MAYBE]
|
||||
Sign Note Text
|
||||
Type Passwords [MAYBE]
|
||||
Seed Vault[IF ENABLED & SSSP RELATED KEYS ENABLED]
|
||||
1: [7126EB3C]
|
||||
[7126EB3C]
|
||||
Use This Seed
|
||||
2: [CCEE13B9]
|
||||
[CCEE13B9]
|
||||
Use This Seed
|
||||
3: [03EE9989]
|
||||
[03EE9989]
|
||||
Use This Seed
|
||||
Type Passwords
|
||||
Seed Vault
|
||||
(none saved yet)
|
||||
Temporary Seed
|
||||
Generate Words
|
||||
12 Words
|
||||
24 Words
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import Words
|
||||
12 Words
|
||||
18 Words
|
||||
24 Words
|
||||
Import via NFC
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Secure Logout
|
||||
Advanced/Tools
|
||||
File Management
|
||||
Sign Text File
|
||||
Batch Sign PSBT
|
||||
List Files
|
||||
Export Wallet
|
||||
Sparrow
|
||||
Cove
|
||||
Bitcoin Core
|
||||
Nunchuk
|
||||
Bull Bitcoin
|
||||
Blue Wallet
|
||||
Electrum Wallet
|
||||
Wasabi Wallet
|
||||
Fully Noded
|
||||
Unchained
|
||||
Theya
|
||||
Bitcoin Safe
|
||||
Zeus
|
||||
Samourai Postmix
|
||||
Samourai Premix
|
||||
Descriptor
|
||||
Generic JSON
|
||||
Export XPUB
|
||||
Segwit (BIP-84)
|
||||
Classic (BIP-44)
|
||||
P2WPKH/P2SH (BIP-49)
|
||||
Master XPUB
|
||||
Current XFP
|
||||
Key Expression
|
||||
Dump Summary
|
||||
Verify Sig File
|
||||
NFC File Share [IF NFC ENABLED]
|
||||
BBQr File Share [IF QR SCANNER]
|
||||
QR File Share [IF QR SCANNER]
|
||||
Format SD Card
|
||||
Format RAM Disk [IF VIRTDISK ENABLED]
|
||||
Backup
|
||||
Backup System
|
||||
Verify Backup
|
||||
Restore Backup
|
||||
Clone Coldcard
|
||||
Export Wallet
|
||||
Sparrow
|
||||
Cove
|
||||
Bitcoin Core
|
||||
Nunchuk
|
||||
Bull Bitcoin
|
||||
Blue Wallet
|
||||
Sparrow Wallet
|
||||
Electrum Wallet
|
||||
Wasabi Wallet
|
||||
Fully Noded
|
||||
Unchained
|
||||
Theya
|
||||
Bitcoin Safe
|
||||
Zeus
|
||||
Lily Wallet
|
||||
Samourai Postmix
|
||||
Samourai Premix
|
||||
Descriptor
|
||||
@ -680,45 +207,194 @@
|
||||
Export XPUB
|
||||
Segwit (BIP-84)
|
||||
Classic (BIP-44)
|
||||
P2WPKH/P2SH (BIP-49)
|
||||
P2WPKH/P2SH (49)
|
||||
Master XPUB
|
||||
Current XFP
|
||||
Key Expression
|
||||
Dump Summary
|
||||
Teleport Multisig PSBT [MAYBE]
|
||||
Upgrade Firmware
|
||||
Show Version
|
||||
From MicroSD
|
||||
From VirtDisk [IF VIRTDISK ENABLED]
|
||||
Bless Firmware
|
||||
File Management
|
||||
Verify Backup
|
||||
Backup System
|
||||
Export Wallet
|
||||
Bitcoin Core
|
||||
Sparrow Wallet
|
||||
Electrum Wallet
|
||||
Wasabi Wallet
|
||||
Unchained
|
||||
Lily Wallet
|
||||
Samourai Postmix
|
||||
Samourai Premix
|
||||
Descriptor
|
||||
Generic JSON
|
||||
Export XPUB
|
||||
Segwit (BIP-84)
|
||||
Classic (BIP-44)
|
||||
P2WPKH/P2SH (49)
|
||||
Master XPUB
|
||||
Current XFP
|
||||
Dump Summary
|
||||
Sign Text File
|
||||
Batch Sign PSBT
|
||||
List Files
|
||||
Verify Sig File
|
||||
NFC File Share [IF NFC ENABLED]
|
||||
Clone Coldcard
|
||||
Format SD Card
|
||||
Format RAM Disk [IF VIRTDISK ENABLED]
|
||||
Derive Seed B85
|
||||
View Identity
|
||||
Temporary Seed [IF SSSP RELATED KEYS ENABLED]
|
||||
Import from QR Scan [IF QR SCANNER]
|
||||
Temporary Seed
|
||||
Generate Words
|
||||
12 Words
|
||||
24 Words
|
||||
12 Word Dice Roll
|
||||
24 Word Dice Roll
|
||||
Import Words
|
||||
12 Words
|
||||
18 Words
|
||||
24 Words
|
||||
Import via NFC [IF NFC ENABLED]
|
||||
Import via NFC
|
||||
Import XPRV
|
||||
Tapsigner Backup
|
||||
Coldcard Backup
|
||||
Restore Seed XOR
|
||||
Paper Wallets
|
||||
WIF Store
|
||||
Enable HSM
|
||||
Default Off
|
||||
Enable
|
||||
User Management
|
||||
(no users yet)
|
||||
NFC Tools [IF NFC ENABLED]
|
||||
Sign PSBT
|
||||
Show Address
|
||||
Sign Message
|
||||
Verify Sig File
|
||||
Verify Address
|
||||
File Share
|
||||
Push Transaction [IF PUSHTX ENABLED]
|
||||
Show Firmware Version
|
||||
Destroy Seed [IF SECRET AND NOT TMP SEED]
|
||||
Secure Logout
|
||||
EXIT TEST DRIVE [MAYBE]
|
||||
[NFC key shortcut] [IF NFC ENABLED]
|
||||
Sign PSBT
|
||||
Show Address
|
||||
Sign Message
|
||||
Verify Sig File
|
||||
Verify Address
|
||||
File Share
|
||||
Push Transaction [IF PUSHTX ENABLED]
|
||||
Import Multisig
|
||||
Danger Zone
|
||||
Debug Functions
|
||||
Seed Functions
|
||||
View Seed Words
|
||||
Seed XOR
|
||||
Split Existing
|
||||
Restore Seed XOR
|
||||
Destroy Seed
|
||||
Lock Down Seed
|
||||
I Am Developer.
|
||||
Serial REPL
|
||||
Wipe LFS
|
||||
Warm Reset
|
||||
Restore Txt Bkup
|
||||
Seed Vault
|
||||
Default Off
|
||||
Enable
|
||||
Perform Selftest
|
||||
Set High-Water
|
||||
Wipe HSM Policy [IF HSM POLICY]
|
||||
Clear OV cache
|
||||
Sighash Checks
|
||||
Default: Block
|
||||
Warn
|
||||
Testnet Mode
|
||||
Bitcoin
|
||||
Testnet3
|
||||
Regtest
|
||||
Settings Space
|
||||
MCU Key Slots
|
||||
Settings
|
||||
Login Settings
|
||||
Change Main PIN
|
||||
Trick PINs
|
||||
Add New Trick
|
||||
Add If Wrong
|
||||
Delete All
|
||||
Set Nickname
|
||||
Scramble Keypad
|
||||
Kill Key
|
||||
Login Countdown
|
||||
Disabled
|
||||
5 minutes
|
||||
15 minutes
|
||||
30 minutes
|
||||
1 hour
|
||||
2 hours
|
||||
4 hours
|
||||
8 hours
|
||||
12 hours
|
||||
24 hours
|
||||
48 hours
|
||||
3 days
|
||||
1 week
|
||||
28 days later
|
||||
MicroSD 2FA [MAYBE]
|
||||
Add Card
|
||||
Check Card
|
||||
Remove Card #1
|
||||
Test Login Now
|
||||
Hardware On/Off
|
||||
USB Port
|
||||
Default On
|
||||
Disable USB
|
||||
Virtual Disk
|
||||
Default Off
|
||||
Enable
|
||||
Enable & Auto
|
||||
NFC Sharing
|
||||
Default Off
|
||||
Enable NFC
|
||||
Multisig Wallets
|
||||
2/2: core2of2_native
|
||||
"core2of2_native"
|
||||
View Details
|
||||
Delete
|
||||
Coldcard Export
|
||||
Descriptors
|
||||
View Descriptor
|
||||
Export
|
||||
Bitcoin Core
|
||||
Electrum Wallet
|
||||
Import from File
|
||||
Import via NFC [MAYBE]
|
||||
Export XPUB
|
||||
Create Airgapped
|
||||
Trust PSBT?
|
||||
Skip Checks?
|
||||
Display Units
|
||||
BTC
|
||||
mBTC
|
||||
bits
|
||||
sats
|
||||
Max Network Fee
|
||||
10% (default)
|
||||
25%
|
||||
50%
|
||||
no limit
|
||||
Idle Timeout
|
||||
2 minutes
|
||||
5 minutes
|
||||
15 minutes
|
||||
1 hour
|
||||
4 hours
|
||||
8 hours
|
||||
Never
|
||||
Delete PSBTs
|
||||
Default Keep
|
||||
Delete PSBTs
|
||||
Menu Wrapping
|
||||
Default Off
|
||||
Enable
|
||||
Keyboard EMU
|
||||
Default Off
|
||||
Enable
|
||||
---
|
||||
|
||||
[FACTORY MODE]
|
||||
Bag Me Now
|
||||
DFU Upgrade
|
||||
Show Version
|
||||
Ship W/O Bag
|
||||
Debug Functions
|
||||
Perform Selftest
|
||||
---
|
||||
|
||||
|
||||
@ -29,9 +29,6 @@ tools may or may not hide it from you based on Unix filename
|
||||
conventions. Reformating the card will certainly remove this file,
|
||||
so keep that in mind when managing your "special" cards.
|
||||
|
||||
If using COLDCARD Q and both card slot are populated during login
|
||||
make sure that enrolled card is in top slot (slot A).
|
||||
|
||||
## Menu Settings
|
||||
|
||||
See menu in: `Settings -> Login Settings -> MicroSD 2FA`
|
||||
|
||||
27
docs/miniscript.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Miniscript
|
||||
|
||||
**COLDCARD<sup>®</sup>** Mk4 experimental `EDGE` versions
|
||||
support Miniscript and MiniTapscript.
|
||||
|
||||
## Import/Export
|
||||
|
||||
* `Settings` -> `Miniscript` -> `Import from file`
|
||||
* only [descriptors](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki) allowed for import
|
||||
* `Settings` -> `Miniscript` -> `<name>` -> `Descriptors`
|
||||
* only [descriptors](https://github.com/bitcoin/bips/blob/master/bip-0380.mediawiki) are exported
|
||||
* export extended keys to participate in miniscript:
|
||||
* `Advanced/Tools` -> `Export Wallet` -> `Generic JSON`
|
||||
* `Settings` -> `Multisig Wallets` -> `Export XPUB`
|
||||
|
||||
## Address Explorer
|
||||
|
||||
Same as with basic multisig. After miniscript wallet is imported,
|
||||
item with `<name>` is added to `Address Explorer` menu.
|
||||
|
||||
|
||||
## Limitations
|
||||
* no duplicate keys in miniscript (at least change indexes in subderivation has to be different)
|
||||
* subderivation may be omitted during the import - default `<0;1>/*` is implied
|
||||
* only keys with key origin info `[xfp/p/a/t/h]xpub`
|
||||
* maximum number of keys allowed in segwit v0 miniscript is 20
|
||||
* check MiniTapscript limitations in `docs/taproot.md`
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
## Background
|
||||
|
||||
The **COLDCARD<sup>®</sup>** Mk4 and Q have two secure elements:
|
||||
The Mk4 **COLDCARD<sup>®</sup>** has two secure elements:
|
||||
|
||||
- SE1 (Secure Element 1): Microchip ATECC608
|
||||
- SE1 (Secure Element 1): Microchip ATECC608B
|
||||
- SE2 (Secure Element 2): Maxim DS28C36B
|
||||
|
||||
Because different vendors make them, they do not share bugs and weaknesses.
|
||||
@ -20,13 +20,15 @@ HMAC-SHA256.
|
||||
|
||||
Assume attackers have physical access to a COLDCARD, have opened
|
||||
the case, and can probe the bus connections between the MCU and SE1
|
||||
or SE2. They may even de-solder SE1 and SE2 from the board, and put
|
||||
active circuits between them and the MCU — an active MiTM attack.
|
||||
or SE2. They may even desolder SE1 and SE2 from the board, and put
|
||||
active circuits between them and the MCU — an active MiTM
|
||||
attack. (The Mk4 has improved goop on all three parts and all these
|
||||
critical signals run on internal layers of the PCBA.)
|
||||
|
||||
|
||||
### The Solutions
|
||||
|
||||
Three parties hold secrets in the COLDCARD: the main MCU (microcontroller)
|
||||
Three parties hold secrets in the Mk4: the main MCU (microcontroller)
|
||||
and the two secure elements. Our goal is that **all three** must
|
||||
be fully compromised to access the seed words. Thus, if one part
|
||||
has a vulnerability, the COLDCARD as a whole is still secure.
|
||||
@ -35,7 +37,7 @@ if all three devices are cracked wide open. (This is a last line
|
||||
of defence, a brute-force attack on all PIN combinations will breach
|
||||
it.)
|
||||
|
||||
COLDCARD also supports new Trick PIN codes with side effects such
|
||||
The Mk4 also supports new Trick PIN codes with side effects such
|
||||
as wiping or bricking the COLDCARD, or providing access to a decoy
|
||||
or duress wallet. Ideally, attackers will not detect using a false
|
||||
PIN, even while probing the signals on the board.
|
||||
@ -51,7 +53,7 @@ and the MicroPython code cannot read this area of the chip. Using
|
||||
an internal firewall feature and PCROP (proprietary code readout
|
||||
protection) achieves this result.
|
||||
|
||||
COLDCARD also shares a secret between SE2 and the MCU. Just like SE1,
|
||||
Mk4 also shares a secret between SE2 and the MCU. Just like SE1,
|
||||
this authenticates SE2 to the MCU and encrypts their mutual
|
||||
communications. The Pairing Secret for SE2 is not stored in SE1 and
|
||||
is unique from the other Pairing Secret used for SE1.
|
||||
@ -92,8 +94,8 @@ increases flexibility and resistance to known plain text attacks.
|
||||
| `pin stretch` | slot 2 | HMAC | SE1 | Key stretching for PIN entry and anti-phish words
|
||||
| `firmware` | slot 14 | SHA256d | SE1 | Firmware checksum, controls green/red LEDs
|
||||
| `nonce/chksum` | slot 10 | data | SE1 | AES nonce and GMAC tag, protected by PIN
|
||||
| `SE2 easy key` | page 14 | AES via HMAC | SE2 | Another SE2 part of AES seed key
|
||||
| `SE2 hard key` | page 15 | AES via ECC | SE2 | SE2's part of AES seed key; ECC used to unlock
|
||||
| `SE2 easy key` | page 15 | AES via HMAC | SE2 | Another SE2 part of AES seed key
|
||||
| `SE2 hard key` | page 14 | AES via ECC | SE2 | SE2's part of AES seed key; ECC used to unlock
|
||||
| `tpin key` | `tpin_key` | HMAC(key) | MCU | Key for HMAC used to encrypt trick PINs
|
||||
| `trick PIN slots` | pages 0-12 | HMAC | SE2 | Protect duress wallet seeds and pins (6 spots)
|
||||
| `SE2 trash` | secret B | HMAC | SE2 | Used to destroy values (only SE2 knows the value)
|
||||
@ -193,7 +195,7 @@ user enters against all the slots and works silently to support the
|
||||
Trick features.
|
||||
|
||||
The type of support depends on the type of Trick. Duress wallets
|
||||
require storing 32 or 64 bytes of seed words (generated from the true
|
||||
require storing 32 bytes of seed words (generated from the true
|
||||
seed via BIP-85). Other cases dictate encoding a short numeric code
|
||||
provided to higher layers for implementation. For example, a flag
|
||||
in that code can trigger the boot ROM to wipe the `mcu seed key`.
|
||||
@ -204,8 +206,7 @@ key` being zero makes the seed permanently inaccessible.
|
||||
|
||||
The MCU code may continue speaking to SE1 to complete the fraud,
|
||||
but in general, SE1 will no longer store the duress wallet or Brick
|
||||
Me PINs as in previous generations (Mk1-3). Mk4 and Q implement
|
||||
those feature in SE2.
|
||||
Me PINs. Mk4 implements those feature in SE2.
|
||||
|
||||
|
||||
### Trick PIN Operation
|
||||
@ -267,7 +268,7 @@ inside SE1. This storage is called Spare Secrets. Spare Secrets has
|
||||
3 × 72 bytes of space, protected by the same measures as the
|
||||
seed words.
|
||||
|
||||
Mk4/Q still supports the Long Secret (416 bytes), but its API is
|
||||
Mk4 still supports the Long Secret (416 bytes), but its API is
|
||||
changed. The slow speed of fetching the Long Secret in 32-byte
|
||||
blocks due to the reconstructing the primary AES Seed Key for each
|
||||
call necessitated the change.
|
||||
@ -291,7 +292,7 @@ PINs that continue operation (duress PINs), unlike the average thug.
|
||||
|
||||
## Fast Brick
|
||||
|
||||
Quickly bricking the system is done by rotating the SE1
|
||||
On the Mk4, quickly bricking the system is done by rotating the SE1
|
||||
pairing secret by mixing in a random nonce via the chip's key
|
||||
rotation process. Only a knowledge of the old pairing secret is
|
||||
needed for this change. This is similar the to `brick_me` PIN
|
||||
@ -1,4 +1,4 @@
|
||||
# COLDCARD Mk4/Mk5/Q Security Model
|
||||
# COLDCARD Mk4 Security Model
|
||||
|
||||
## Abstract
|
||||
|
||||
@ -8,14 +8,11 @@ seed words used to generate a deterministic wallet. This secure
|
||||
element is in a limited and read-only state until authorized by PIN entry.
|
||||
|
||||
Clearing the secure element is impossible without first entering
|
||||
the correct PIN. The Mk4 COLDCARD introduced several new security
|
||||
features, including a second secure element and Trick PINs which
|
||||
can render stored data unrecoverable, or brick the COLDCARD entirely
|
||||
if necessary, without entering the true authorization PIN (True
|
||||
PIN).
|
||||
|
||||
The COLDCARD Q continues with the same security model introduced
|
||||
in Mk4.
|
||||
the correct PIN. The Mark 4 COLDCARD (Mk4) introduces several new
|
||||
security features, including a second secure element and Trick PINs
|
||||
which can render stored data unrecoverable, or brick the COLDCARD
|
||||
entirely if necessary, without entering the true authorization PIN
|
||||
(True PIN).
|
||||
|
||||
|
||||
## Introduction
|
||||
@ -25,7 +22,7 @@ the Microchip ATECC508A and later the ATECC608B, to store its
|
||||
secrets. This secure element has 72 bytes of storage protected by
|
||||
a 4- to 12-digit PIN code.
|
||||
|
||||
Mk4 adds a second secure element to the COLDCARD. The ATECC608 is
|
||||
Mk4 adds a second secure element to the COLDCARD. The ATECC608B is
|
||||
still used, now called SE1 (Secure Element 1), along with a new
|
||||
chip, the Maxim DS28C36B, called SE2 (Secure Element 2). The
|
||||
DS28C36B (SE2) has more memory with fifteen 32-byte slots of secure
|
||||
@ -96,10 +93,9 @@ user entered the True PIN. An attacker will only have access to the
|
||||
duress wallet. They won't have access to steal the main stash.
|
||||
|
||||
The private key can be automatically derived using BIP-85 methods,
|
||||
based on account numbers 1001, 1002, or 1003 for a 24-word duress wallet
|
||||
(or 2001, 2002, 2003 for a 12-word one). Because this is BIP-85
|
||||
based, it behaves exactly like a normal wallet. Defining a passphrase
|
||||
for the wallet is also possible.
|
||||
based on account numbers 1001, 1002, or 1003. Because this is BIP-85
|
||||
based and uses a 24-word seed, it behaves exactly like a normal
|
||||
wallet. Defining a passphrase for the wallet is also possible.
|
||||
|
||||
The Mk4 also supports older COLDCARD duress wallets and their UTXOs
|
||||
on the blockchain. There is an option to create compatible wallets
|
||||
@ -191,9 +187,9 @@ customers suggested.
|
||||
|
||||
### Kill Key Feature
|
||||
|
||||
On the Mk4, this feature allows the user to execute a Fast Wipe
|
||||
when the anti-phishing words are displayed on the screen. This
|
||||
feature is turned off by default.
|
||||
This feature allows the user to execute a Fast Wipe when the
|
||||
anti-phishing words are displayed on the screen. This feature is
|
||||
turned off by default.
|
||||
|
||||
The user sets a particular key number to trigger Fast Wipe. If that
|
||||
key is pressed while viewing the anti-phishing words, the seed is
|
||||
@ -204,19 +200,13 @@ It is strongly recommended that the first digit for the second half
|
||||
of the True PIN is **not** used as the Kill Key. Missing a step
|
||||
would unintentionally wipe the seed.
|
||||
|
||||
For the COLDCARD Q, the same feature exists: any letter may be
|
||||
specified but numbers are not supported. This change allows the
|
||||
"kill button" to be active through-out the entire login process.
|
||||
It can be even be pressed while the nickname is shown, and at any
|
||||
point during the PIN entry.
|
||||
|
||||
|
||||
### SPI Serial Flash Removed
|
||||
|
||||
The Mk3 and earlier had a dedicated, external chip to hold settings
|
||||
and the PSBT during operation. Mk4 and later, do not have that
|
||||
chip. The settings now reside inside the main MCU, increasing
|
||||
security. Settings are still AES-encrypted as before.
|
||||
and the PSBT during operation. Mk4 does not have that chip. The
|
||||
settings now reside inside the main MCU, increasing security.
|
||||
Settings are still AES-encrypted as before.
|
||||
|
||||
The separate settings chip could be blanked externally or even
|
||||
removed/replaced. This possibility might enable getting around
|
||||
@ -244,11 +234,11 @@ COLDCARD's case to do so, but the option is there if needed.
|
||||
|
||||
## SD Card Recovery Mode
|
||||
|
||||
Mk4/Mk5/Q bootloader is smart enough to be able to read an SD card. You
|
||||
Mk4 bootloader is smart enough to be able to read an SD card. You
|
||||
will only be able to trigger the SD card loading code, if the
|
||||
COLDCARD was powered down during the upgrade process. At that point,
|
||||
the intended firmware image has been lost because it it held in
|
||||
PSRAM only, during the flash writing process. The bootloader knows
|
||||
PSRAM only during the flash writing process. The bootloader knows
|
||||
main flash (ie. Micropython code) is corrupt because it fails the
|
||||
checksum check (and/or signature check).
|
||||
|
||||
@ -266,9 +256,6 @@ If any other parts of flash---beyond the normal upgradable firmware
|
||||
area---have also been corrupted, this process will not work and the
|
||||
unit will be a brick.
|
||||
|
||||
On the COLDCARD Q, only the top slot (A) is supported for this
|
||||
operation.
|
||||
|
||||
|
||||
## Flash ECC (Error Detection/Correction Codes)
|
||||
|
||||
@ -287,7 +274,7 @@ that it's an attack, such as exposing the bare die to targeted UV-C
|
||||
radiation. If the attacker is able to flip 2 or more bits, then
|
||||
this will effectively brick the COLDCARD once the ECC error is detected.
|
||||
|
||||
Critical flash cells, such as those that prevent JTAG access, are
|
||||
not a single bit (it's a special bit pattern), and regardless are
|
||||
protected via ECC the same as other flash cells.
|
||||
Critical flash cells, such as those that prevent both JTAG access,
|
||||
are not a single bit (it's a special bit pattern), and regardless
|
||||
are protected via ECC the same as other flash cells.
|
||||
|
||||
@ -2,16 +2,15 @@
|
||||
|
||||
COLDCARD can sign messages send to it via USB with the help of `ckcc` utility,
|
||||
sign messages provided via specially crafted file on SD card or Vdisk,
|
||||
and NFC-equipped models (Mk4, Mk5, and Q) can also sign messages sent to COLDCARD via NFC.
|
||||
The resulting signature can be returned over SD card/Vdisk, NFC, or — on Q — as a QR code.
|
||||
and Mk4 can also sign messages sent to COLDCARD via NFC.
|
||||
|
||||
Signature format follows [BIP-0137](https://github.com/bitcoin/bips/blob/master/bip-0137.mediawiki) specification.
|
||||
COLDCARD Mk3 and COLDCARD Mk4 up to version `5.1.0` used compressed P2PKH header byte for all script types.
|
||||
From version `5.1.0` correct header byte is used for corresponding script type.
|
||||
From Mk4 `5.1.0` correct header byte is used for corresponding script type.
|
||||
|
||||
### Verification
|
||||
|
||||
From version `5.1.0` users can verify signed messages directly on the device.
|
||||
From COLDCARD Mk4 version `5.1.0` users can verify signed messages directly on the device.
|
||||
If signature file is on SD card or Virtual disk `Advanced/Tools -> File Management -> Verify Sig File`. In case
|
||||
signature file is detached signature of signed export (or any other file), COLDCARD can check if digest of file
|
||||
specified in the message matches contents of file. This requires file signed to be available on SD card or Vdisk.
|
||||
@ -22,7 +21,7 @@ Bitcoin core can only verify P2PKH.
|
||||
|
||||
## Signed Exports
|
||||
|
||||
From version `5.1.0` most of SD card and Virtual disk exports are accompanied by detached signature file.
|
||||
From Mk4 version `5.1.0` most of SD card and Virtual disk exports are accompanied by detached signature file.
|
||||
If exported file name is `addresses.csv` signature file name will be `addresses.sig`.
|
||||
|
||||
### Message construction and signature file format
|
||||
@ -40,23 +39,29 @@ IFOvGVJrm31S0j+F4dVfQ5kbRKWKcmhmXIn/Lw8iIgaCG5QNZswjrN4X673R7jTZo1kvLmiD4hlIrbuL
|
||||
-----END BITCOIN SIGNATURE-----
|
||||
```
|
||||
|
||||
### What Is Signed
|
||||
### What is signed
|
||||
|
||||
1. **Single sig address explorer exports:** Signed by the key corresponding to the first (0th) address on the exported list.
|
||||
2. **Specific single sig exports:** Signed by the key corresponding to the external address at index zero of chosen application specific derivation `m/<app_deriv>h/<coin_type>'h/<account>h/0/0`.
|
||||
1. **Single sig address explorer exports**. Signed by key corresponding to first (0th) address on the exported list.
|
||||
2. **Specific single sig exports**. Signed by key corresponding to external address at index zero of chosen application specific derivation `m/<app_deriv>/0/0`
|
||||
* Bitcoin Core
|
||||
* Electrum Wallet
|
||||
* Wasabi Wallet
|
||||
* Samourai Postmix
|
||||
* Samourai Premix
|
||||
* Descriptor
|
||||
3. **Generic single sig exports:** Signed by key that corresponds to first (0th) external address at derivation `m/44h/<coin_type>h/<account>h/0/0`.
|
||||
* Lily Wallet
|
||||
* Generic JSON
|
||||
* Dump Summary
|
||||
4. **BIP85 derived entropy exports:** Signed by path that corresponds to specific BIP85 application.
|
||||
5. **Paper wallet exports:** Signed by key and address exported as paper wallet itself.
|
||||
6. **Multisig exports:** public keys are encoded as P2PKH address for all multisg signature exports
|
||||
* Multisig wallet descriptor: signed by the key corresponding to the first external address of own enrolled extended key `my_key/0/0`
|
||||
* Generic XPUBs export: signed by the key corresponding to the first external address of own standard P2WSH derivation `m/48h/<coin_type>h/<account>h/2h/0/0`
|
||||
* Multisig address explorer export: Signed by own key at the same derivation as first (0th) row on exported list. `my_key/<change>/<start_index>`
|
||||
3. **Generic single sig exports**. Signed by key that corresponds to address at derivation `m/44'/<coin_type>'/0'/0/0`
|
||||
Lily Wallet
|
||||
Generic JSON
|
||||
Dump Summary
|
||||
4. **BIP85 derived entropy exports**. Signed by path that corresponds to specific BIP85 application.
|
||||
5. **Paper wallet exports**. Signed by key and address exported as paper wallet itself.
|
||||
|
||||
### What is NOT signed
|
||||
|
||||
Multisig exports and generic multisig xpub exports are not signed. It is not clear at this point
|
||||
whether to sign these exports with some generic single signature key (i.e. `m/44'/<coin_type>'/0'/0/0`)
|
||||
or with our portion (leg) of script. In both cases script type (address format) would not match as multisignature
|
||||
message signing is not standardized.
|
||||
|
||||
1. **Multisig exports**
|
||||
2. **Generic multisig exports**
|
||||
@ -1,20 +1,10 @@
|
||||
# NFC and Coldcard
|
||||
# NFC and Coldcard Mk4
|
||||
|
||||
(Applies to NFC-equipped models: Mk4, Mk5, and Q)
|
||||
(Applies to Coldcard Mk4 only)
|
||||
|
||||
## Usage
|
||||
|
||||
The NFC antenna location depends on the hardware:
|
||||
|
||||
- **Mk4**: a PCB trace loop, centered under number `8` on the keypad.
|
||||
- **Mk5**: a discrete coil (`L6`) in the **top-right corner** of the device
|
||||
- **Q1**: a flexible "sticker" antenna behind the display. The green LED below the
|
||||
bottom-right of the display (`D12`) lights up while an NFC transfer is active —
|
||||
it is the activity indicator, not the antenna.
|
||||
|
||||

|
||||
|
||||
Before using NFC,
|
||||
Mk4 NFC antenna is centered under number `8` on the keypad. Before using NFC,
|
||||
it is important to locate the position of NFC antenna on your device and point it
|
||||
correctly towards the Coldcard NFC antenna. Picture below shows an example with iPhone
|
||||
that has NFC antenna located at the top right edge. The NFC smartphone antenna
|
||||
@ -46,7 +36,7 @@ in general. Good interoperability is critical with radio standards.
|
||||
|
||||
## Lower Layers
|
||||
|
||||
The Coldcard has a chip that acts as a Type 5 NFC tag. The
|
||||
The Coldcard Mk4 has an chip that acts as a Type 5 NFC tag. The
|
||||
radio standard is called "NFC-V" or ISO-15693, and operates on a
|
||||
13.56 Mhz carrier wave.
|
||||
|
||||
@ -68,13 +58,9 @@ unless we are actively sharing something. We disable the "energy
|
||||
harvesting" features of the chip, so it will not do anything when
|
||||
the Coldcard is powered-down, regardless of the NFC setting.
|
||||
|
||||
If the above is not enough for you, the antenna can be destroyed:
|
||||
|
||||
- **Mk4**: cut the trace labeled "NFC" inside the hole for the MicroSD card,
|
||||
using the point of a sharp knife to cut and peel up the trace.
|
||||
- **Mk5**: has no such trace — its antenna is the discrete coil `L6` in the
|
||||
top-right corner, which would have to be physically removed instead.
|
||||
- **Q1**: cut the trace labeled "NFC DATA" under the batteries.
|
||||
If the above is not enough for you, the antenna can be destroyed
|
||||
by cutting the trace labeled "NFC" inside the hole for the MicroSD
|
||||
card. Use the point of a sharp knife to cut and peel up the trace.
|
||||
|
||||
The NFC traffic is not encrypted and is subject to eavesdropping.
|
||||
While the NFC feature is active, your Coldcard can be uniquely
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
# NFC Push Tx
|
||||
|
||||
This feature allows single-tap broadcast of the freshly-signed transaction.
|
||||
|
||||
`PSBT ==[SD|QR|NFC]==> COLDCARD signed TXN ==[NFC tap]==> Phone Browser ==> Server TXN Broadcast`
|
||||
|
||||
Once enabled with a URL, the COLDCARD will show the NFC animation
|
||||
after signing the transaction. When the user taps their phone, the
|
||||
phone will see an NFC tag with URL inside. That URL contains the
|
||||
signed transaction ready to go, and once opening in the mobile
|
||||
browser, that URL will load. The landing page will connect to a
|
||||
Bitcoin node (or similar) and send the transaction on the public
|
||||
Bitcoin network.
|
||||
|
||||
This feature is available on Q and Mk4 and requires NFC to be enabled.
|
||||
See `Settings > NFC Push Tx`.
|
||||
|
||||
See the latest on our feature minisite: [PushTx.org](https://pushtx.org)
|
||||
|
||||
## Protocol Spec
|
||||
|
||||
The COLDCARD needs a URL prefix. To that it appends some values:
|
||||
|
||||
- `t=...`
|
||||
- this is the transaction, in binary encoded with
|
||||
[base64url](https://datatracker.ietf.org/doc/html/rfc4648#section-5)
|
||||
|
||||
- `&c=...`
|
||||
- the rightmost 8 bytes of SHA256 over the transaction. Also `base64url` encoded.
|
||||
|
||||
- `&n=XTN`
|
||||
- if, and only if, the COLDCARD is set for Testnet, this value is appended to
|
||||
indicate that the transaction is for Testnet3 and not MainNet.
|
||||
- when RegTest is enabled, the value will be `XRT`
|
||||
|
||||
We provide a few default URL values to our customers, including one backend we
|
||||
will operate on `coldcard.com`. The URL can also be directly entered by the
|
||||
customer. On the Q, it can be scanned from a QR code.
|
||||
|
||||
For COLDCARD backend, the url used is:
|
||||
|
||||
https://coldcard.com/pushtx#
|
||||
|
||||
The complete URL with a typical transaction might look like this (but longer):
|
||||
|
||||
https://coldcard.com/pushtx#t=AgAAAAMNCxXtp2GVYVhkRXHLMmdZFs4p3kbFK ⋯ ABf&c=uiSVRda-1tw
|
||||
|
||||
We are using hash symbol here so that our server logs do not get
|
||||
contaminated with the arguments. The landing page uses javascript
|
||||
to read the hash part of the URL and decodes from there. If you
|
||||
prefer, your URL can end with `?` and then the arguments will be
|
||||
sent by the phone's browser to your server. Your processing can be
|
||||
entirely done in the backend in this case.
|
||||
|
||||
## Expectations for the Backend
|
||||
|
||||
Your code should decode the transaction and check the SHA-256 hash
|
||||
matches. If it does not match, or if `c` value is missing, assume
|
||||
the URL has been truncated and report that to the user.
|
||||
|
||||
Once decoded, your code should immediately broadcast the transaction.
|
||||
A confirmation step is not required in our opinion. Once it is
|
||||
submitted to Bitcoin Core (or other API), any status response should
|
||||
be decoded and shown to the user so they know it is on it's way.
|
||||
If it was not accepted, please report the error to the user as
|
||||
clearly as possible.
|
||||
|
||||
Next, it would make sense to either link to the TXID on a block
|
||||
explorer to provide further proof that it has been sent and that
|
||||
it is now waiting in the mempool.
|
||||
|
||||
## Backend Implementations
|
||||
|
||||
- Mempool.space's [implementation of this feature](https://github.com/mempool/mempool/pull/5132).
|
||||
|
||||
- A single-file (html and javascript) file is available
|
||||
at [coldcard.com/static/coldcard-pushtx.html](https://coldcard.com/static/coldcard-pushtx.html).
|
||||
You can host this file anywhere your phone can reach, and then use that URL in your
|
||||
COLDCARD settings. It uses your phone's browser to submit directly
|
||||
to `mempool.space` and `blockstream.info` sites (both at same time). It is equivalent
|
||||
to the page hosted at `https://coldcard.com/pushtx#`. Full source code is published here:
|
||||
[github.com/Coldcard/push-tx](https://github.com/Coldcard/push-tx)
|
||||
|
||||
### Notes
|
||||
|
||||
- Complete URL might be as large as 8,000 bytes. Some web servers will not support beyond
|
||||
4k bytes and the NFC implementation of the phone may also have limits.
|
||||
- The service URL provided must end in `?` or `#` or `&`.
|
||||
- `base64url` values from COLDCARD will not have padding (`=` bytes) at end.
|
||||
- POST cannot be used directly because the expect the phone to do a GET on the URL provided.
|
||||
- Honest backends will not log the IP address of incoming transactions, but there is
|
||||
no way to enforce that, and CloudFlare sees all.
|
||||
|
||||
## Example URL
|
||||
|
||||
```
|
||||
https://mempool.space/pushtx#t=AgAAAAOHqK3w3hC6PSC0buthnJA5R9Y88WAlEvm9cifNVUPhIwAAAABqRzBEAiB-M9YprNYoohqHdQHg4wY_qcEMwDmyIQH8prykk8-0KwIgARxcojKrtixicouiUxhk4jQq_MAl11ptIgHDlRjgk5ABIQM4bgMAVDbDSr_9CvLjbg5nxrWnDGI-kVmkfL81GXZtCf____8OaH0RxW7DjZKdIF6rvbHvvyFGCBQ0PTgpx20nA_wbLgAAAABqRzBEAiBwUFigORJDPK8ptnYPAntjV-RUn1jAuzphicQstwVv-QIgEbMC8FWXQ5Jve5DaAqKJsqoj3peK83iub_oOkmbiYg4BIQO5Ehn2t0oUG3hnK4cBnwCwMc33DcdJ8aSMWzRQ_wjZL_____-UG6M-eBeAun-EZp6EbVypvVJ3mXCQrN_fUDn-kwoEnQAAAABqRzBEAiAgFAtVTpQYTKplc9NuV7Ws7ZFYeNO8BCS4ozgWrgd2ogIgGTTcw98xQdcGWeWQhVfVm_vZorBIOYovQPQeK0Lg9t8BIQLPWPioVWvj1z4NMHBCkeirYOUalCa83wbSH0CREnGZvv____8CjM_wCAAAAAAZdqkUIJA8_yqzaj0NzhvYVEIBno5gETGIrIzP8AgAAAAAGXapFEaV7xTyleuEX9OejdlUlsz7RTr0iKwAAAAA&c=hre47vyMC78&n=XTN
|
||||
```
|
||||
|
||||
- this transaction doesn't have valid inputs, and will cause an error
|
||||
- mempool.space will redirect this to a testnet endpoint (because ends with `n=XTN`)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 258 KiB |
@ -11,17 +11,11 @@ The entrypoint makefile for repro builds.
|
||||
The `repro` command in `shared.mk` is the first step in the repro build process, which triggers a docker build and run process.
|
||||
|
||||
```makefile
|
||||
repro: submods-match code-committed
|
||||
repro:
|
||||
docker build -t coldcard-build - < dockerfile.build
|
||||
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh $(VERSION_STRING) $(HW_MODEL) $(PARENT_MKFILE))
|
||||
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh $(VERSION_STRING) $(MK_NUM))
|
||||
```
|
||||
|
||||
`$(HW_MODEL)` is the model string (e.g. `mk4`, `q1`) and `$(PARENT_MKFILE)` is the
|
||||
top-level makefile being used (`MK-Makefile` or `Q1-Makefile`). The `submods-match`
|
||||
and `code-committed` prerequisites ensure the submodules and working tree are clean
|
||||
before a repro build.
|
||||
|
||||
Below are interesting sections from the docker logs that give an idea as to what is going on in build process:
|
||||
|
||||
```stdout
|
||||
@ -67,19 +61,19 @@ Successfully installed signit-1.0
|
||||
|
||||
...
|
||||
|
||||
+ make -f MK-Makefile setup
|
||||
+ make -f MK4-Makefile setup
|
||||
|
||||
...
|
||||
|
||||
+ make -f MK-Makefile firmware-signed.bin firmware-signed.dfu production.bin dev.dfu firmware.lss firmware.elf
|
||||
+ make -f MK4-Makefile firmware-signed.bin firmware-signed.dfu production.bin dev.dfu firmware.lss firmware.elf
|
||||
|
||||
...
|
||||
|
||||
signit sign -b l-port/build-COLDCARD_MK4 -m mk4 5.0.7 -o firmware-signed.bin
|
||||
signit sign -b l-port/build-COLDCARD_MK4 -m 4 5.0.7 -o firmware-signed.bin
|
||||
|
||||
...
|
||||
|
||||
signit sign -m mk4 5.0.7 -r firmware-signed.bin -k 1 -o production.bin
|
||||
signit sign -m 4 5.0.7 -r firmware-signed.bin -k 1 -o production.bin
|
||||
You don't have that key (1), so using key zero instead!
|
||||
...
|
||||
|
||||
@ -102,7 +96,7 @@ production.bin
|
||||
|
||||
...
|
||||
|
||||
+ make -f MK-Makefile 'PUBLISHED_BIN=/tmp/checkout/firmware/releases/2022-10-05T1724-v5.0.7-mk4-coldcard.dfu' check-repro
|
||||
+ make -f MK4-Makefile 'PUBLISHED_BIN=/tmp/checkout/firmware/releases/2022-10-05T1724-v5.0.7-mk4-coldcard.dfu' check-repro
|
||||
|
||||
...
|
||||
|
||||
@ -189,7 +183,7 @@ To summarize `check-repro`:
|
||||
|
||||
- `split` (cli/signit.py: Line 153-175) is run against the release `*.dfu` resulting in a `check-fw.bin` and `check-bootrom.bin`. "This splits the DFU file into the two parts it contains: the main firmware (COLDCARD application) and the boot loader code."
|
||||
|
||||
- `check` (cli/signit.py: Line 176-243) is run against each the release `check-fw.bin` and our built `firmware-signed.bin`.
|
||||
- `check` (cli/signit.py: Line 176-241) is run against each the release `check-fw.bin` and our built `firmware-signed.bin`.
|
||||
|
||||
- a hexdump is taken of each the release `check-fw.bin` and our built `firmware-signed.bin` piped through $TRIM_SIG which removes 64 bytes of signature data and subsitutes it with a common string.
|
||||
|
||||
|
||||
@ -183,12 +183,12 @@ This double-hashed value is what's stored inside the secure element
|
||||
as it travels on the bus. Because of the inclusion of the pairing
|
||||
secret, the hashes generated by each Coldcard will be different.
|
||||
|
||||
With Mark3 hardware, we've added a key-stretching step, which starts
|
||||
With Mark3 hardware, we've added a key-streching step, which starts
|
||||
with the above value, and does HMAC-SHA256 using a secret key known
|
||||
only to the 608a (repeatedly). That value is used directly to check the
|
||||
duress PIN, and if that doesn't match, it is HMAC-SHA256'ed again,
|
||||
using a key that is usage limited. This limits actual PIN login attempts
|
||||
to a set value and is enforced by the 608a internally.
|
||||
to a set value and is enfoced by the 608a internally.
|
||||
|
||||
## Genuine vs. Caution Lights
|
||||
|
||||
@ -281,7 +281,7 @@ Here's what the warning screen looks like:
|
||||
|
||||
### Benefits
|
||||
|
||||
- no warnings, but still trustable thanks to ATECC608
|
||||
- no warnings, but still trustable thanks to ATECC608A
|
||||
- random devs can replace 99% of firmware at Micropython layer (everything but bootloader)
|
||||
- but they need to retain our code for talking to bootloader and secure element,
|
||||
so that PIN can be entered and verified.
|
||||
|
||||
@ -1,112 +0,0 @@
|
||||
# BIP-322 Generic Signed Message Format
|
||||
|
||||
BIP-322 specification: <https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki>
|
||||
|
||||
## Proof of Reserves (POR)
|
||||
|
||||
### PoR PSBT
|
||||
|
||||
COLDCARD accepts a specially crafted PSBT file to sign as BIP-322 Proof of Reserves. The PSBT
|
||||
must meet all these requirements:
|
||||
|
||||
* COLDCARD acts as a BIP-322 PSBT signer. It validates the BIP-322 `to_sign`
|
||||
transaction, shows the message from `PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE`, and
|
||||
adds signatures to the PSBT. Finalizing and encoding the final BIP-322
|
||||
signature string is the responsibility of the finalizer.
|
||||
* PSBT MUST include `PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE = 0x09`; the value is
|
||||
the exact message shown to the user and signed by BIP-322.
|
||||
* PSBT requires `PSBT_IN_BIP32_DERIVATION` for each input
|
||||
* P2SH wrapped segwit addresses MUST have proper redeem script in PSBT: `PSBT_IN_REDEEM_SCRIPT`
|
||||
* P2WSH segwit addresses MUST have proper witness script in PSBT: `PSBT_IN_WITNESS_SCRIPT`
|
||||
* PSBT (`to_sign`) MUST have at least one input.
|
||||
* First (0th) input of `to_sign` MUST spend the BIP-322 `to_spend` output.
|
||||
* Input 0 MUST include one of `PSBT_IN_NON_WITNESS_UTXO` or `PSBT_IN_WITNESS_UTXO`.
|
||||
* When input 0 provides `PSBT_IN_WITNESS_UTXO`, COLDCARD reconstructs the
|
||||
expected `to_spend` txid from `PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE` and the
|
||||
witness UTXO scriptPubKey.
|
||||
* When input 0 provides `PSBT_IN_NON_WITNESS_UTXO`, it MUST be the BIP-322
|
||||
`to_spend` transaction as defined in
|
||||
[BIP-322](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#full):
|
||||
* 1 input, 1 output
|
||||
* output nValue is 0
|
||||
* input prevout hash is 0
|
||||
* input prevout n is 0xffffffff
|
||||
* input scriptSig is `OP_0 PUSH32 message_hash`
|
||||
* PSBT (`to_sign`) MUST only have one output with null-data `OP_RETURN`
|
||||
* `to_sign` transaction version MUST be 0 or 2.
|
||||
* Optionally inputs can be added to `to_sign` for Proof of Reserve signing.
|
||||
* PSBT MUST be version 0 or 2.
|
||||
* Foreign inputs not allowed in POR PSBT.
|
||||
|
||||
The signatures created by the BIP-322 process will never be suitable
|
||||
for a on-chain Bitcoin transaction that could move funds, because
|
||||
of these restrictions imposed by BIP-322.
|
||||
|
||||
### Output
|
||||
|
||||
COLDCARD always returns a signed PSBT for BIP-322 message signing and Proof of
|
||||
Reserves. It never returns an extracted/finalized transaction for these PSBTs.
|
||||
This is true even when finalization is requested over USB, such as with
|
||||
`ckcc unsigned.psbt --finalize`.
|
||||
|
||||
The signed PSBT is the handoff artifact for the external finalizer/verifier. It
|
||||
keeps the PSBT metadata needed to verify or finalize the BIP-322 signature,
|
||||
including public keys, scripts, partial signatures, and UTXO data. This matters
|
||||
because the address being proven normally commits only to a hash of the public
|
||||
key or script, not the public key or script itself.
|
||||
|
||||
### Proof of Reserves Signing Experience
|
||||
|
||||
After Coldcard recognizes a BIP-322 PSBT it reads the message from
|
||||
`PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE` and shows it to the user for approval.
|
||||
COLDCARD verifies that the message hash matches the input 0 `to_spend`
|
||||
commitment before offering to sign.
|
||||
|
||||
When the PSBT contains only input 0, COLDCARD labels the request as
|
||||
`BIP-322 Message`, because it is message signing and does not prove ownership
|
||||
of any additional reserve UTXOs. In that case it does not show transaction
|
||||
input/output counts. When the PSBT contains additional inputs, COLDCARD labels
|
||||
the request as `Proof of Reserves` and shows the reserve amount.
|
||||
|
||||
If the message contains non-ASCII characters, COLDCARD warns that some
|
||||
characters may not be readable on screen.
|
||||
|
||||
Legacy PoR PSBTs without `PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE` are rejected by
|
||||
this flow.
|
||||
|
||||
Read more [here.](https://gist.github.com/orangesurf/0c1d0a31d3ebe7e48335a34d56788d4c)
|
||||
|
||||
Example screen text for a one-input BIP-322 message signing PSBT:
|
||||
|
||||
```text
|
||||
BIP-322 Message
|
||||
|
||||
Message:
|
||||
This is the signed message
|
||||
|
||||
Challenge Address:
|
||||
bc1qzvjnhf7k70uxv6xvneaqxql7k09dd6nsr5wheq
|
||||
|
||||
Press ENTER to approve and sign message. Press (2) to explore transaction.
|
||||
CANCEL to abort.
|
||||
```
|
||||
|
||||
Example screen text for a Proof of Reserves PSBT:
|
||||
|
||||
```text
|
||||
Proof of Reserves
|
||||
|
||||
Message:
|
||||
POR
|
||||
|
||||
Amount 0.20000000 BTC
|
||||
|
||||
Challenge Address:
|
||||
bc1qzvjnhf7k70uxv6xvneaqxql7k09dd6nsr5wheq
|
||||
|
||||
21 inputs
|
||||
1 output
|
||||
|
||||
Press ENTER to approve and sign proof of reserves. Press (2) to explore transaction.
|
||||
CANCEL to abort.
|
||||
```
|
||||
@ -12,7 +12,7 @@ are not discrete and you could be compelled to produce the passphrase.
|
||||
|
||||
Enter [_Seed XOR_](https://seedxor.com), a plausibly deniable means
|
||||
of storing secrets in two or more parts that look and behave just
|
||||
like the original secret. One 12-, 18-, or 24-word seed phrase becomes two or more parts
|
||||
like the original secret. One 12 or 24-word seed phrase becomes two or more parts
|
||||
that are also BIP-39 compatible seeds phrases. These should be backed up in your
|
||||
preferred method, metal or otherwise. These parts can be individually loaded
|
||||
with honeypot funds as each one has same word length, with the last being
|
||||
@ -22,7 +22,7 @@ This one more solution for your game-theory arsenal.
|
||||
|
||||
- *Q*: I'm lazy, can I do this to my Existing Seed?
|
||||
- *A*: Yes. You can split the words you have already in your Coldcard, making
|
||||
2, 3 or 4 new SEEDPLATES. You could also use any number of existing SEEDPLATES
|
||||
2, 3 or 4 new SEEDPLATES. You could also any number of existing SEEDPLATES
|
||||
you have, and combine them to make a new random wallet that is the XOR of
|
||||
their values. Effectively that makes a new random wallet.
|
||||
|
||||
@ -78,12 +78,10 @@ words right the next day.
|
||||
|
||||
When the parts are made deterministically, we take a double-SHA256 over
|
||||
a fixed string (`Batshitoshi`), your master secret, and the text
|
||||
`0 of 4 parts` which changes for each part (the index is 0-based).
|
||||
`1 of 4 parts` which changes for each part.
|
||||
|
||||
In random mode, we simply pick random bytes (and then double-SHA256
|
||||
them) from the Coldcard's True Random Number Generator (TRNG). The number
|
||||
of bytes matches your secret length: 16, 24, or 32 bytes for a 12-, 18-,
|
||||
or 24-word seed respectively.
|
||||
In random mode, we simply pick 32 random bytes (and then double-SHA256
|
||||
them) from the Coldcard's True Random Number Generator (TRNG)..
|
||||
|
||||
This is done to make all but the one part. The final part is the
|
||||
value needed to get back to your secret, so it's the XOR of the
|
||||
@ -159,12 +157,6 @@ with the others on a SEEDPLATE.
|
||||
- right to A, down to B ... take that number, and go to that column
|
||||
- down to C, that is answer: a ⊕ b ⊕ c
|
||||
|
||||
## Open Standard
|
||||
Seed XOR is an open standard. Other software and hardware wallets are encouraged to
|
||||
implement support. No license or permission is required, including usage of the term
|
||||
"Seed XOR" when referring to implementations of this feature. Such implementations
|
||||
should match the process described in this documentation and be fully interoperable.
|
||||
|
||||
---
|
||||
|
||||
# 24 Words XOR Seed Example Using 3 Parts
|
||||
|
||||
@ -1,209 +0,0 @@
|
||||
# Spending Policy
|
||||
|
||||
This special mode will stop you from signing transactions if they
|
||||
exceed a spending policy you define beforehand. Once enabled, many
|
||||
features of the COLDCARD are disabled or inaccessible.
|
||||
|
||||
You might want to use this feature when traveling with your COLDCARD.
|
||||
|
||||
## Spending Policy: Multisig (formerly CCC)
|
||||
|
||||
We also support a mode where the COLDCARD is a multisig co-signer
|
||||
and only performs its signature when a spending policy is met. The
|
||||
other multisig signers are free to sign or not sign as appropriate.
|
||||
|
||||
Multisig mode is more advanced and requires use of multisig addresses,
|
||||
new UTXO, and cooperating multisig on-chain wallets.
|
||||
|
||||
This document will only discuss the "Single signer" version of
|
||||
Spending Policy. Both modes can be active at the same time, but if
|
||||
a transaction would be signed by Multisig policy, then we assume
|
||||
it's also okay to sign your main key as well.
|
||||
|
||||
# Before You Start
|
||||
|
||||
When a Spending Policy is in effect, there are limitations
|
||||
in effect:
|
||||
|
||||
- Firmware updates are blocked.
|
||||
- There is no way to backup the COLDCARD.
|
||||
- Seed vault and Secure Notes are read-only (and can also be hidden).
|
||||
- Settings menu is inaccessible.
|
||||
- BIP-39 passphrases may be blocked (optional).
|
||||
|
||||
We recommend getting the COLDCARD fully configured and setup
|
||||
for typical transactions before enabling the Spending Policy.
|
||||
|
||||
# Setup Spending Policy
|
||||
|
||||
Visit `Advanced / Tools > Spending Policy` menu and choose
|
||||
"Single-Signer". First some background information is shown,
|
||||
then you are prompted to define the "Bypass PIN". This PIN code
|
||||
is only used when you need to disable the spending policy, but is
|
||||
also the only way to do so once enabled... so don't loose it.
|
||||
|
||||
Once the "Bypass PIN" is confirmed, you will arrive at menu for
|
||||
related settings. Use "Edit Policy..." to change the spending policy
|
||||
and define a Max Magnitude (limit number of BTC per transaction),
|
||||
Velocity (minimum time gaps between signed transactions). You can
|
||||
define a whitelist of up to 25 destination addresses (leave empty
|
||||
for any). Finally you can enroll your phone in 2FA (second factor)
|
||||
so that you must open an Authenticator app on your phone before
|
||||
transactions are signed.
|
||||
|
||||
## Other Security Settings
|
||||
|
||||
In addition to policy itself, there are a number of on/off
|
||||
switches which affect operation of the COLDCARD while the Spending
|
||||
Policy is in effect:
|
||||
|
||||
### Word Check
|
||||
|
||||
If enabled, you will have to enter the first and last seed word
|
||||
after the Bypass PIN as an additional security check.
|
||||
|
||||
### Allow Notes
|
||||
|
||||
On the Q, secure notes and passwords may be visible or hidden
|
||||
using this setting. In either case they are strictly readonly.
|
||||
|
||||
### Related Keys
|
||||
|
||||
BIP-39 passphrase entry, Seed Vault usage will be blocked unless this
|
||||
setting is enabled. Even when enabled, the Seed Vault is always readonly
|
||||
and cannot be changed.
|
||||
|
||||
# Other Menu Items
|
||||
|
||||
## Last Violation
|
||||
|
||||
If you have recently tried and failed to sign a transaction, the
|
||||
reason for the transaction being rejected can be viewed and cleared,
|
||||
using menu item "Last Violation". It is shown only if a Spending
|
||||
Policy violation (attempt) has occurred since the last valid signing.
|
||||
|
||||
This is meant as a debugging tool, and the information stored is
|
||||
terse.
|
||||
|
||||
## Remove Policy
|
||||
|
||||
This will remove your spending policy completely and remove
|
||||
the Bypass PIN. Your COLDCARD will be back to normal.
|
||||
|
||||
## Test Drive
|
||||
|
||||
Experiment with how the COLDCARD will function if the Spending
|
||||
Policy was enabled. You can try to sign transactions that should
|
||||
be rejected and view the menus in the new mode without rebooting.
|
||||
|
||||
Choose "EXIT TEST DRIVE" on top menu to return to the Spending
|
||||
Policy menu. Reboot will also restore normal operation without
|
||||
any special challenges.
|
||||
|
||||
## ACTIVATE
|
||||
|
||||
This step will enable the Spending Policy and return to the
|
||||
main menu with it in effect. When you reboot the COLDCARD,
|
||||
the policy will still be in effect. You must use the
|
||||
Bypass PIN, followed by the normal main PIN, possibly
|
||||
followed by entering the first and last words of your seed
|
||||
phrase, before you can disable and change the policy.
|
||||
|
||||
We recommend test-driving the feature before doing that.
|
||||
|
||||
|
||||
# Tips and Tricks
|
||||
|
||||
## Money Manager Mode
|
||||
|
||||
You could setup a Coldcard for another person, perhaps a family member,
|
||||
and enable web 2FA authentication. There does not need to be any
|
||||
other spending policy limits (velocity could be unlimited).
|
||||
|
||||
Then enroll your own phone with the required 2FA values, and
|
||||
keep both that and the spending policy bypass PIN confidential.
|
||||
|
||||
The holder the the Coldcard will need a 2FA code from your phone
|
||||
when they want to spend. They can call you for the 6-digit code
|
||||
from the 2FA app on your phone. This is not hard to provide over a
|
||||
voice call.
|
||||
|
||||
Because a spending policy is in effect, they will not be able to
|
||||
see the seed words, other private key material, so regardless of
|
||||
any spoofing or phishing, they cannot move funds without your help.
|
||||
|
||||
You should record the bypass PIN, so it can be revealed somehow,
|
||||
should you die. You do not need to share the risks associated with
|
||||
holding a copy of the seed words.
|
||||
|
||||
## Passphrase Considerations
|
||||
|
||||
If you are using the same BIP-39 passphrase for everything, you should
|
||||
probably do a "Lock Down Seed" (Advanced/Tools > Danger Zone > Seed
|
||||
Functions) first. This takes your master seed and BIP-39 passphrase
|
||||
and cooks them together into an XPRV which then is stored as your
|
||||
master secret. (Replacing the master seed phrase.) This process
|
||||
cannot be reversed, so other funds you may have on the same seed
|
||||
words are protected. Once you are operating in XPRV mode, you can
|
||||
define a spending policy, and know that it is restricted to only
|
||||
that wallet.
|
||||
|
||||
When operating in XPRV mode, the "Passphrase" menu item is not shown
|
||||
because BIP-39 passwords cannot be applied to XPRV secrets.
|
||||
|
||||
## Trick PIN Thoughts
|
||||
|
||||
When doing your game theory w.r.t to bypass mode and this feature,
|
||||
remember that you should assume the attacker already has your main
|
||||
PIN. That's how they know they cannot spend all your coin, because
|
||||
they either tried to, or noticed the menus are very limited. They also
|
||||
have all your UTXO locations and total wallet balance (because they
|
||||
can export your xpubs to any wallet and load balance from there).
|
||||
|
||||
Therefore, a trick pin that leads to a duress wallet after giving up
|
||||
the bypass unlock PIN, will not fool them. Best would be to provide
|
||||
a false bypass PIN that is in fact a brick/wipe PIN.
|
||||
|
||||
|
||||
## Lock Out Changes to Policy
|
||||
|
||||
In the Trick Pin menu once Spending Policy has been enabled, you will
|
||||
find the Bypass PIN listed. You could delete or "hide" it. Hiding
|
||||
it is pointless since you cannot get to the trick PIN menu while
|
||||
the policy is in effect. Deleting the PIN however, is useful because
|
||||
it assures changes to spending policy are impossible. To recover
|
||||
the COLDCARD when this move is later regretted, under Advanced,
|
||||
there is "Destroy Seed" option which will clear the seed words and
|
||||
all settings, including the spending policy.
|
||||
|
||||
### Unlock Policy & Wipe
|
||||
|
||||
We've provided a new trick PIN that pretends to be the unlock
|
||||
spending policy pin, so the login sequence is correct... but it
|
||||
will wipe the seed in the process. It will be obvious to your
|
||||
attackers that you've wiped the seed because the main PIN will lead
|
||||
to blank wallet now (no seed loaded).
|
||||
|
||||
### Delta Mode and Spending Policy
|
||||
|
||||
If, from the start, you gave your "delta mode PIN" to the attackers,
|
||||
then when they bypass the policy (after also getting the bypass PIN
|
||||
from you), they will still be in Delta Mode.
|
||||
|
||||
They could attempt unlimited spending, but transactions signed will
|
||||
not be valid. If they try to view the seed words or generally export
|
||||
private key material, they will hit many of the "wipe seed if delta
|
||||
mode" cases.
|
||||
|
||||
## Forgotten Bypass PIN Code
|
||||
|
||||
If you've enabled a spending policy and still remember the main PIN,
|
||||
but cannot disable the feature because you've forgotten the Bypass
|
||||
PIN, your only option is to use `Advanced > Destroy Seed`. After
|
||||
some confirmations, this erases the master seed, all settings, seed
|
||||
vault items, secure notes, and trick pins. It's basically a factory
|
||||
reset except for the main PIN code which is unchanged. Once you've
|
||||
done that, you can enter your seed words from backup (or restore a
|
||||
backup file) and continue to use the COLDCARD again.
|
||||
|
||||
|
||||
66
docs/taproot.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Taproot
|
||||
|
||||
**COLDCARD<sup>®</sup>** Mk4 experimental `EDGE` versions
|
||||
support Schnorr signatures ([BIP-0340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki)),
|
||||
Taproot ([BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki))
|
||||
and Tapscript ([BIP-0342](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki)) support.
|
||||
|
||||
## Output script (a.k.a address) generation
|
||||
|
||||
If the spending conditions do not require a script path, the output key MUST commit to an unspendable script path.
|
||||
`Q = P + int(hashTapTweak(bytes(P)))G` a.k.a internal key MUST be tweaked by `TapTweak` tagged hash of itself. If
|
||||
the spending conditions require script path, internal key MUST be tweaked by `TapTweak` tagged hash of tree merkle root.
|
||||
|
||||
Addresses in `Address Explorer` for `p2tr` are generated with above-mentioned methods. Outputs `scriptPubkeys` in PSBT
|
||||
MUST be generated with above-mentoned methods to be considered change.
|
||||
|
||||
## Allowed descriptors
|
||||
|
||||
1. Single signature wallet without script path: `tr(key)`
|
||||
2. Tapscript multisig with internal key and up to 8 leaf scripts:
|
||||
* `tr(internal_key, sortedmulti_a(2,@0,@1))`
|
||||
* `tr(internal_key, pk(@0))`
|
||||
* `tr(internal_key, {sortedmulti_a(2,@0,@1),pk(@2)})`
|
||||
* `tr(internal_key, {or_d(pk(@0),and_v(v:pkh(@1),older(1000))),pk(@2)})`
|
||||
|
||||
## Provably unspendable internal key
|
||||
|
||||
There are few methods to provide/generate provably unspendable internal key, if users wish to only use script path
|
||||
for multisig.
|
||||
|
||||
1. use provably unspendable internal key H from [BIP-0341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs). This way is leaking the information that key path spending is not possible and therefore not recommended privacy-wise.
|
||||
|
||||
`tr(50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0, sortedmulti_a(2,@0,@1))`
|
||||
|
||||
2. use COLDCARD specific placeholder `@` to let HWW pick a fresh integer r in the range 0...n-1 uniformly at random and use `H + rG` as internal key. COLDCARD will not store r and therefore user is not able to prove to other party how the key was generated and whether it is actually unspendable.
|
||||
|
||||
`tr(r=@, sortedmulti_a(MofN))`
|
||||
|
||||
3. pick a fresh integer r in the range 0...n-1 uniformly at random yourself and provide that in the descriptor. COLDCARD generates internal key with `H + rG`. It is possible to prove to other party that this internal key does not have a known discrete logarithm with respect to G by revealing r to a verifier who can then reconstruct how the internal key was created.
|
||||
|
||||
`tr(r=77ec0c0fdb9733e6a3c753b1374c4a465cba80dff52fc196972640a26dd08b76, sortedmulti_a(2,@0,@1))`
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
### Tapscript Limitations
|
||||
|
||||
In current version only `TREE` of max depth 4 is allowed (max 8 leaf script allowed).
|
||||
Taproot single leaf multisig has artificial limit of max 32 signers (M=N=32).
|
||||
Number of keys in taptree is limited to 32.
|
||||
|
||||
If Coldcard can sign by both key path and script path - key path has precedence.
|
||||
|
||||
### PSBT Requirements
|
||||
|
||||
PSBT provider MUST provide following Taproot specific input fields in PSBT:
|
||||
1. `PSBT_IN_TAP_BIP32_DERIVATION` with all the necessary keys with their leaf hashes and derivation (including XFP). Internal key has to be specified here with empty leaf hashes.
|
||||
2. `PSBT_IN_TAP_INTERNAL_KEY` MUST match internal key provided in `PSBT_IN_TAP_BIP32_DERIVATION`
|
||||
3. `PSBT_IN_TAP_MERKLE_ROOT` MUST be empty if there is no script path. Otherwise it MUST match what Coldcard can calculate from registered descriptor.
|
||||
4. `PSBT_IN_TAP_LEAF_SCRIPT` MUST be specified if there is a script path. Currently MUST be of length 1 (only one script allowed)
|
||||
|
||||
PSBT provider MUST provide following Taproot specific output fields in PSBT:
|
||||
1. `PSBT_OUT_TAP_BIP32_DERIVATION` with all the necessary keys with their leaf hashes and derivation (including XFP). Internal key has to be specified here with empty leaf hashes.
|
||||
2. `PSBT_OUT_TAP_INTERNAL_KEY` must match internal key provided in `PSBT_OUT_TAP_BIP32_DERIVATION`
|
||||
3. `PSBT_OUT_TAP_TREE` with depth, leaf version and script defined. Currently only one script is allowed.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Temporary Seeds
|
||||
|
||||
|
||||
[_(new in v5.0.7, requires Mk4, Mk5, or Q)_](upgrade.md)
|
||||
[_(new in v5.0.7, requires Mk4)_](upgrade.md)
|
||||
|
||||
|
||||
Temporary seed (renamed in `5.2.0` from Ephemeral seed) is a temporary secret completely separate
|
||||
@ -42,7 +42,7 @@ Read more about `Seed Vault` feature below.
|
||||
- `24 words`
|
||||
- `XPRV (BIP-32)`
|
||||
- pick derivation `Index` in next prompt, or just press OK for index 0
|
||||
- Press (0) in next prompt to activate derived secret as a temporary seed
|
||||
- Press (2) in next prompt to activate derived secret as a temporary seed
|
||||
|
||||
* temporary seed can be activated from Duress Wallet
|
||||
- go to `Settings -> Login Settings -> Trick Pins`
|
||||
@ -66,7 +66,7 @@ Ability to generate and use **Temporary seed** is available on Coldcard when:
|
||||
|
||||
# Restore Master
|
||||
|
||||
[_(new in v5.2.0, requires Mk4, Mk5, or Q)_](upgrade.md)
|
||||
[_(new in v5.2.0, requires Mk4)_](upgrade.md)
|
||||
|
||||
From version `5.2.0` users no longer need to reboot COLDCARD to return
|
||||
to their "master seed" (one stored in SE2). Once COLDCARD has temporary
|
||||
@ -84,7 +84,7 @@ Seed Vault entries can only be deleted in Seed Vault menu.
|
||||
|
||||
# Seed Vault
|
||||
|
||||
[_(new in v5.2.0, requires Mk4, Mk5, or Q)_](upgrade.md)
|
||||
[_(new in v5.2.0, requires Mk4)_](upgrade.md)
|
||||
|
||||
Seed Vault adds the ability to store multiple temporary secrets into encrypted settings for simple
|
||||
recall and later use (AES-256-CTR encrypted with your master seed's key).
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
|
||||
# Firmware Upgrade and Recovery Process
|
||||
|
||||
_This document applies to the Mk4, Mk5, and Q. Earlier COLDCARDs did not use this approach._
|
||||
_This document applies only to the Mk4. Earlier COLDCARDs did not use this approach._
|
||||
|
||||
On the COLDCARD, we have done away with the slow external SPI flash
|
||||
(serial flash) chip entirely (used in Mk1-Mk3). In it's place we
|
||||
use a much faster and huge 64 Mbit PSRAM chip (quad SPI RAM chip:
|
||||
ESP-PSRAM64H).
|
||||
On the new Mk4 COLDCARD, we have done away with the slow external
|
||||
SPI flash (serial flash) chip entirely. In it's place we use a much
|
||||
faster and huge 64 Mbit PSRAM chip (quad SPI RAM chip: ESP-PSRAM64H).
|
||||
|
||||
This chip is volatile and forgets its contents at power down.
|
||||
|
||||
@ -15,7 +14,7 @@ can be a problem during firmware upgrades. This document explains
|
||||
how we've solved the risks of firmware upgrades and possible bricking
|
||||
that can happen with power fails at just the wrong time.
|
||||
|
||||
## Firmware Upgrade Process
|
||||
## Firmware Upgrade Process on Mk4
|
||||
|
||||
Steps:
|
||||
|
||||
@ -27,7 +26,7 @@ Steps:
|
||||
- a checksum is calculated over the new firmware, and the current contents of
|
||||
flash, including the bootloader code, its secrets, unique identity bits
|
||||
(for the main chip). We call this the "world checksum".
|
||||
- before anything else happens, we update the main secure element (608C) with
|
||||
- before anything else happens, we update the main secure element (608B) with
|
||||
the world checksum, and during boot, knowledge of the world checksum is required
|
||||
to light the green genuine light.
|
||||
- the light stays green at this point, and the system could still boot the old firmware
|
||||
@ -80,7 +79,7 @@ to main flash. The PSRAM will forget it's contents, and the COLDCARD
|
||||
no longer has a complete copy of firmware anywhere.
|
||||
|
||||
Most products would be a "brick" at this point, and the docs would
|
||||
warn against power fails during upgrade. However, the COLCARD can read
|
||||
warn against power fails during upgrade. However, the Mk4 can read
|
||||
SD Cards to load replacement firmware. The card does not need to
|
||||
be specially prepared, but we recommend erasing it, formating with
|
||||
FAT32 and then copying just the firmware onto the card.
|
||||
@ -92,7 +91,7 @@ Once a card is inserted, a search is made for a suitable firmware file.
|
||||
All DFU files will be considered, but you must provide the firmware
|
||||
file that you were attempting to upgrade to during the power failure,
|
||||
because the "world checksum" is calculated for each image found on
|
||||
the card. You will not be able to substitute a newer version of firmware.
|
||||
the card. You will not be able to substitue a newer version of firmware.
|
||||
Of course, firmware factory signatures are checked as well.
|
||||
|
||||
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
# Web 2FA Authentication
|
||||
|
||||
How to support [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238)
|
||||
TOTP (Time based One Time Password) 2FA check, on our little embedded
|
||||
device without a real-time clock?
|
||||
|
||||
Solution: Store the pre-shared secret in the COLDCARD, and send that
|
||||
securely to a trusted webserver which knows the time and can do a
|
||||
fancy UX. That webserver accepts the time-based-one-time 2FA numeric
|
||||
code from the user, and if correct, reveals a secret
|
||||
that can be used back on the COLDCARD to authorize an action.
|
||||
|
||||
For the Mk4, the secret is 8 digit numeric code to be entered,
|
||||
for the COLDCARD Q, it is a QR code to be scanned.
|
||||
|
||||
### History / Background
|
||||
|
||||
The HSM feature uses HOTP tokens, which do not require a backend,
|
||||
but are not as robust as time-based tokens.
|
||||
|
||||
Web2FA is available to be enabled as part of a Spending Policy,
|
||||
both in Multisig and Single Signer modes. When enabled, you will be
|
||||
prompted complete 2FA authentication after viewing the details of
|
||||
the transaction to be signed. You will not be able to sign without
|
||||
the correct code.
|
||||
|
||||
## How It Works
|
||||
|
||||
- Web backend has a ECC keypair, with pubkey known to CC firmware releases.
|
||||
- Usual 2fa base32 secret is picked by CC and stored in CC (so that server is stateless)
|
||||
- CC creates URL encrypted to the pubkey of server, containing args:
|
||||
- shared secret for TOTP (same value as held in user's phone)
|
||||
- the response nonce (32 bytes, shown as 64 hex chars, on Q; or 8 digits on Mk4)
|
||||
to be revealed to the user on successful auth
|
||||
- flag if Q model, so can provide a QR to be scanned in that case (rather than digits)
|
||||
- some text label for what's being approved, which is presented to user so they can pick
|
||||
correct 2fa shared secret.
|
||||
- above is all encrypted in transit, and only the server can decrypt
|
||||
- user is sent to that encrypted URL using NFC tap on the COLDCARD
|
||||
- user arrives at server:
|
||||
- shown label [which also indicates the server can be trusted, since only it could decrypt it]
|
||||
- prompt for 6 digits from authenticator app
|
||||
- does [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238) 2FA check using current time
|
||||
- checks using current time and the shared secret provided by CC, fails if wrong.
|
||||
- time based failure: offer retry (they typed too slow / minor clock drift)
|
||||
- can offer to retry, but also do some rate limiting (only one attempt per 30-sec period)
|
||||
- server will store very recent responses so attacker cannot get two codes
|
||||
in any 30sec period (ie. blocks immediate reuse of same URL)
|
||||
- until a valid code is given, user is stuck here
|
||||
- when valid token received:
|
||||
- if Q, show a QR code to be scanned, with the full nonce
|
||||
- for non-Q system, a 8-digit decimal value is given: user has to enter that into the COLDCARD
|
||||
- web site shows instructions about what to do next on product.
|
||||
|
||||
## From COLDCARD PoV
|
||||
|
||||
- makes complex encrypted URL, which contains a nonce it wants, waits for that nonce back (or QR)
|
||||
- it's either the nonce from the URL, or fail
|
||||
- if the right nonce, then we know the server knows the decryption key, and we
|
||||
are trusting it actually verify the 2FA token properly.
|
||||
|
||||
## Encryption - Simple ECDH
|
||||
|
||||
- CC picks a secp256k1 keypair, generates compressed pubkey
|
||||
- multiplies that private key by server's known public key
|
||||
- apply sha256(resulting coordinate) => the session key
|
||||
- apply AES-256-CTR over URL contents (ascii text)
|
||||
- prepend 33 bytes of pubkey, and then base64url encode all of it
|
||||
- full url is: `https://coldcard.com/2fa?{base64 encoded binary}`
|
||||
|
||||
## Trust Issues
|
||||
|
||||
- 2FA enrol happens on the CC, which picks the shared secret and shows QR for mobile
|
||||
app setup. Same TRNG process as picking a seed.
|
||||
- Server knows the shared secret, but only during operation, and we won't store it [sorry,
|
||||
gotta trust us on that, but no help to us to store it].
|
||||
- Only we can run the server, because the private key is company-secret.
|
||||
- MiTM and network snoopers get nothing because HTTPS is used and only your browser
|
||||
can see the nonce, and only after you've given the right digits.
|
||||
- Coinkite server could skip the 2FA checks and just give you the answer
|
||||
you want to type into the COLDCARD. Again, you have to trust us on that.
|
||||
|
||||
## URL Format
|
||||
|
||||
https://coldcard.com/2fa?g={nonce}&ss={shared_secret}&nm={label_text}&q={is_q}
|
||||
|
||||
(the query string is then encrypted to the server's pubkey, so the args above
|
||||
are what is inside the encrypted payload.)
|
||||
|
||||
- `nonce`: text string that is either 8 digits on Mk4, or 64 hex chars on Q
|
||||
- `shared_secret`: 16 chars of Base32-encoded pre-shared secret
|
||||
- `nm`: human readable label for the transaction/purpose
|
||||
- `is_q`: flag indicating use of QR to provide nonce back to user
|
||||
|
||||
Server will accept plaintext arguments as above, but normally everything
|
||||
after the question mark is encrypted.
|
||||
|
||||
2
external/ckcc-protocol
vendored
@ -1 +1 @@
|
||||
Subproject commit 3d1dfa858beb58b8dac37d8c66d7aed2909812f2
|
||||
Subproject commit 11c711e929a090ec29ccd2a05d094aa3d2cbc113
|
||||
2
external/libngu
vendored
@ -1 +1 @@
|
||||
Subproject commit 537519a829259622ea6b0334fbafd6cae852852f
|
||||
Subproject commit 7bdb03864630ff68b143e3e5b4521ca3ef6588cc
|
||||
2
external/micropython
vendored
@ -1 +1 @@
|
||||
Subproject commit 4107246f8a080807b62c3b4838e71e812ea68b6f
|
||||
Subproject commit abf88c98b6ee9897b6fcc8ffea0276f07447dd48
|
||||
2
external/mpy-qr
vendored
@ -1 +1 @@
|
||||
Subproject commit 11347d83f4eb325b10676a4eb8e17deccfe0df44
|
||||
Subproject commit 3ccf19ca142e9059904f0c8e53b6baeccb9c6b79
|
||||
@ -1,16 +1,15 @@
|
||||
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
|
||||
all: graphics_mk4.py graphics_q1.py
|
||||
all: graphics.py graphics_mk4.py
|
||||
|
||||
MK4_SOURCES = $(wildcard mono/*.txt) $(wildcard mono/*.png)
|
||||
Q1_SOURCES = colour/*.???
|
||||
SOURCES = $(filter-out mk4_%, $(wildcard *.txt) $(wildcard *.png))
|
||||
MK4_SOURCES = $(wildcard mk4_*.txt) $(wildcard mk4_*.png)
|
||||
|
||||
graphics.py: Makefile $(SOURCES) build.py
|
||||
./build.py graphics.py $(SOURCES)
|
||||
|
||||
graphics_mk4.py: Makefile $(MK4_SOURCES) build.py
|
||||
./build.py graphics_mk4.py $(MK4_SOURCES)
|
||||
|
||||
graphics_q1.py: Makefile $(Q1_SOURCES) compress.py
|
||||
./compress.py graphics_q1.py $(Q1_SOURCES)
|
||||
|
||||
up: all
|
||||
(cd ../shared; make up)
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ def read_text(fname):
|
||||
def read_img(fn):
|
||||
img = Image.open(fn)
|
||||
w,h = img.size
|
||||
assert 1 <= w < 128, (w, fn)
|
||||
assert 1 <= w < 128, w
|
||||
|
||||
img = img.convert('L')
|
||||
# fix colour issues: assume minority colour is white (1)
|
||||
@ -88,7 +88,7 @@ class Graphics:
|
||||
assert img.mode == '1'
|
||||
#img.show()
|
||||
|
||||
varname = fn.split('/')[-1].split('.')[0].replace('-', '_')
|
||||
varname = fn.split('.')[0].replace('-', '_')
|
||||
|
||||
w,h = img.size
|
||||
raw = img.tobytes()
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Read in PNG (or even JPG) and output heavily compressed RGB565 data suited to Q1's LCD panel.
|
||||
#
|
||||
# - also renders status bar icons/indicators
|
||||
#
|
||||
import os, sys, pdb
|
||||
from PIL import Image, ImageOps, ImageFont, ImageDraw
|
||||
import zlib
|
||||
from struct import pack
|
||||
|
||||
WBITS = -10
|
||||
|
||||
FONT_PATH = './fonts/'
|
||||
|
||||
def read_img(fn):
|
||||
img = Image.open(fn)
|
||||
w,h = img.size
|
||||
assert 1 <= w <= 320, f'too wide; {w}'
|
||||
assert 1 <= h <= 240, f'too tall: {h}'
|
||||
|
||||
img = img.convert('RGB')
|
||||
|
||||
# maybe: quantitize to a reasonable num colours, so compression
|
||||
# can work better?
|
||||
|
||||
return img
|
||||
|
||||
def compress(n, wbits=WBITS):
|
||||
# NOTE: neg wbits implies no zlib header, and receiver may need to know it?
|
||||
z = zlib.compressobj(wbits=wbits, level=zlib.Z_BEST_COMPRESSION)
|
||||
rv = z.compress(n)
|
||||
rv += z.flush(zlib.Z_FINISH)
|
||||
return rv
|
||||
|
||||
def crunch(n):
|
||||
# try them all... not finding any difference tho.
|
||||
a = [(wb,compress(n, wb)) for wb in range(-9, -15, -1)]
|
||||
|
||||
a.sort(key=lambda i: (-len(i[1]), -i[0]))
|
||||
|
||||
print("Wbit values:")
|
||||
print('\n'.join("%3d => %d" % (wb,len(d)) for wb,d in a))
|
||||
|
||||
return a[0]
|
||||
|
||||
# LCD Display wants RGB565 values, but big endian, so green gets split weird.
|
||||
def swizzle(r,g,b):
|
||||
# from 0-255 per component => two bytes
|
||||
b = (b >> 3)
|
||||
g = (g >> 3) # should be >> 2 for 6 bits; but looks trash?
|
||||
r = (r >> 3)
|
||||
|
||||
return pack('>H', ((r<<11) | (g<<6) | b))
|
||||
|
||||
# these values tested on real hardware
|
||||
assert swizzle(255, 0, 0) == b'\xf8\x00' # red
|
||||
##assert swizzle(0, 255, 0) == b'\xc0\x0f' # green (6 bits)
|
||||
assert swizzle(0, 255, 0) == b'\x07\xc0' # green (5 bits)
|
||||
assert swizzle(0, 0, 255) == b'\x00\x1f' # blue
|
||||
|
||||
|
||||
def into_bgr565(img):
|
||||
# get the raw bytes needed for this specific display
|
||||
rv = bytearray()
|
||||
for y in range(img.height):
|
||||
for x in range(img.width):
|
||||
px = img.getpixel((x, y))
|
||||
assert len(px) == 3
|
||||
r,g,b = px
|
||||
rv.extend(swizzle(r,g,b))
|
||||
|
||||
return rv
|
||||
|
||||
def make_icons():
|
||||
# return list of (varname, img) for each image
|
||||
|
||||
# - see shared/lcd_display.py TOP_MARGIN for this
|
||||
ICON_SIZE = 14
|
||||
MAX_HEIGHT = 14
|
||||
|
||||
# PROBLEM: this file costs money... altho free version looks okay too
|
||||
try:
|
||||
awesome = ImageFont.truetype(FONT_PATH + 'Font Awesome 6 Sharp-Regular-400.otf', ICON_SIZE)
|
||||
except:
|
||||
raise
|
||||
|
||||
# use a bitmap font for best readability
|
||||
sm_font = ImageFont.load('ter-powerline-x12b.pil')
|
||||
|
||||
targets = [
|
||||
#( 'brand', True, 'Q', dict(col='#ffb000') ),
|
||||
( 'shift', True, 'SHIFT', {} ),
|
||||
( 'symbol', True, 'SYM', {} ),
|
||||
( 'caps', True, 'CAPS', {} ),
|
||||
( 'bip39', True, 'PASSPHRASE', dict(col_1='yellow') ),
|
||||
( 'tmp', True, 'TMP.SEED', dict(col_0='black', col_1='red') ),
|
||||
( 'devmode', True, 'DEV', dict(col='#66E6FF') ),
|
||||
( 'edge', True, 'EDGE', dict(col='#66E6FF') ),
|
||||
( 'bat_0', False, '\uf244', dict(col='red', y=-1, pad=1)),
|
||||
( 'bat_1', False, '\uf243', dict(col='yellow', y=-1, pad=1)),
|
||||
( 'bat_2', False, '\uf242', dict(col='amber', y=-1, pad=1)),
|
||||
( 'bat_3', False, '\uf240', dict(col='amber', y=-1, pad=1)),
|
||||
( 'plugged', False, '\uf1e6', dict(col='amber', x=3, w=16, y=-2)), # to match width of bat_*
|
||||
#( 'locked', False, '\uf023', dict(col='green')),
|
||||
#( 'unlocked', False, '\uf3c1', dict(col='green')), # why tho?
|
||||
]
|
||||
|
||||
targets += [ ( 'ch_'+c, True, c.upper(), dict(col='white') ) for c in
|
||||
'0123456789abcdef']
|
||||
|
||||
samples = Image.new('RGB', (320*3, ICON_SIZE+1))
|
||||
s_x = 5
|
||||
|
||||
for basename, is_text, body, opts in targets:
|
||||
for state in [0, 1]:
|
||||
col = opts.get('col', '#fff' if state else '#444')
|
||||
vn = f'{basename}_{state}'
|
||||
|
||||
if 'col' in opts:
|
||||
if state == 0: continue
|
||||
vn = basename
|
||||
|
||||
if state == 0 and 'col_0' in opts:
|
||||
col = opts['col_0']
|
||||
if state == 1 and 'col_1' in opts:
|
||||
col = opts['col_1']
|
||||
|
||||
img = Image.new('RGB', (100,100))
|
||||
d = ImageDraw.Draw(img)
|
||||
f = sm_font if is_text else awesome
|
||||
|
||||
|
||||
x, y = (0, 1 if is_text else 0)
|
||||
y += opts.get('y', 0)
|
||||
x += opts.get('x', 0)
|
||||
|
||||
tl = (x, y)
|
||||
_,_, w,h = d.textbbox(tl, body, font=f)
|
||||
|
||||
w = opts.get('w', w)
|
||||
|
||||
if h > MAX_HEIGHT:
|
||||
h = MAX_HEIGHT
|
||||
print(f'"{vn}" too tall, cropped')
|
||||
elif opts.get('pad'):
|
||||
h = MAX_HEIGHT
|
||||
|
||||
if col == 'amber':
|
||||
# brand colour
|
||||
col = '#ffb000'
|
||||
|
||||
d.text(tl, body, font=f, fill=col)
|
||||
rv = img.crop( (0, 0, w,h) )
|
||||
|
||||
samples.paste(rv, (s_x, 0))
|
||||
s_x += w + 10
|
||||
|
||||
yield (vn, rv)
|
||||
|
||||
samples = samples.crop( (0,0, s_x, samples.height ))
|
||||
samples.save('icon-samples.png')
|
||||
|
||||
|
||||
|
||||
def doit(outfname, fnames):
|
||||
|
||||
assert outfname.endswith('.py')
|
||||
assert outfname != 'compress.py'
|
||||
assert fnames, "need some files"
|
||||
|
||||
fp = open(outfname, 'wt')
|
||||
|
||||
fp.write("""\
|
||||
# autogenerated; don't edit
|
||||
#
|
||||
# BGR565 pixel data
|
||||
#
|
||||
class Graphics:
|
||||
# (w,h, data)
|
||||
|
||||
""")
|
||||
|
||||
fnames += make_icons()
|
||||
|
||||
for fn in fnames:
|
||||
if isinstance(fn, str):
|
||||
img = read_img(fn)
|
||||
varname = fn.split('/')[-1].split('.')[0].replace('-', '_')
|
||||
else:
|
||||
varname, img = fn
|
||||
|
||||
assert img.mode == 'RGB'
|
||||
|
||||
w,h = img.size
|
||||
raw = into_bgr565(img)
|
||||
comp = compress(raw)
|
||||
#crunch(raw)
|
||||
|
||||
print(" %s = (%d, %d,\n %r\n )\n" % (varname, w, h, comp), file=fp)
|
||||
|
||||
print("done: '%s' (%d x %d) => %d raw => %d compressed bytes" % (
|
||||
varname, w, h, len(raw), len(comp)))
|
||||
|
||||
fp.write("\n# EOF\n")
|
||||
|
||||
if 1:
|
||||
doit(sys.argv[1], sys.argv[2:])
|
||||
|
||||
# EOF
|
||||
@ -1,7 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Generate some data for hsm_ux.py animation
|
||||
#
|
||||
from math import sin, pi
|
||||
from collections import Counter
|
||||
|
||||
|
||||
3
graphics/fonts/.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
Font Awesome 6*.otf
|
||||
iosevka-*.ttf
|
||||
!iosevka-heavy.ttf
|
||||
@ -1,11 +0,0 @@
|
||||
|
||||
# Fonts for Q1
|
||||
|
||||
This directory may contain font files from Font Awesome.
|
||||
|
||||
We cannot re-distribute the OTF files themselves due to their license.
|
||||
|
||||
However, once we render and build the compressed graphics file that we need,
|
||||
the font is not required anymore.
|
||||
|
||||
Iosveka is open and can be re-distributed here.
|
||||
@ -1,18 +0,0 @@
|
||||
Font Awesome Pro License
|
||||
------------------------
|
||||
|
||||
Font Awesome Pro is commercial software that requires a paid license. Full
|
||||
Font Awesome Pro license: https://fontawesome.com/license.
|
||||
|
||||
# Commercial License
|
||||
The Font Awesome Pro commercial license allows you to pay for FA Pro once, own
|
||||
it, and use it just about everywhere you'd like.
|
||||
|
||||
# Attribution
|
||||
Attribution is not required by the Font Awesome Pro commercial license.
|
||||
|
||||
# Brand Icons
|
||||
All brand icons are trademarks of their respective owners. The use of these
|
||||
trademarks does not indicate endorsement of the trademark holder by Font
|
||||
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
|
||||
to represent the company, product, or service to which they refer.**
|
||||
27
graphics/graphics.py
Normal file
@ -0,0 +1,27 @@
|
||||
# autogenerated; don't edit
|
||||
#
|
||||
class Graphics:
|
||||
# (w,h, w_bytes, wbits, data)
|
||||
|
||||
arrow_down = (7, 11, 1, 0, b'\x10\x10\x10\x10\x10\x10\x10\xfe|8\x10')
|
||||
|
||||
arrow_up = (7, 11, 1, 0, b'\x108|\xfe\x10\x10\x10\x10\x10\x10\x10')
|
||||
|
||||
box = (13, 21, 2, 0, b'?\xe0@\x10\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08@\x10?\xe0')
|
||||
|
||||
scroll = (3, 61, 1, 0, b'@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@@\xe0@')
|
||||
|
||||
selected = (15, 12, 2, 0, b'\x00\x00\x00\x00\x00\x06\x00\x0c\x00\x18\x0000`\x18\xc0\r\x80\x07\x00\x02\x00\x00\x00')
|
||||
|
||||
sm_box = (11, 17, 2, 0, b'\xe4\xe0\x80 \x80 \x80 \x00\x00\x00\x00\x80 \x00\x00\x00\x00\x00\x00\x80 \x00\x00\x00\x00\x80 \x80 \x80 \xe4\xe0')
|
||||
|
||||
space = (9, 2, 2, 0, b'\x80\x80\xff\x80')
|
||||
|
||||
spin = (13, 36, 2, 0, b'\x02\x00\x07\x00\x0f\x80\x1f\xc0\x00\x00\x00\x00\x00\x00\xf2x\x80\x08\x80\x08\x80\x08\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x80\x08\x80\x08\x80\x08\xf2x\x00\x00\x00\x00\x00\x00\x1f\xc0\x0f\x80\x07\x00\x02\x00\x00\x00')
|
||||
|
||||
wedge = (6, 11, 1, 0, b'\x00\x00\xc0\xe0p8\x1c8p\xe0\xc0')
|
||||
|
||||
xbox = (13, 21, 2, 0, b'?\xe0b0\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88b0?\xe0')
|
||||
|
||||
|
||||
# EOF
|
||||
@ -3,12 +3,6 @@
|
||||
class Graphics:
|
||||
# (w,h, w_bytes, wbits, data)
|
||||
|
||||
arrow_down = (7, 11, 1, 0, b'\x10\x10\x10\x10\x10\x10\x10\xfe|8\x10')
|
||||
|
||||
arrow_up = (7, 11, 1, 0, b'\x108|\xfe\x10\x10\x10\x10\x10\x10\x10')
|
||||
|
||||
box = (13, 21, 2, 0, b'?\xe0@\x10\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08\x80\x08@\x10?\xe0')
|
||||
|
||||
mk4_nfc_1 = (126, 49, 16, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\xfe\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\xfe\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
mk4_nfc_2 = (118, 49, 15, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
@ -17,27 +11,5 @@ class Graphics:
|
||||
|
||||
mk4_nfc_4 = (102, 49, 13, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xcf\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x03\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\x8f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
mk5_nfc_1 = (126, 49, 16, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff\xfe0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff\xfe0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\xfe\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\xfe\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
mk5_nfc_2 = (118, 49, 15, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\xe0\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\xe0\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\xe0\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe00\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00\xe0?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
mk5_nfc_3 = (110, 49, 14, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x00\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x00\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x00\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xff0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e\x000\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x000\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e\x000\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xff0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x000\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e\x00?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\xff\x0f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
mk5_nfc_4 = (102, 49, 13, 0, b'\x00\x7f\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf0\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x0e\x03\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\xe0\x0e\x07\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xe0\x0e\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 G\xe3\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xe0\x0e0\x000D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00(D\x04\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xff\xff\xb0\x00$G\xe4\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00"D\x04\x00\x00\x00xp\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00 \xc4\x04\x00\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x04\x10\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xff\xff\xb0\x00 D\x03\xe0\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e0\x00\x00\x00\x00\x00\x00\x00\x7f\xf0\x00\xe0\x0e?\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\xff\xff\x9f\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\xff\xff\x8f\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x7f\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
scroll = (3, 61, 1, 0, b'@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@\x00\x00@@\xe0@')
|
||||
|
||||
selected = (9, 12, 2, 0, b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x00\x03\x00\x82\x00\xc6\x00d\x00<\x00\x18\x00\x00\x00')
|
||||
|
||||
sm_box = (11, 17, 2, 0, b'\xe4\xe0\x80 \x80 \x80 \x00\x00\x00\x00\x80 \x00\x00\x00\x00\x00\x00\x80 \x00\x00\x00\x00\x80 \x80 \x80 \xe4\xe0')
|
||||
|
||||
space = (9, 2, 2, 0, b'\x80\x80\xff\x80')
|
||||
|
||||
spin = (13, 36, 2, 0, b'\x02\x00\x07\x00\x0f\x80\x1f\xc0\x00\x00\x00\x00\x00\x00\xf2x\x80\x08\x80\x08\x80\x08\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x00\x00\x80\x08\x00\x00\x00\x00\x80\x08\x80\x08\x80\x08\xf2x\x00\x00\x00\x00\x00\x00\x1f\xc0\x0f\x80\x07\x00\x02\x00\x00\x00')
|
||||
|
||||
wedge = (6, 11, 1, 0, b'\x00\x00\xc0\xe0p8\x1c8p\xe0\xc0')
|
||||
|
||||
xbox = (13, 21, 2, 0, b'?\xe0b0\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88\xa2(\x88\x88b0?\xe0')
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@ -1,49 +0,0 @@
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxx xxx xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
@ -1,49 +0,0 @@
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxx xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxx xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxx xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
@ -1,49 +0,0 @@
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
@ -1,49 +0,0 @@
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxx xxxx xxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxx xxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxx
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy yyyyyyyyyyy
|
||||
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx yy n n ffffff ccccc yyyyyyyyyyy
|
||||
xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxx xxx yy nn n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n n f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n n f c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxxxxxxxxxxxxxxxx yy n n n ffffff c yyyy yyy
|
||||
xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx yy n n n f c yyyy yyy
|
||||
xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxx xxx yy n nn f c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n f c c yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yy n n f ccccc yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yy yyyyyyyyyyy
|
||||
xxx xxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
xxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
@ -1,12 +0,0 @@
|
||||
|
||||
|
||||
X
|
||||
XX
|
||||
X
|
||||
XX
|
||||
X X
|
||||
XX XX
|
||||
XX X
|
||||
XXXX
|
||||
XX
|
||||
|
||||
12
graphics/selected.txt
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
xx
|
||||
xx
|
||||
xx
|
||||
xx
|
||||
xx xx
|
||||
xx xx
|
||||
xx xx
|
||||
xxx
|
||||
x
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,3 +1,4 @@
|
||||
|
||||
# Coldcard Hardware Details
|
||||
|
||||
This directory contains enough information for you to be able to
|
||||
@ -5,20 +6,9 @@ build your own Coldcard from off-the-shelf parts.
|
||||
We are sharing this information for the benefit of security
|
||||
researchers who wish to analyse the Coldcard more completely.
|
||||
|
||||
|
||||
# Schematic
|
||||
|
||||

|
||||
|
||||
`schematic-q1d.png`
|
||||
|
||||
This is the Q rev D schematic.
|
||||
|
||||

|
||||
|
||||
`schematic-mark5f.png`
|
||||
|
||||
This is the Mark4 rev F schematic.
|
||||
|
||||

|
||||
|
||||
`schematic-mark4d.png`
|
||||
@ -34,13 +24,11 @@ This is the Mark3 rev B schematic.
|
||||
|
||||
# BOM - Bill of Materials
|
||||
|
||||
The parts used in the Coldcard are detailed in these spreadsheets.
|
||||
Most of them could be bought on Digikey, but some are direct from suppliers.
|
||||
`bom-mark3b.xlsx`
|
||||
|
||||
- BOM for Q rev D: `bom-q1d.xlsx`
|
||||
- BOM for Mk5 rev F: `bom-mark5f.xlsx`
|
||||
- BOM for Mk4 rev D: `bom-mark4d.xlsx`
|
||||
- BOM for Mk3 rev B: `bom-mark3b.xlsx`
|
||||
The parts used in the Coldcard are detailed in this spreadsheet file.
|
||||
All of them could be bought on Digikey, and where we know
|
||||
it, we've included the Digikey SKU.
|
||||
|
||||
Not included are these minor bits:
|
||||
|
||||
@ -48,10 +36,14 @@ Not included are these minor bits:
|
||||
- the secure bag (with barcode serial number)
|
||||
- pin-recovery card
|
||||
|
||||
`bom-mark4d.xlsx`
|
||||
|
||||
- Same for Mk4 rev D.
|
||||
|
||||
# Important
|
||||
|
||||
- No promises that these files are 100% current because we constantly make quality improvements.
|
||||
- No promises that these files are 100% current because we do make quality improvements.
|
||||
- Copyright of these files, and all design elements of the Coldcard remain with Coinkite Inc.
|
||||
- This information is for research and testing purposes only—no warranties.
|
||||
- **Coinkite does NOT grant license of this information for comercial use.**
|
||||
- **Coinkite does not grant license of this information for comercial use.**
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 428 KiB |
|
Before Width: | Height: | Size: 796 KiB |
@ -1,28 +1,26 @@
|
||||
diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile
|
||||
index 971f2f81a..0c25a11e0 100644
|
||||
index 971f2f81a..b175c4dc7 100644
|
||||
--- a/mpy-cross/Makefile
|
||||
+++ b/mpy-cross/Makefile
|
||||
@@ -17,7 +17,8 @@ INC += -I$(BUILD)
|
||||
@@ -17,7 +17,7 @@ INC += -I$(BUILD)
|
||||
INC += -I$(TOP)
|
||||
|
||||
# compiler settings
|
||||
-CWARN = -Wall -Werror
|
||||
+CWARN = -Wall -Werror -Wno-error=unused-but-set-variable -Wno-error=array-bounds
|
||||
+CWARN += -Wno-error=unknown-warning-option
|
||||
CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith
|
||||
CFLAGS = $(INC) $(CWARN) -std=gnu99 $(CFLAGS_MOD) $(COPT) $(CFLAGS_EXTRA)
|
||||
CFLAGS += -fdata-sections -ffunction-sections -fno-asynchronous-unwind-tables
|
||||
diff --git a/ports/unix/Makefile b/ports/unix/Makefile
|
||||
index 6a936a242..43e6bf02a 100644
|
||||
index 6a936a242..6b4900561 100644
|
||||
--- a/ports/unix/Makefile
|
||||
+++ b/ports/unix/Makefile
|
||||
@@ -38,7 +38,8 @@ INC += -I$(TOP)
|
||||
@@ -38,7 +38,7 @@ INC += -I$(TOP)
|
||||
INC += -I$(BUILD)
|
||||
|
||||
# compiler settings
|
||||
-CWARN = -Wall -Werror
|
||||
+CWARN = -Wall -Werror -Wno-error=unused-but-set-variable -Wno-error=array-bounds
|
||||
+CWARN += -Wno-error=unknown-warning-option -Wno-error=deprecated-non-prototype -Wno-error=bitwise-instead-of-logical
|
||||
CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion
|
||||
CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(CFLAGS_MOD) $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA)
|
||||
|
||||
|
||||
@ -74,9 +74,4 @@ special_chars = dict(small=[
|
||||
x x x x x
|
||||
'''),
|
||||
|
||||
# thin space
|
||||
('\u2009', dict(y=0, w=5), '''\
|
||||
|
||||
'''),
|
||||
|
||||
])
|
||||
|
||||
10
misc/gpu/.gitignore
vendored
@ -1,10 +0,0 @@
|
||||
|
||||
# build products, see Makefile:
|
||||
gpu.lss
|
||||
gpu.sym
|
||||
gpu.bin.tmp
|
||||
|
||||
checksums.txt
|
||||
version-full.txt
|
||||
version.txt
|
||||
|
||||
@ -1,184 +0,0 @@
|
||||
# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Makefile for Q1's GPU co-processor.
|
||||
#
|
||||
# Targets:
|
||||
# all - make everything, look for dafu.elf inparticular
|
||||
# clean - delete intermediates
|
||||
# clobber - delete all build products
|
||||
#
|
||||
|
||||
# Toolchain
|
||||
TOOLCHAIN = arm-none-eabi-
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
OBJDUMP = $(TOOLCHAIN)objdump
|
||||
OBJCOPY = $(TOOLCHAIN)objcopy
|
||||
NM = $(TOOLCHAIN)nm
|
||||
SIZE = $(TOOLCHAIN)size
|
||||
|
||||
# Basename of all targets
|
||||
TARGET_NAME = gpu
|
||||
|
||||
# Source files, listed here as the object files they will become.
|
||||
OBJS += startup.o
|
||||
OBJS += main.o lcd.o version.o interrupts.o
|
||||
OBJS += stm32c0xx_ll_gpio.o stm32c0xx_ll_spi.o stm32c0xx_ll_i2c.o stm32c0xx_ll_utils.o
|
||||
|
||||
# Have to have copies of these because the DMA and interrupt stuff
|
||||
# needs to be commented-out.
|
||||
#OBJS += stm32l4xx_hal_gpio.o stm32l4xx_hal_spi.o
|
||||
#OBJS += stm32l4xx_hal_rcc.o stm32l4xx_hal_rcc_ex.o
|
||||
|
||||
# Where we will end up in the memory map (at start of flash)
|
||||
GPU_FLASH_BASE = 0x08000000
|
||||
GPU_FLASH_SIZE = 0x4000
|
||||
GPU_FLASH_LAST = 0x08004000
|
||||
|
||||
# Use all of 6k of SRAM...
|
||||
GPU_SRAM_BASE = 0x20000000
|
||||
GPU_SRAM_SIZE = 0x00001800
|
||||
|
||||
# Compiler flags.
|
||||
CFLAGS = -I. -Wall --std=gnu99 -Os -g3 \
|
||||
-mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=soft -mtune=cortex-m0 \
|
||||
-ffunction-sections -fdata-sections \
|
||||
-mcpu=cortex-m0 -DMCU_SERIES_C0 -DSTM32C011xx \
|
||||
-DUSE_FULL_LL_DRIVER
|
||||
#-DUSE_HAL_DRIVER
|
||||
|
||||
# Pass in the locations of stuff
|
||||
CFLAGS += -D GPU_FLASH_BASE=$(GPU_FLASH_BASE) -D GPU_FLASH_SIZE=$(GPU_FLASH_SIZE)
|
||||
CFLAGS += -D GPU_SRAM_BASE=$(GPU_SRAM_BASE) -D GPU_SRAM_SIZE=$(GPU_SRAM_SIZE)
|
||||
|
||||
# Header file search path
|
||||
INC_PATHS = external/cmsis_device_c0/Include \
|
||||
external/stm32c0xx_hal_driver/Inc \
|
||||
../../external/micropython/lib/cmsis/inc
|
||||
|
||||
CFLAGS += $(foreach INC,$(INC_PATHS),-I$(INC))
|
||||
|
||||
# Specialized linker-script here. Not the standard one!
|
||||
#
|
||||
LINKER_SCRIPT = link-script.ld
|
||||
LDFLAGS += -Wl,-T$(LINKER_SCRIPT)
|
||||
|
||||
LDFLAGS += -flto -Wl,--gc-sections --specs=nano.specs
|
||||
LDFLAGS += -Wl,--defsym,GPU_FLASH_BASE=$(GPU_FLASH_BASE)
|
||||
LDFLAGS += -Wl,--defsym,GPU_FLASH_SIZE=$(GPU_FLASH_SIZE)
|
||||
LDFLAGS += -Wl,--defsym,GPU_SRAM_BASE=$(GPU_SRAM_BASE)
|
||||
LDFLAGS += -Wl,--defsym,GPU_SRAM_SIZE=$(GPU_SRAM_SIZE)
|
||||
LDFLAGS += -Wl,-Map=$(TARGET_NAME).map
|
||||
|
||||
ASFLAGS += -Wa,--defsym,GPU_FLASH_BASE=$(GPU_FLASH_BASE) -Wa,--defsym,GPU_FLASH_SIZE=$(GPU_FLASH_SIZE)
|
||||
ASFLAGS += -Wa,--defsym,GPU_SRAM_BASE=$(GPU_SRAM_BASE) -Wa,--defsym,GPU_SRAM_SIZE=$(GPU_SRAM_SIZE)
|
||||
|
||||
|
||||
TARGET_ELF = $(TARGET_NAME).elf
|
||||
TARGETS = $(TARGET_NAME).lss $(TARGET_NAME).bin $(TARGET_NAME).sym gpu_binary.py
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
# recompile on any Makefile change, because with a small project like this...
|
||||
$(OBJS): Makefile
|
||||
$(TARGETS): $(TARGET_ELF) Makefile
|
||||
|
||||
# link step
|
||||
$(TARGET_ELF): $(OBJS) $(LINKER_SCRIPT) Makefile
|
||||
$(CC) $(CFLAGS) -o $(TARGET_ELF) $(LDFLAGS) $(OBJS)
|
||||
$(SIZE) -Ax $@
|
||||
|
||||
# detailed listing, very handy
|
||||
%.lss: $(TARGET_ELF)
|
||||
$(OBJDUMP) -h -S $< > $@
|
||||
|
||||
# symbol dump, meh
|
||||
%.sym: $(TARGET_ELF)
|
||||
$(NM) -n $< > $@
|
||||
|
||||
# raw binary, forced to right size, pad w/ 0xff
|
||||
%.bin: $(TARGET_ELF)
|
||||
$(OBJCOPY) -O binary --gap-fill 0xff $< $@
|
||||
|
||||
# assumes openocd running from current directory
|
||||
up:
|
||||
echo 'flash write_image $(TARGET_ELF)' | nc localhost 4444
|
||||
|
||||
# make a 'release' build
|
||||
release: code-committed clean all capture
|
||||
release: CFLAGS += -DRELEASE=1 -Werror
|
||||
|
||||
.PHONY: code-committed
|
||||
code-committed:
|
||||
@echo ""
|
||||
@echo "Are all changes commited already?"
|
||||
git diff --stat --exit-code .
|
||||
@echo '... yes'
|
||||
|
||||
# these files are what we capture and store for each release.
|
||||
DELIVERABLES = $(TARGET_NAME).bin $(TARGET_NAME).lss gpu_binary.py
|
||||
|
||||
# package the binary into a mpy file to be frozen/included into main micro code
|
||||
gpu_binary.py: version.txt $(TARGET_NAME).bin repackage.py
|
||||
./repackage.py `cat version.txt` $(TARGET_NAME).bin > $@
|
||||
wc -c $(TARGET_NAME).bin
|
||||
|
||||
checksums.txt: $(DELIVERABLES)
|
||||
shasum -a 256 $(DELIVERABLES) > $@
|
||||
|
||||
lcd.o: barcode.h
|
||||
barcode.h: make_barcode.py Makefile
|
||||
python3 make_barcode.py
|
||||
|
||||
# Track released versions
|
||||
.PHONY: capture
|
||||
capture: version.txt version-full.txt $(DELIVERABLES) checksums.txt
|
||||
V=`cat version.txt` && cat checksums.txt > releases/$$V.txt && cat version-full.txt >> releases/$$V.txt && mkdir -p releases/$$V; cp $(DELIVERABLES) releases/$$V
|
||||
@echo
|
||||
@echo " Version: " `cat version.txt`
|
||||
@echo
|
||||
V=`cat version.txt` && git tag -am "Q1 GPU version $$V" "q1-gpu-"$$V
|
||||
git add -f releases/*/gpu.* releases/*.txt releases/*/*.py
|
||||
|
||||
# Pull out the version string from binary object (already linked in) and
|
||||
# construct a text file (version.txt) with those contents
|
||||
version.txt version-full.txt: version.o Makefile
|
||||
$(OBJCOPY) -O binary -j .rodata.version_string version.o version-tmp.txt
|
||||
cat version-tmp.txt | sed -e 's/ .*//' | sed -e 's/ .*//' > version.txt
|
||||
cat version-tmp.txt | tr '\0' '\n' > version-full.txt
|
||||
@echo
|
||||
@echo "Version string: " `cat version-full.txt`
|
||||
@echo
|
||||
$(RM) version-tmp.txt
|
||||
|
||||
# nice version numbers.
|
||||
BUILD_TIME = $(shell date '+%Y%m%d.%H%M%S')
|
||||
BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
|
||||
SHA_VERSION = $(shell git rev-parse --short HEAD)
|
||||
GIT_HASH = "$(BRANCH)@$(SHA_VERSION)"
|
||||
version.o: CFLAGS += -DBUILD_TIME='"$(BUILD_TIME)"' -DGIT_HASH='$(GIT_HASH)'
|
||||
version.o main.o: Makefile version.h
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS)
|
||||
|
||||
clobber: clean
|
||||
$(RM) $(TARGETS)
|
||||
|
||||
# In another window:
|
||||
#
|
||||
# openocd-stm -s /usr/local/Cellar/open-ocd/0.12.0/share/openocd/scripts -f openocd-gpu.cfg
|
||||
#
|
||||
# Can do:
|
||||
# - "load" which writes the flash (medium speed, lots of output on st-util)
|
||||
# - "cont" starts/continues system
|
||||
# - "br main" sets breakpoints
|
||||
# - "mon reset" to reset micro
|
||||
# - and so on
|
||||
#
|
||||
debug:
|
||||
arm-none-eabi-gdb $(TARGET_ELF) -x gogo.gdb
|
||||
|
||||
tags:
|
||||
ctags -f .tags *.[ch] -R $(INC_PATHS)
|
||||
|
||||
# EOF
|
||||
@ -1,97 +0,0 @@
|
||||
# GPU on Q1
|
||||
|
||||
The name is a joke. It's not a GPU, just a very simple and cheap micro that can
|
||||
animate a progress bar. And that's all we want it to do.
|
||||
|
||||
It is field upgradable, but we will remove that and start locking it down in
|
||||
production once it's features are stable.
|
||||
|
||||
|
||||
## Hardware
|
||||
|
||||
It's a STM32C011F4:
|
||||
|
||||
- 16k bytes of Flash
|
||||
- 6k bytes of RAM
|
||||
- 4-48Mhz
|
||||
- 18 GPIO
|
||||
- 20 pins
|
||||
- a newer part, so some challenges there
|
||||
|
||||
Of the two TagConnect spots, the GPU is the inboard one; other is for main micro.
|
||||
|
||||
## OpenOCD
|
||||
|
||||
Version 0.12.0 of OpenOCD, the latest release as of this writing, does not yet support this chip.
|
||||
|
||||
You'll need to compile from ST Micro's fork of OpenOCD. In particular we need
|
||||
this diff:
|
||||
<https://github.com/STMicroelectronics/OpenOCD/commit/21c81a2b2edf5402afbba8c22feaeda6f626554e>
|
||||
|
||||
I am using brew's install of normal 0.12.0 for config files, and
|
||||
a compiled version named `openocd-stm`, so my command line is:
|
||||
|
||||
openocd-stm -s /usr/local/Cellar/open-ocd/0.12.0/share/openocd/scripts -f openocd-gpu.cfg
|
||||
|
||||
Useful commands:
|
||||
|
||||
flash erase_sector 0 0 last
|
||||
|
||||
Set EMPTY bit, so goes into BL:
|
||||
|
||||
> mdw 0x40022000
|
||||
0x40022000: 00040600
|
||||
> mmw 0x40022000 0x10000 0
|
||||
> mdw 0x40022000
|
||||
0x40022000: 00050600
|
||||
|
||||
|
||||
|
||||
## In-Circuit Programming
|
||||
|
||||
- AN4221 describes the protocol used to load the flash
|
||||
- timing is sensitive, but more important is where the i2c start/stops fall:
|
||||
|
||||
## First Time Boot
|
||||
|
||||
- on a fresh device, there is an `EMPTY` bit set on power-up (only) if flash looks empty
|
||||
- this causes bootmode to happen, regardless of subsequent flash contents, resets, and `BOOT0` line
|
||||
- so must clear bit 16 of `FLASH_ACR` after loading image: @ `0x40022000`
|
||||
- also, main micro has control over `BOOT0` (PE2) which stop main flash from running too
|
||||
- and the reset line on E6
|
||||
- we use this flag to get into boot mode from working code
|
||||
|
||||
## Getting BOOT0 to work
|
||||
|
||||
- default config in flash bit (option bytes) is to
|
||||
- see `FLASH_OPTR` bit: `nBOOT_SEL` (bit 24) needs to be zero, default is one
|
||||
- `NRST_MODE` should be 0b01 (input only) not default (0b11 = bidirectional)
|
||||
- register `FLASH_OPTR` at 0x40022020 => found as 0xfffffeaa
|
||||
- loads from 0x1FFF7800 at power up
|
||||
- TODO XXX still need this!
|
||||
|
||||
## AN4221 / Bootloader Bugs
|
||||
|
||||
- command 0x00 - 'get' ... returns 19 bytes, but says v1.1 of protocol; clearly v1.2
|
||||
- command 0x02 - 'getid' ... returns 1 byte, but math wrong on length part of response
|
||||
- command 0x01 - 'getversion' ... return 1 byte, and doesn't include length prefix byte
|
||||
- memory read only works from flash, some parts of SRAM... not IO registers
|
||||
- undocumented need for N-1 as length in read/write commands
|
||||
- flash writes need to be 256-aligned, or else they do nothing and don't fail
|
||||
|
||||
## Resource Sharing
|
||||
|
||||
- SPI bus to the display is shared by the GPU and main micro.
|
||||
- Main micro configures its pins as pull-up, open-drain outputs (there is no input except TEAR).
|
||||
- GPU does the same, but no pull-ups (so open drain).
|
||||
- Later turns out we need push-pull I/O to get the SPI speeds involved (rise times too slow
|
||||
with built-in resistors)
|
||||
|
||||
## Other References
|
||||
|
||||
- Ideas, not code: <https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/341cd894516f747f14108de5da593dad99900ae0/tools/macosx/src/stm32flash_serial/src/stm32.c>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
// autogen file, see make_barcode.py
|
||||
|
||||
// in python: b'\x00\x00\x00\x00\x00\x00\x00?\x1c\x0e\x00\x1f\x8e\x00\xe0\x0f\xf8\xff\x8f\xc7\xe3\xfe?\xe3\xf1\xf8\xff\xf1\xf8\x03\xfe8\xfc\x00\x00\x00\x00\x00\x00\x00'
|
||||
static const uint8_t test_barcode[40] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x1c, 0x0e, 0x00, 0x1f, 0x8e, 0x00, 0xe0, 0x0f, 0xf8, 0xff, 0x8f, 0xc7, 0xe3, 0xfe, 0x3f, 0xe3, 0xf1, 0xf8, 0xff, 0xf1, 0xf8, 0x03, 0xfe, 0x38, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// EOF
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern void fatal_error(const char *) __attribute__((noreturn));
|
||||
|
||||
// I don't like the ususal assert macro, so here is my replacement,
|
||||
// and BTW: "assert()" is actually a macro, so it should
|
||||
// always be capitalized.
|
||||
//
|
||||
#undef assert
|
||||
#undef ASSERT
|
||||
|
||||
// this does have an impact, but probably doesn't matter.
|
||||
#define __unlikely(x) __builtin_expect((x),0)
|
||||
|
||||
#define ASSERT(x) do { if(__unlikely(!(x))) { fatal_error("assert");} } while(0)
|
||||
|
||||
// Use anywhere. Will just crash on production, but useful in dev.
|
||||
#ifndef RELEASE
|
||||
#define BREAKPOINT asm("BKPT #0")
|
||||
#else
|
||||
#define BREAKPOINT #error
|
||||
#endif
|
||||
|
||||
// An assertion that we will be checked at *compile* time. Useful GCC feature.
|
||||
#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
|
||||
|
||||
// Similarly: for those times when you want to write ASSERT(False),
|
||||
// use this instead to provide a msg and abort. Altho the msg isn't
|
||||
// in the binary, it's still helpful when looking at source via debugger.
|
||||
//
|
||||
// CAUTION: some security checks end up here, so we want to always crash
|
||||
// in those cases.
|
||||
//
|
||||
#define INCONSISTENT(x) fatal_error("incon")
|
||||
|
||||
// Wait for an interrupt which will never happen (ie. die)
|
||||
#define LOCKUP_FOREVER() while(1) { __WFI(); }
|
||||
|
||||
// Like "sizeof()" but works on arrays, and returns the "numberof" elements.
|
||||
//
|
||||
#define numberof(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
// This is an old favourite with dangerous programers...
|
||||
//
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
#define msizeof(TYPE, MEMBER) sizeof(((TYPE *)0)->MEMBER)
|
||||
|
||||
// Handy macros.
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define CLAMP(x,mn,mx) (((x)>(mx))?(mx):( ((x)<(mn)) ? (mn) : (x)))
|
||||
#define SGN(x) (((x)<0)?-1:(((x)>0)?1:0))
|
||||
#define ABS(x) (((x)<0)?-(x):(x))
|
||||
|
||||
1
misc/gpu/external/cmsis_device_c0
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 7e32bf9d8117ee4c8f6a1d138b814fc24bf4c906
|
||||
1
misc/gpu/external/stm32c0xx_hal_driver
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 4c5e3e45a8478a33decb1f45d663f485f89c459b
|
||||
@ -1,22 +0,0 @@
|
||||
add-symbol-file gpu.elf 0x08000000
|
||||
|
||||
# hex for all numbers
|
||||
set output-radix 16
|
||||
|
||||
# kill X repeats N times, which interfere w/ cut-n-paste into python of dumps
|
||||
set print repeats 128
|
||||
|
||||
# Use ST-Link (st-utils)
|
||||
#target extended-remote :4242
|
||||
|
||||
# Connect to the OpenOCD gdb server (needs to be already running & connected)
|
||||
target extended-remote :3333
|
||||
|
||||
define reset
|
||||
mon reset init
|
||||
end
|
||||
|
||||
define wipe_chip
|
||||
mon flash erase_sector 0 0 last
|
||||
mon reset halt
|
||||
end
|
||||
@ -1,118 +0,0 @@
|
||||
# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# Binary for Q1 GPU co-processor
|
||||
#
|
||||
# see misc/gpu for source
|
||||
#
|
||||
VERSION = '1.3.3'
|
||||
|
||||
LENGTH = const(3536) # bytes (after decompression)
|
||||
|
||||
# len(BINARY) = 2618
|
||||
BINARY = (b"x\x9c\x8dW\x7fl\x13\xd7\x1d\xff\xbe\xbbsb;\x8188\xd08?\xcf~\t\x04\x9c\x10'"
|
||||
b'GZ\ne\x1c>j\x12\x1f\xa4\x14J\x051\xdd\x9c\x1f\x853\xa1\xeb\xad\xe9*k\xfdc.'
|
||||
b'\xa8]\xc76\xdabZ\xc0\x866\x90\xfeB\xedF\x16\xc9\x9b\xd6\x11\x95\xb5\xdb\xaa'
|
||||
b'\xb5j\xeb#\xad\xc6\x08ZS\xe8duSWSM\xb5\x81\xc6\xb7\xef\x99P9\x12\x9af\xeb'
|
||||
b'\xeb\xf7\xfd\xf5\xbe\xdf\xcf}\xef\xbd\xef{\x86J\xe0-\x04\xcc\xab,`^\x8d\x04'
|
||||
b"\xff\xe3\xb3\xa6\xc0.!\x7f'\xd2\x16r]g\x8c7\xa3\xff\xc7v#\xe6\xcd\xfc"
|
||||
b'o\xa6/\xc4dK\x14\xad\xa7ap\x17k&\x19\xdcL\x92\xeb\x84)x\x93P\xaa\xda\xc6\xd1'
|
||||
b'\x917|\xfe8\x07\xcc\x9clK\x80\x9bM\xb2]\xd7=l\xe3g|\x86\xad\x14}\x0c{i'
|
||||
b'\xe7\xe3>H\x1f\xbc\x02Nv\xca*\x07\xb6J\x1b\xb96K\xa7U\xf6V\xc6\xbc_OX\xfc'
|
||||
b'\xcc\x14P{\x90k/\x96c\xde+\x13\x90\xfe\xd3\x95\\\n*\xf3\xf1\xf3d\x9e'
|
||||
b'\xa1\xb5Hh\x81t\x824\xfet\xb4\xc2\x85\xf1^\x85\xf4\xb9\xe92y\xb8\xa1g'
|
||||
b'\x90Y\xb4D\xea\xe9\x0f\x0c~fr\x8b$J\xe2\xab)s\x92\x87\xc8B&\xca'
|
||||
b"\x1d\x82\xf4\x85k@\xbf&<0'\x99\xe7\xd8g\xb9CEQH\x9f\xbd\xf6r\x9fe4="
|
||||
b'\x0e6F\xb4%\x98\x06^vv\xf5<\xf0\xe4\xa8U\xec\xb9\xbfg\x10\x9c\xae|'
|
||||
b'\xbcR\x17\x15!\x8a\x99\xe2\x90>1}\x86\x92\xe8\x9b+\xb9(9i\x12\x16t'
|
||||
b'\x16E\x99\x83\x90\xfe\xd5\xb5\x1a\xc4B\x10\xcb\x81\xe9y\xb2}=\x13'
|
||||
b'}\x97\xf2\x80\x1e\x98\xa3m:B\xfb\x948g\x97\xfa\x83\x97\x949~\xbbx\t'
|
||||
b'\xc7\xd2\xfcH\x95\x12\x1c)\x8e\xd6\xfch\x1e\xb5\xe5\xb1\xe8\x1f\xea:'
|
||||
b'p\xb0\xd1\xce\xd9\x00\xee\x01\x91du]\xff1\x92\x9eA\x9a\xd6#\x94\xf5\x07\x98'
|
||||
b'r\xa5D\xb2\x07\xd5uXk\x9e\x11\xcd4\x93\xe8_?\xe0?\x1erxm\xc9OC%R\xc4y\xa9w'
|
||||
b'$\xd4\xc5\x94\x88\x0e\xe8\xac{lM\xb9\nn\xaam\xef*Q\r\xb2\x87\x8f\x87Ht\x9b\\'
|
||||
b"\x1f\xde&\xd7\x86y\xea\xf0\xb6'/\x86\xec\xd2\xa5^\x12\x07w\xb3vO\xb7\xa0"
|
||||
b"\xf6\xb7nK\x96\x9d\xebm\xbd?\xd9\xd7*'\xefm\xadHn\x947\xfa\xab\x82"
|
||||
b'f\xea\x9d\xa2\xca]]%\x88\xf9|\xea\xe1\xd6\xef&\xfd\xf2#\xadBRm\xfd'
|
||||
b'\xb7\xe6\xf7w\xa8\xe81\x05-\xdb\xb5\xce\xce\xb5re\x10\xd2e\xd3\xc4\xe3Q\t'
|
||||
b'=\x81\xf1Gz\xc1\xc5\xd1r8\xea#\xfc\x0e\xdfHH\xf0n\xd0`1I\xde*\xd7\xab\xb0'
|
||||
b'\x84$;\xe4ZU_\x04\x8d\xcc\xc2\xd3>\xa1sY\x17\x1b\xb6*\xe4\x05h]\xafA\x0bI'
|
||||
b'\xb2\xaa5\x98\x19\x87\x96\xa5Z;\xe2(\xa2\x82:\x91\x82\x96E\x9a'
|
||||
b"\x9e\xf9\xa1\xde*7\xf9\xedA\x96\xfe'\xc5\xb6\xd4iM\xfef\xb9<|D\xbd;\xfc7\xd5"
|
||||
b'\xe4<\x10\xeeQ\xab\xc2\xfb\xa4*\xf5\xf7\xe8_\xa6\x11W\x83\xec\x08[Q\xbeO'
|
||||
b'\x1dCM\x91\xc6:y\xd9\xf0(\x07\xc6\xf5Uj\x81\\\x8b\xf1-\xf4\x83T\xff'
|
||||
b'\xae\xc91h-N\x16\xb7\xbcr\xce\xde\xbd\xabfh\xcb\xc7c\x0c\x1cO\xd5\x03'
|
||||
b'\x81\xe7S;}\xaf\xf4\x9dJ\xbd\x12\xba\xcd{*\t\xeeLr~\x98\xac|\xa9'
|
||||
b'\x8f\xb4\x93c\x93c#)\xe3\xdd-\xc55\xdb\x82\xd4\x80\xb4\x0c\xc9\x83\xb4\r\xf7'
|
||||
b'\x84\xae\xe7\xf4F\xe4\x0f"\xbf\x11\xe9^\xa4;P\xee\xc31\xe2\xaa\x92\xe7\x99b'
|
||||
b'\xc1\x88\xcb!o\x02\x87\x828\x91?\x8b\\\xb9\xf7\x9bd\x8fR\xd1U.\xa2L'
|
||||
b'\xacRO\xb0J\x99\x97\x976\x11\xc3+\xa6\x94uY\xc5bg\x0c\xb9}R,\xb8\xdcuD'
|
||||
b'1\x83M,\xf7^\xd5b\x8a\x05m\xb1\xa0E\xb6\xf8{\x82&\xd7\xe1`\x15\xae'
|
||||
b'\x1e\xf8\x02w\xac\xb1\xe6"\xb8\xb4\xdeAJ#=\xa5\xeb\xb6)\x98\xfa\x04\xc1W'
|
||||
b'\xcaC\t\xc7\xee\xca\xce\xe1\x06"9\x06\x1es\x92\x85U\xbb\xbbL\x8cH\x0e\x90'
|
||||
b'\xd8y\xdckV\xe9|\xff\xe4`\t\xee\x10\x88\xeb\x99\xf6\x1c\xa4Og\xf5\xcc\xd6'
|
||||
b'\\\xa9\x7fN\x97])\x11\xedA=\xf3\xeb\x1c\xc5\xf7|U\xe3\x1bi\xc8\xed\xfd'
|
||||
b':\t\xe9\x17\xaf\x18\xdc\xe5\xa4\x9e\x19\xc9]I\x19\xf9\x89\xcd\xd8\xe9'
|
||||
b'\x88\x03\xeb2Lm.\xb3s\xd2dK\xc4z\x8f\xf4\xc5\xfa\xce\xd0\x00\xa9\x01\xb3'
|
||||
b'\xff\x19\xc5!\x1e\xf7^K\xda\x86\x90\xf3~\xa3\x19~\x93\xa6X\xafyE\xac7\xdfs<D'
|
||||
b'\x1c\xa6\x9e\x04\xcen0\xf4G{q\xae\x0b\xa8e\xfd&r\xd8[|aD\xb1\x17\xcc\xf2\x8c'
|
||||
b"\xbf\xa4\xcc\x17\x9f\xc5x\xefo'B\xe3\xd0\x17)c\xfePb.\xd4\x02Y\xb1w,VI^?0V"
|
||||
b'm\xa5jP\xb5[\x97\xf0#\xeaE\x15\x9fEw\x02\xc7\xeb\x99\xf7t\xb2jll\x8b\xe3\x97'
|
||||
b'c\xf3\xac.\xd5\xcd/\xb1\x9ePw\xa8\xff@\x8f\xe1\x19\x8f7\xf4f\xfc=\xa2'
|
||||
b'\x0f\x8d\x0fSu6&\xfa\xc1\xd8\xa7"4\x94\xf8\x03d\x18q\xcd~\x1au\xfcY\xa5V<'
|
||||
b'\x86\xb8\x16\x0c\xdd\xe0\xca\x87\xc8\xb2\xcf\xf3\xf8\xd4D\rp\xaf\xd5=Ia>\xe8'
|
||||
b'\x99Wu\x1e\xda@\x91\xf4\xcc\xfb\xba:nK\xec\x1b\xad\x99\xe9\x8a'
|
||||
b'\x1b\xae\x9d\xa1\xd5\x0cD\xf7\xd0\x00\x90\xe8\x14\x9d\x04&\x1a\xc1J\x84'
|
||||
b'|ql\xaaFgz\xf9\xaaH]\x8a]\xa2AB9\x7fy\x18\xbb\x89j\x1d\xbdQGc-\xa6\x13\x8f'
|
||||
b'\x8fV\x00\x1bu6\xddv\xc1\xb2\xb8\xe3\x82\xa5Q\x91^\xf7\xcd\xe9\xd8 \x95\xd1'
|
||||
b'\xc50o\x91]\xfcsmY\xdb\xc5"\xf8h\xa7\x8f=\xdaWwp\r\xef\x1f\xf0V\x88\xf5'
|
||||
b'\xfe\x17+\x19w\xbd\xb6\x02<@V\xc2A=\xf3\xa2\xbe\x18\x085\xe4\x866['
|
||||
b'\x87\xa1\x89\xeb\x15\x94\xb8\x16\xa3\xc6\x90\x0e\xceHi\x07Yn\xc8?'
|
||||
b'\xd7M\xd8\x97\x89\xdb\x94d\xddW\xb4\x05\x94s\xc1\xc1\xa2)\x16;\x93I3\xcb'
|
||||
b'+\x00\xa2\x0b\xa8\x07\xfe\x99\x02\xc4\x01\xd17\xealmfO\x85p1u\xc6\xc7'
|
||||
b'\xbe\x80\x1d2\x08\x90I\xd4\xca\x9b\x9cp\xea\xd8\xda\xef\xfb\xc0U\xd4'
|
||||
b'P\xd5\xa9\xb7\xd9\x81\x8a\xf7y\x07\xd7Tn\x08xY1\xe0\xd8\x02\xa4}h\xf3'
|
||||
b'>\xefW\x9a\x89\x0e\xbb\xfe\xe5\x04>\xe0\xdd\x84U\x9d\xce\x99\x1al'
|
||||
b'\x1b\x86y\xb7\x12\x81G\x83e\xf4G\n\xb9\xe3HU\x17\xfc\xa0R\xcf|\x98\x83'
|
||||
b'\xe6\xb4\xf6\xa8b\xf1\x13\xc1.r\x1f\xdb\\d\xe5&\xaf]"\xc2\xb8\xcf\x8d\xbd'
|
||||
b'i\xeb\xb4\xae\xdf\xc0 b\r\xcb\x00"\xd9D\x85l \xd9\xed\x8b\xf0.\x00g'
|
||||
b'\x11\x94\xcb\x81\xed\xacX\x0b\x83\x88&\xe0\xb5GDs\x85F\xda\x9a\x16\xdb\xb4a'
|
||||
b'W\x98\x0e/4P\xbc\x91\x0b7\xfe\x06\x9cF\rW\xe9\x99h\x0eZ\xb2\xda\xb8'
|
||||
b'/;\xee\x01\xa6\xfd\xef\xa9\x08\xe6\xd9\x8d\xbdBMD\xe8p\xe3\x13\xa3_\x98&'
|
||||
b'I)\xbe\xfb\xd7z\x8d\xb7\xdf}\x95]XD\x99\x86\x90\xaf\t\xeb\xc3<G\xf0\x94\nd+'
|
||||
b')DoYI\xa2@\r=\xf3\x1c\x17e\xa3\xc6J\xe8\xce:\xbb\x850\xf5r\xb8\x1fO\xea'
|
||||
b'BxD\x12T!<YL>\xd63\x01\xfd\xa10,\xa9K:64+\xd0l\xd3\x84\xf0\xe5p\xa0x'
|
||||
b'\xb2l4\xacg\xbe\xcc\x11\x97\x10\xdeV\xfc\x97\xb2}\x12\xe7<\x00?\x93\xec8'
|
||||
b'\xd3\xe6\xef\xaf1*\xc3\x7f[\x19wp\xb8\xa1b\xe6\x84$W\xeb\xe8g\xa6\x90'
|
||||
b'\x0f\x0fv\xcc\\\x9e\x8d\xd0I2\xd2W4\xaa\xe2\xba3\xd6\xdcr\xa4z\xacY'
|
||||
b'.\x95K\xa9\xeb\x8co:\xf1\x93\xd1\x12\x85y\xa6\xdeE\x9c$j\x02r8`\x06w\x9d\xf6'
|
||||
b'\xb4\xc8\x0b\xd0\xc4`7\xf9\xdd\x95\xb7\xc7\xd8(\x13gc\x01\x05O'
|
||||
b'\xe6\xf8\x84\xb8@\xe3\xe2d%q\x9b\xcf\r*$F\x8eD%&vV\xf1#\xbe\xc1 \xf0'
|
||||
b'\xc5\xb8\xb6j\xf0\xed\x94\x8a\xbcDV\x04\xcc\x7fM\x11\xca\xc6\xc8\xd1\xa8Hb5'
|
||||
b'b\x95\x17\xb4\x89\xa9\x1df\x1e\x04\xa8w\xb5*\xe0V\x0br\xed5r\xb9\x02\x10'
|
||||
b'\x13y8$\x19\x1e\xe0\xdeY`\x1fB;\x173\xec\\\xfc\x16\x89\xacl\r\x12w\xc39&'
|
||||
b'\xceC\\\xa9w\x99\xa2\x02\x9c\xc2\x88[\nf\xf4\xdc$\xe2\xfa\x02'
|
||||
b'\xbb\x1f\xed\xa6|\xc4[\xa4SA#Ru\xe8z\xa4/1\xd2w\n<;n\x12\xa9\xa3\xc0'
|
||||
b'\xde\xf8m$\x03\xdb\x97A\xc6\xfd=m\xe6)\xa7\x88S\x04\x87h\xce\xd7\xe3\xf1'
|
||||
b'T\xa1\xb4\x7f\x964<K:9K\xfa\xed,\xe9\xcc,\tOG\x8d\x89?-\x06B\\\xb4'
|
||||
b'\x03\xb1|\x92-nj\xbb0\x93}\x17\xb8k\x0bpj\xd9\xb7\xc7\xca\\q\xc4YO\x0fI'
|
||||
b'\xb0t\x01\xa2<,\xf2\xf4\xba\xf5\xad\xacQ\xe1x\xbe\x1e\xad}l\x9c\x080+'
|
||||
b'\xd3\xb9\x14\xe1\x07\xc0#rX\xfdG\xcc\x9f\xa4\x16Z\x07\xac\x1e\xa0'
|
||||
b'\x98\xe7A\xccS]\x90\xe7\xd8\xec<\xcd\x15\x9a\xa7 \xcf\xfe\x82<\x0f\xf6\xbd;'
|
||||
b'+\xc7y\xcc\xf1\x08\xf0b\x11\xe6\x180\x7f\x8awRVa@M\x10>P\xd4\xfcQ\xb7R'
|
||||
b'\xa2t\x86\x04\xe9\x03e\xbe"HsC\xa5\xbb\x04i\x7f\x88\x95P\x13\x82RA\xb2uS\xa9'
|
||||
b'Ct\xe3\xedm\xb3\xf2\x0benw\x93$\x88\x95R\x84n\x0cV\x93c^\xeel\x84\xdf\xac'
|
||||
b'\x88\x84\x956\x07#\x14\xf889\xeae4yW|\xcc\xde;\x11b\xf1\xd65\x81g\x84\x08'
|
||||
b'\xba~-}\xfdJH\\\xac\xe2ID%6\xf8\x1e\xc6\xabf8\xc5-Ulh\x16)jd\xc5\xbe\x9e'
|
||||
b'\xede\x959\xa1*\x89\t\xeeU\xf6\x85\xa8\xb8\x17\xc7%\xd2\xa4u\x92\xa9\x92\xf6'
|
||||
b'\x04\xa1\x85KF\xe8\x1e\x9cg\x97\xf6\x06\x19\xc5,W\x89%\x8a\xe1-)~\xf4'
|
||||
b'\xb6K\x12\xee\x19\x0f\xeeO}\xea\xc6=\xf4C\xbd\xc6Ih5w\xd8K&\xa0\xd4v{\xb5'
|
||||
b'5\xcfY\xcd\xb7W[\xf2\x9c\x85\xbb\x9d9a\xdb.V\x1a\xf7S\x8ee\x18\x82'
|
||||
b"\x9f\x1b\xff+X\xd8S\x19\xc5]\xa6\xaes\xa8D\xc0\xf3\x9f\xbe\x85'0,\xb9"
|
||||
b'\xa6m\xad\xc3\x1b\td\x13g|\xd9\xd3\xe6\xd3\xcf\xfb\xd4u\x85|\xdbRa\xa9'
|
||||
b'\x00w\xc9\xe0\xed\x1d\xe0\xfb\x1f\x18X\x9dgz\x1f\xda9\xb4\xfaF\xe8\xd55s'
|
||||
b'\xa1~?L\x95e\xf5\xa7\xde\xb9\x94[}\xe9rV\xbf\x9ces\xcb\xbf\x99q'
|
||||
b'\xc8\xc7\xe0\x1f\x0e=p\xff\xaavO\xfb2O[\xfb\xadK\xdb<\xc2\xad\xed\xed'
|
||||
b'\xfc\xce\xd0\xc3\xab\xee\x16=\xfd;<}\xfd\x1e~\xed\x9d\xf7\xaej\xbb>\xe7s'
|
||||
b'\x00\xf3\x1f\x90\xfe\x0bJ`i=')
|
||||
|
||||
# EOF
|
||||
@ -1,107 +0,0 @@
|
||||
// from https://github.com/STMicroelectronics/STM32CubeC0/blob/main/Projects/STM32C0116-DK/Templates_LL/Src/stm32c0xx_it.c
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32c0xx_it.c
|
||||
* @brief Interrupt Service Routines.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2022 STMicroelectronics.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed under terms that can be found in the LICENSE file
|
||||
* in the root directory of this software component.
|
||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
/* USER CODE END Header */
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "main.h"
|
||||
#include "interrupts.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Cortex Processor Interruption and Exception Handlers */
|
||||
/******************************************************************************/
|
||||
/**
|
||||
* @brief This function handles Non maskable interrupt.
|
||||
*/
|
||||
void NMI_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
|
||||
|
||||
/* USER CODE END NonMaskableInt_IRQn 0 */
|
||||
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
/* USER CODE END NonMaskableInt_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Hard fault interrupt.
|
||||
*/
|
||||
void HardFault_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN HardFault_IRQn 0 */
|
||||
|
||||
/* USER CODE END HardFault_IRQn 0 */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
|
||||
/* USER CODE END W1_HardFault_IRQn 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles System service call via SWI instruction.
|
||||
*/
|
||||
void SVC_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN SVC_IRQn 0 */
|
||||
|
||||
/* USER CODE END SVC_IRQn 0 */
|
||||
/* USER CODE BEGIN SVC_IRQn 1 */
|
||||
|
||||
/* USER CODE END SVC_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Pendable request for system service.
|
||||
*/
|
||||
void PendSV_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN PendSV_IRQn 0 */
|
||||
|
||||
/* USER CODE END PendSV_IRQn 0 */
|
||||
/* USER CODE BEGIN PendSV_IRQn 1 */
|
||||
|
||||
/* USER CODE END PendSV_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles System tick timer.
|
||||
*/
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN SysTick_IRQn 0 */
|
||||
|
||||
/* USER CODE END SysTick_IRQn 0 */
|
||||
|
||||
/* USER CODE BEGIN SysTick_IRQn 1 */
|
||||
|
||||
/* USER CODE END SysTick_IRQn 1 */
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* STM32C0xx Peripheral Interrupt Handlers */
|
||||
/* Add here the Interrupt Handlers for the used peripherals. */
|
||||
/* For the available peripheral interrupt handler names, */
|
||||
/* please refer to the startup file (startup_stm32c0xx.s). */
|
||||
/******************************************************************************/
|
||||
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
/* USER CODE END 1 */
|
||||
@ -1,9 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
void NMI_Handler(void);
|
||||
void HardFault_Handler(void);
|
||||
void SVC_Handler(void);
|
||||
void PendSV_Handler(void);
|
||||
void SysTick_Handler(void);
|
||||
|
||||
450
misc/gpu/lcd.c
@ -1,450 +0,0 @@
|
||||
/*
|
||||
* (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*
|
||||
* Control the LCD.
|
||||
*
|
||||
* - see <external/stm32c0xx_hal_driver/Inc/stm32c0xx_ll_spi.h> for the API of the SPI
|
||||
*
|
||||
*/
|
||||
#include "main.h"
|
||||
#include "lcd.h"
|
||||
#include <string.h>
|
||||
#include "stm32c0xx_hal_gpio_ex.h"
|
||||
#include "barcode.h"
|
||||
|
||||
lcd_state_t lcd_state;
|
||||
|
||||
const int LCD_WIDTH = 320;
|
||||
const int LCD_HEIGHT = 240;
|
||||
const int NUM_PIXELS = (LCD_WIDTH*LCD_HEIGHT);
|
||||
|
||||
// doing RGB565, but swab16
|
||||
const uint16_t COL_BLACK = 0;
|
||||
const uint16_t COL_WHITE = ~0;
|
||||
const uint16_t COL_RED = 0x00f8; //SWAP16(0xf800);
|
||||
const uint16_t COL_FOREGROUND = 0x60fd; //SWAB16(0xfd60); // brand orange
|
||||
|
||||
// progress bar specs
|
||||
const uint16_t PROG_HEIGHT = 5;
|
||||
const uint16_t PROG_Y = LCD_HEIGHT - PROG_HEIGHT;
|
||||
|
||||
static const int NUM_PHASES = 16;
|
||||
|
||||
#if 0
|
||||
// memset2()
|
||||
//
|
||||
static inline void
|
||||
memset2(uint16_t *dest, uint16_t value, uint16_t byte_len)
|
||||
{
|
||||
for(; byte_len; byte_len-=2, dest++) {
|
||||
*dest = value;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void wait_vsync(void) {
|
||||
// PA5 is TEAR input: a positive pulse every 60Hz that
|
||||
// corresponds to vertical blanking time
|
||||
uint32_t timeout = 1000000;
|
||||
for(; timeout; timeout--) {
|
||||
if(LL_GPIO_IsInputPinSet(GPIOA, PIN_TEAR)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
//puts("TEAR timeout");
|
||||
}
|
||||
#endif
|
||||
|
||||
// write_byte()
|
||||
//
|
||||
static inline void
|
||||
write_byte(uint8_t b)
|
||||
{
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) == LL_SPI_TX_FIFO_FULL) {
|
||||
// wait for space
|
||||
}
|
||||
|
||||
LL_SPI_TransmitData8(SPI1, b);
|
||||
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY) {
|
||||
// wait for FIFO to drain completely
|
||||
}
|
||||
}
|
||||
|
||||
// write_bytes()
|
||||
//
|
||||
static inline void
|
||||
write_bytes(int len, const uint8_t *buf)
|
||||
{
|
||||
for(int n=0; n<len; n++, buf++) {
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) == LL_SPI_TX_FIFO_FULL) {
|
||||
// wait for space
|
||||
}
|
||||
|
||||
LL_SPI_TransmitData8(SPI1, *buf);
|
||||
}
|
||||
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY) {
|
||||
// wait for FIFO to drain completely
|
||||
}
|
||||
}
|
||||
|
||||
// write_uint16()
|
||||
//
|
||||
static inline void
|
||||
write_uint16(int count, uint16_t val)
|
||||
{
|
||||
uint8_t a = val & 0xff;
|
||||
uint8_t b = val >> 8;
|
||||
|
||||
for(int n=0; n<count; n++) {
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) == LL_SPI_TX_FIFO_FULL) {
|
||||
// wait for space
|
||||
}
|
||||
LL_SPI_TransmitData8(SPI1, a);
|
||||
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) == LL_SPI_TX_FIFO_FULL) {
|
||||
// wait for space
|
||||
}
|
||||
LL_SPI_TransmitData8(SPI1, b);
|
||||
}
|
||||
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY) {
|
||||
// wait for FIFO to drain completely
|
||||
}
|
||||
}
|
||||
|
||||
// lcd_write_cmd()
|
||||
//
|
||||
static void
|
||||
lcd_write_cmd(uint8_t cmd)
|
||||
{
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_DATA_CMD);
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_CS);
|
||||
|
||||
write_byte(cmd);
|
||||
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_DATA_CMD);
|
||||
}
|
||||
|
||||
|
||||
// lcd_write_data()
|
||||
//
|
||||
void
|
||||
lcd_write_data(int len, const uint8_t *pixels)
|
||||
{
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_DATA_CMD);
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_CS);
|
||||
|
||||
write_bytes(len, pixels);
|
||||
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
}
|
||||
|
||||
// lcd_write_constant()
|
||||
//
|
||||
void
|
||||
lcd_write_constant(int len, const uint16_t pixel)
|
||||
{
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_DATA_CMD);
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_CS);
|
||||
|
||||
write_uint16(len, pixel);
|
||||
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_CS);
|
||||
}
|
||||
|
||||
// lcd_write_cmd4()
|
||||
//
|
||||
static void
|
||||
lcd_write_cmd4(uint8_t cmd, uint16_t a, uint16_t b)
|
||||
{
|
||||
uint8_t d[4] = { (a>>8), a&0xff, (b>>8), b&0xff };
|
||||
|
||||
lcd_write_cmd(cmd);
|
||||
lcd_write_data(4, d);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// lcd_write_data1()
|
||||
//
|
||||
static void
|
||||
lcd_write_data1(uint8_t data)
|
||||
{
|
||||
lcd_write_data(1, &data);
|
||||
}
|
||||
#endif
|
||||
|
||||
// lcd_spi_setup()
|
||||
//
|
||||
// Just setup SPI, do not reset display, etc.
|
||||
//
|
||||
void
|
||||
lcd_setup(void)
|
||||
{
|
||||
LL_SPI_InitTypeDef init = { 0 };
|
||||
|
||||
// see SPI_InitTypeDef
|
||||
init.TransferDirection = LL_SPI_HALF_DUPLEX_TX;
|
||||
init.Mode = LL_SPI_MODE_MASTER;
|
||||
init.DataWidth = LL_SPI_DATAWIDTH_8BIT;
|
||||
init.ClockPolarity = LL_SPI_POLARITY_LOW;
|
||||
init.ClockPhase = LL_SPI_PHASE_1EDGE;
|
||||
init.NSS = LL_SPI_NSS_SOFT;
|
||||
init.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2; // measured: 6 Mhz
|
||||
init.BitOrder = LL_SPI_MSB_FIRST;
|
||||
init.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
|
||||
|
||||
LL_SPI_Init(SPI1, &init);
|
||||
LL_SPI_Enable(SPI1);
|
||||
|
||||
// usually want the busy bar
|
||||
lcd_state.activity_bar = true;
|
||||
|
||||
#if 0
|
||||
// debug values
|
||||
lcd_state.cursor_x = 9;
|
||||
lcd_state.cursor_y = 2;
|
||||
lcd_state.outline_cursor = true;
|
||||
#endif
|
||||
#if 0
|
||||
lcd_state.dbl_wide = true;
|
||||
lcd_state.cursor_x = 16;
|
||||
lcd_state.cursor_y = 4;
|
||||
lcd_state.solid_cursor = true;
|
||||
//lcd_state.outline_cursor = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// take_control()
|
||||
//
|
||||
// Make the shared SPI bus ours. All push-pull, because we need the speed.
|
||||
// - force PIN_G_CTRL low while we are in control, so CPU knows we are actively
|
||||
// speaking to the LCD
|
||||
//
|
||||
static void
|
||||
take_control(void)
|
||||
{
|
||||
LL_GPIO_SetOutputPin(GPIOA, PIN_GPU_BUSY);
|
||||
|
||||
LL_GPIO_InitTypeDef init = {0};
|
||||
|
||||
init.Pin = SPI_PINS;
|
||||
init.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
init.Alternate = GPIO_AF0_SPI1;
|
||||
|
||||
LL_GPIO_Init(GPIOA, &init);
|
||||
|
||||
init.Pin = SPI_CTRL_PINS;
|
||||
init.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
init.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
init.Alternate = 0;
|
||||
|
||||
LL_GPIO_Init(GPIOA, &init);
|
||||
}
|
||||
|
||||
// release_control()
|
||||
//
|
||||
// Go back to being a listener only on SPI.
|
||||
//
|
||||
static void
|
||||
release_control(void)
|
||||
{
|
||||
// make all inputs again
|
||||
LL_GPIO_InitTypeDef init = {0};
|
||||
|
||||
init.Pin = SPI_PINS | SPI_CTRL_PINS;
|
||||
init.Mode = LL_GPIO_MODE_INPUT;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
|
||||
LL_GPIO_Init(GPIOA, &init);
|
||||
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_GPU_BUSY);
|
||||
}
|
||||
|
||||
// send_window()
|
||||
//
|
||||
static void
|
||||
send_window(int x, int y, int w, int h, const void *data)
|
||||
{
|
||||
// write inclusive range
|
||||
// note, MADCTL MV/MX/MY setting causes row vs. col swap here
|
||||
lcd_write_cmd4(0x2a, x, x+w-1); // CASET - Column address set range (x)
|
||||
lcd_write_cmd4(0x2b, y, y+h-1); // RASET - Row address set range (y)
|
||||
lcd_write_cmd(0x2c); // RAMWR - memory write
|
||||
|
||||
if(data) {
|
||||
// follow with data write of 2*w*h bytes
|
||||
lcd_write_data(2*w*h, (uint8_t *)data);
|
||||
}
|
||||
}
|
||||
|
||||
// send_solid()
|
||||
//
|
||||
static void
|
||||
send_solid(int x, int y, int w, int h, uint16_t pixel)
|
||||
{
|
||||
send_window(x, y, w, h, NULL);
|
||||
|
||||
lcd_write_constant(w*h, pixel);
|
||||
}
|
||||
|
||||
// cursor_draw()
|
||||
//
|
||||
void
|
||||
cursor_draw(int char_x, int char_y, uint8_t ctype, bool phase)
|
||||
{
|
||||
// see shared/lcd.py and shared/font_iosevka.py
|
||||
const int LEFT_MARGIN = 7;
|
||||
const int TOP_MARGIN = 15;
|
||||
const int CHARS_W = 34;
|
||||
const int CHARS_H = 10;
|
||||
const int CELL_W = 9;
|
||||
const int CELL_H = 22;
|
||||
|
||||
// no error reporting.. but dont die either
|
||||
if(char_x >= CHARS_W) return;
|
||||
if(char_y >= CHARS_H) return;
|
||||
|
||||
bool dbl_wide = ctype & 0x10;
|
||||
ctype &= 0xf;
|
||||
|
||||
// top left corner, just on edge of character cell
|
||||
int x = LEFT_MARGIN + (char_x * CELL_W);
|
||||
int y = TOP_MARGIN + (char_y * CELL_H);
|
||||
int cell_w = CELL_W + (dbl_wide?CELL_W:0);
|
||||
|
||||
uint16_t colour = phase ? COL_BLACK : COL_FOREGROUND;
|
||||
|
||||
if(ctype == CURSOR_OUTLINE) {
|
||||
// horz
|
||||
send_solid(x,y, cell_w, 1, colour);
|
||||
send_solid(x,y+CELL_H-1, cell_w, 1, colour);
|
||||
|
||||
// vert
|
||||
send_solid(x, y+1, 1, CELL_H-2, colour);
|
||||
send_solid(x+cell_w-1, y+1, 1, CELL_H-2, colour);
|
||||
}
|
||||
|
||||
if(ctype == CURSOR_SOLID) {
|
||||
if(!phase) {
|
||||
// solid fill -- draw first time
|
||||
send_solid(x,y, cell_w, CELL_H, COL_FOREGROUND);
|
||||
} else {
|
||||
// box shape, blank interior pixels
|
||||
send_solid(x+1,y+1, cell_w-2, CELL_H-2, COL_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
if(ctype == CURSOR_MENU) {
|
||||
// half-block
|
||||
send_solid(x,y, 4, CELL_H, colour);
|
||||
}
|
||||
}
|
||||
|
||||
// lcd_fill_solid()
|
||||
//
|
||||
void
|
||||
lcd_fill_solid(uint16_t pattern)
|
||||
{
|
||||
// whole screen
|
||||
send_window(0, 0, LCD_WIDTH, LCD_HEIGHT, NULL);
|
||||
lcd_write_constant(LCD_WIDTH*LCD_HEIGHT, pattern);
|
||||
}
|
||||
|
||||
// lcd_draw_progress()
|
||||
//
|
||||
void
|
||||
lcd_draw_progress(void)
|
||||
{
|
||||
static int phase = 0;
|
||||
|
||||
uint16_t row[LCD_WIDTH + NUM_PHASES + 1];
|
||||
|
||||
for(int i=0; i<numberof(row); i++) {
|
||||
row[i] = ((i % 8) < 2) ? COL_BLACK : COL_FOREGROUND;
|
||||
}
|
||||
|
||||
send_window(0, PROG_Y, LCD_WIDTH, PROG_Y-LCD_HEIGHT, NULL);
|
||||
|
||||
for(int y=0; y<PROG_HEIGHT; y++) {
|
||||
lcd_write_data(LCD_WIDTH*2, (uint8_t *)(&row[NUM_PHASES - phase - 1]));
|
||||
}
|
||||
|
||||
phase = (phase + 1) % NUM_PHASES;
|
||||
}
|
||||
|
||||
// lcd_animate()
|
||||
//
|
||||
// Called at LCD frame rate, when we have control over LCD.
|
||||
//
|
||||
void
|
||||
lcd_animate(void)
|
||||
{
|
||||
take_control();
|
||||
|
||||
if(lcd_state.test_pattern) {
|
||||
lcd_test_pattern();
|
||||
lcd_state.test_pattern = false;
|
||||
}
|
||||
|
||||
if(lcd_state.activity_bar) {
|
||||
lcd_draw_progress();
|
||||
}
|
||||
|
||||
if(lcd_state.cursor_type != NO_CURSOR) {
|
||||
static int cur_phase;
|
||||
|
||||
if(cur_phase == 0) {
|
||||
cursor_draw(lcd_state.cursor_x, lcd_state.cursor_y,
|
||||
lcd_state.cursor_type, lcd_state.cur_flash);
|
||||
|
||||
lcd_state.cur_flash = !lcd_state.cur_flash;
|
||||
}
|
||||
|
||||
cur_phase = (cur_phase+1) % 32;
|
||||
}
|
||||
|
||||
release_control();
|
||||
}
|
||||
|
||||
// lcd_test_pattern()
|
||||
//
|
||||
void
|
||||
lcd_test_pattern(void)
|
||||
{
|
||||
// NOTE: this is very limited so it cannot be abused to show arbitrary things
|
||||
// - take packed pixels in (blk/white)
|
||||
// - draw them centered w/ red side border
|
||||
// - repeat same pattern bunch of times.
|
||||
// - used for a linear barcode in selftest process
|
||||
// - important: this cannot render a QR code, nor misleading text.
|
||||
// - LATER: let's just make fully static instead.
|
||||
|
||||
STATIC_ASSERT(sizeof(test_barcode) == LCD_WIDTH/8);
|
||||
|
||||
uint16_t row[LCD_WIDTH];
|
||||
for(int i=0, x=0; i<sizeof(test_barcode); i++) {
|
||||
for(uint8_t m=0x80; m; m >>= 1) {
|
||||
row[x++] = (test_barcode[i] & m) ? COL_BLACK : COL_WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
const int y = 40, h = 120;
|
||||
send_window(0, y, LCD_WIDTH, h, NULL);
|
||||
|
||||
for(int i=0; i<h; i++) {
|
||||
lcd_write_data(sizeof(row), (uint8_t *)&row);
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
||||
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
static const uint8_t NO_CURSOR = 0;
|
||||
static const uint8_t CURSOR_SOLID = 0x01;
|
||||
static const uint8_t CURSOR_OUTLINE = 0x02;
|
||||
static const uint8_t CURSOR_MENU = 0x03;
|
||||
static const uint8_t CURSOR_DW_OUTLINE = 0x11;
|
||||
static const uint8_t CURSOR_DW_SOLID = 0x12;
|
||||
|
||||
|
||||
typedef struct {
|
||||
bool activity_bar:1;
|
||||
bool test_pattern:1; // self clearing
|
||||
|
||||
bool cur_flash:1; // clear when changing pos/type/enable
|
||||
uint8_t cursor_type;
|
||||
|
||||
uint8_t cursor_x, cursor_y;
|
||||
} lcd_state_t;
|
||||
|
||||
extern lcd_state_t lcd_state;
|
||||
|
||||
void lcd_setup(void);
|
||||
void lcd_animate(void);
|
||||
void lcd_test_pattern(void);
|
||||
|
||||
// EOF
|
||||
@ -1,213 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* a specialized linker script:
|
||||
* - limits itself to just part of the device
|
||||
* - assumes only a small amount of ram is available.
|
||||
* - reference: <https://sourceware.org/binutils/docs/ld/>
|
||||
* - spaces actually matter in expressions in this file
|
||||
*
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
SEARCH_DIR(.)
|
||||
|
||||
/* Memory Spaces Definitions */
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = GPU_FLASH_BASE, LENGTH = GPU_FLASH_SIZE
|
||||
RAM (rwx) : ORIGIN = GPU_SRAM_BASE, LENGTH = GPU_SRAM_SIZE
|
||||
}
|
||||
|
||||
/* from /Applications/ARM/share/gcc-arm-none-eabi/samples/ldscripts/gcc.ld */
|
||||
|
||||
/* Linker script to place sections and symbol values. Should be used together
|
||||
* with other linker script that defines memory regions FLASH and RAM.
|
||||
* It references following symbols, which must be defined in code:
|
||||
* Reset_Handler : Entry of reset handler
|
||||
*
|
||||
* It defines following symbols, which code can use without definition:
|
||||
* __exidx_start
|
||||
* __exidx_end
|
||||
* __copy_table_start__
|
||||
* __copy_table_end__
|
||||
* __zero_table_start__
|
||||
* __zero_table_end__
|
||||
* __etext
|
||||
* __data_start__
|
||||
* __preinit_array_start
|
||||
* __preinit_array_end
|
||||
* __init_array_start
|
||||
* __init_array_end
|
||||
* __fini_array_start
|
||||
* __fini_array_end
|
||||
* __data_end__
|
||||
* __bss_start__
|
||||
* __bss_end__
|
||||
* __end__
|
||||
* end
|
||||
* __HeapLimit
|
||||
* __StackLimit
|
||||
* __StackTop
|
||||
* __stack
|
||||
*/
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
KEEP(*(.isr_vector))
|
||||
*(.text*)
|
||||
|
||||
KEEP(*(.init))
|
||||
KEEP(*(.fini))
|
||||
|
||||
/* .ctors */
|
||||
*crtbegin.o(.ctors)
|
||||
*crtbegin?.o(.ctors)
|
||||
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
|
||||
*(SORT(.ctors.*))
|
||||
*(.ctors)
|
||||
|
||||
/* .dtors */
|
||||
*crtbegin.o(.dtors)
|
||||
*crtbegin?.o(.dtors)
|
||||
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
|
||||
*(SORT(.dtors.*))
|
||||
*(.dtors)
|
||||
|
||||
*(.rodata*)
|
||||
|
||||
KEEP(*(.eh_frame*))
|
||||
} > FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
} > FLASH
|
||||
|
||||
__exidx_start = .;
|
||||
.ARM.exidx :
|
||||
{
|
||||
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||
} > FLASH
|
||||
__exidx_end = .;
|
||||
|
||||
/* To copy multiple ROM to RAM sections,
|
||||
* uncomment .copy.table section and,
|
||||
* define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */
|
||||
/*
|
||||
.copy.table :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__copy_table_start__ = .;
|
||||
LONG (__etext)
|
||||
LONG (__data_start__)
|
||||
LONG (__data_end__ - __data_start__)
|
||||
LONG (__etext2)
|
||||
LONG (__data2_start__)
|
||||
LONG (__data2_end__ - __data2_start__)
|
||||
__copy_table_end__ = .;
|
||||
} > FLASH
|
||||
*/
|
||||
|
||||
/* To clear multiple BSS sections,
|
||||
* uncomment .zero.table section and,
|
||||
* define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */
|
||||
/*
|
||||
.zero.table :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__zero_table_start__ = .;
|
||||
LONG (__bss_start__)
|
||||
LONG (__bss_end__ - __bss_start__)
|
||||
LONG (__bss2_start__)
|
||||
LONG (__bss2_end__ - __bss2_start__)
|
||||
__zero_table_end__ = .;
|
||||
} > FLASH
|
||||
*/
|
||||
|
||||
/* Location counter can end up 2byte aligned with narrow Thumb code but
|
||||
__etext is assumed by startup code to be the LMA of a section in RAM
|
||||
which must be 4byte aligned */
|
||||
__etext = ALIGN (4);
|
||||
|
||||
.data : AT (__etext)
|
||||
{
|
||||
__data_start__ = .;
|
||||
*(vtable)
|
||||
*(.data*)
|
||||
|
||||
. = ALIGN(4);
|
||||
/* preinit data */
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP(*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
|
||||
. = ALIGN(4);
|
||||
/* init data */
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP(*(SORT(.init_array.*)))
|
||||
KEEP(*(.init_array))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
|
||||
|
||||
. = ALIGN(4);
|
||||
/* finit data */
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP(*(SORT(.fini_array.*)))
|
||||
KEEP(*(.fini_array))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
|
||||
KEEP(*(.jcr*))
|
||||
. = ALIGN(4);
|
||||
/* All data end */
|
||||
__data_end__ = .;
|
||||
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
__bss_end__ = .;
|
||||
} > RAM
|
||||
|
||||
.heap (COPY):
|
||||
{
|
||||
__end__ = .;
|
||||
PROVIDE(end = .);
|
||||
*(.heap*)
|
||||
__HeapLimit = .;
|
||||
} > RAM
|
||||
|
||||
/* .stack_dummy section doesn't contains any symbols. It is only
|
||||
* used for linker to calculate size of stack sections, and assign
|
||||
* values to stack symbols later */
|
||||
.stack_dummy (COPY):
|
||||
{
|
||||
*(.stack*)
|
||||
} > RAM
|
||||
|
||||
/* Set stack top to end of RAM, and stack limit move down by
|
||||
* size of stack_dummy section */
|
||||
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
|
||||
PROVIDE(__stack = __StackTop);
|
||||
|
||||
/* Check if data + heap + stack exceeds RAM limit */
|
||||
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
|
||||
|
||||
/* my values for startup.S code inhirited */
|
||||
PROVIDE(_estack = __StackTop);
|
||||
PROVIDE(_sidata = __init_array_start); /* start address for the init values of the .data section. */
|
||||
PROVIDE(_sdata = __data_start__); /* start address for the .data section. */
|
||||
PROVIDE(_edata = __data_end__); /* end address for the .data section. */
|
||||
PROVIDE(_sbss = __bss_start__); /* start address for the .bss section. */
|
||||
PROVIDE(_ebss = __bss_end__); /* end address for the .bss section. */
|
||||
|
||||
}
|
||||
322
misc/gpu/main.c
@ -1,322 +0,0 @@
|
||||
/*
|
||||
* (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#include "main.h"
|
||||
#include "lcd.h"
|
||||
#include "version.h"
|
||||
#include <string.h>
|
||||
|
||||
// LL/CMSIS internal vars
|
||||
uint32_t SystemCoreClock = 12000000UL;
|
||||
|
||||
#define MY_I2C_ADDR 0x65
|
||||
|
||||
// gpio_setup()
|
||||
//
|
||||
void
|
||||
gpio_setup(void)
|
||||
{
|
||||
LL_GPIO_InitTypeDef init = {0};
|
||||
|
||||
// see external/stm32c0xx_hal_driver/Inc/stm32c0xx_ll_gpio.h
|
||||
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
|
||||
|
||||
//LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_3);
|
||||
|
||||
// Setup input pins -- which is all!
|
||||
// - make SPI stuff input at this point as well.
|
||||
init.Pin = INPUT_PINS | SPI_PINS | SPI_CTRL_PINS;
|
||||
init.Mode = LL_GPIO_MODE_INPUT;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_HIGH;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
|
||||
LL_GPIO_Init(GPIOA, &init);
|
||||
|
||||
// one output PIN: tells CPU we are controlling the bus
|
||||
init.Pin = OUTPUT_PINS;
|
||||
init.Mode = LL_GPIO_MODE_OUTPUT;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_HIGH;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
LL_GPIO_Init(GPIOA, &init);
|
||||
|
||||
// clear output signal
|
||||
LL_GPIO_ResetOutputPin(GPIOA, PIN_GPU_BUSY);
|
||||
}
|
||||
|
||||
// i2c_setup()
|
||||
//
|
||||
void
|
||||
i2c_setup(void)
|
||||
{
|
||||
// useful <https://github.com/STMicroelectronics/STM32CubeG0/blob/ae31d181b3244190d7d5bc0d91e66a82ce4270ce/Projects/NUCLEO-G031K8/Examples_LL/I2C/I2C_TwoBoards_MasterRx_SlaveTx_IT_Init/Src/main.c#L205>
|
||||
|
||||
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
|
||||
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
|
||||
|
||||
{
|
||||
// setup pins PB6/7
|
||||
LL_GPIO_InitTypeDef init = {0};
|
||||
|
||||
init.Pin = I2C_PINS;
|
||||
init.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
init.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
||||
init.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
|
||||
init.Pull = LL_GPIO_PULL_NO;
|
||||
init.Alternate = LL_GPIO_AF_6;
|
||||
|
||||
LL_GPIO_Init(GPIOB, &init);
|
||||
}
|
||||
|
||||
{
|
||||
LL_I2C_InitTypeDef init = {0};
|
||||
|
||||
init.PeripheralMode = LL_I2C_MODE_I2C;
|
||||
|
||||
// from MXCube w/ FastMode @ 12Mhz main micro
|
||||
init.Timing = 0x00100413;
|
||||
|
||||
init.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
|
||||
init.DigitalFilter = 0x0; // disabled
|
||||
init.OwnAddress1 = MY_I2C_ADDR << 1;
|
||||
init.TypeAcknowledge = LL_I2C_ACK;
|
||||
init.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
|
||||
|
||||
LL_I2C_Init(I2C1, &init);
|
||||
|
||||
LL_I2C_EnableAutoEndMode(I2C1);
|
||||
LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
|
||||
LL_I2C_DisableOwnAddress2(I2C1);
|
||||
LL_I2C_DisableGeneralCall(I2C1);
|
||||
LL_I2C_EnableClockStretching(I2C1);
|
||||
}
|
||||
}
|
||||
|
||||
// enter_bootloader()
|
||||
//
|
||||
void
|
||||
enter_bootloader(void)
|
||||
{
|
||||
// Force entry into bootloader on next reset
|
||||
SET_BIT(FLASH->ACR, FLASH_ACR_PROGEMPTY);
|
||||
}
|
||||
|
||||
// i2c_poll()
|
||||
//
|
||||
void
|
||||
i2c_poll(void)
|
||||
{
|
||||
static uint8_t cmd, respLen, argLen;
|
||||
static uint8_t args[8];
|
||||
static const char *resp;
|
||||
static bool isRead;
|
||||
|
||||
// do we have work from I2C port?
|
||||
if(I2C1->ISR & I2C_ISR_ADDR) {
|
||||
// we are selected; note DIR and ADDR
|
||||
I2C1->ICR |= I2C_ICR_ADDRCF;
|
||||
|
||||
isRead = !!(I2C1->ISR & I2C_ISR_DIR);
|
||||
|
||||
// reset our state
|
||||
if(!isRead) {
|
||||
cmd = 0;
|
||||
argLen = 0;
|
||||
} else {
|
||||
// during write to us, allow any length, but we might ignore stuff
|
||||
CLEAR_BIT(I2C1->CR1, I2C_CR1_SBC); // start bit control BROKEN?
|
||||
}
|
||||
}
|
||||
|
||||
if(I2C1->ISR & I2C_ISR_STOPF) {
|
||||
// master is done sending us bytes
|
||||
I2C1->ICR |= I2C_ICR_STOPCF;
|
||||
|
||||
// Implement the command logic after STOP of the sending request
|
||||
// - sending strings as zero-terminated
|
||||
// - for other responses, master will need to know true length of response
|
||||
if(!isRead) {
|
||||
respLen = 0;
|
||||
switch(cmd) {
|
||||
case 'V': // full version
|
||||
if(argLen != 0) goto bad_args;
|
||||
resp = version_string;
|
||||
respLen = strlen(version_string)+1;
|
||||
break;
|
||||
|
||||
case 'v': // short version
|
||||
if(argLen != 0) goto bad_args;
|
||||
resp = RELEASE_VERSION;
|
||||
respLen = strlen(RELEASE_VERSION)+1;
|
||||
break;
|
||||
|
||||
case 'p': // ping
|
||||
resp = (const char *)args;
|
||||
respLen = argLen;
|
||||
break;
|
||||
|
||||
case 'b': // enter bootloader (follow w/ hard reset)
|
||||
if(argLen != 0) goto bad_args;
|
||||
enter_bootloader();
|
||||
resp = "OK";
|
||||
respLen = 3;
|
||||
break;
|
||||
|
||||
case 'c': // enable cursor: args=x,y,ctype
|
||||
if(argLen != 3) goto bad_args;
|
||||
lcd_state.activity_bar = false;
|
||||
lcd_state.cursor_x = args[0];
|
||||
lcd_state.cursor_y = args[1];
|
||||
lcd_state.cursor_type = args[2];
|
||||
lcd_state.cur_flash = false;
|
||||
break;
|
||||
|
||||
case 'a': // disable cursor (implied: enable activity bar)
|
||||
if(argLen != 0) goto bad_args;
|
||||
lcd_state.activity_bar = true;
|
||||
lcd_state.cursor_type = NO_CURSOR;
|
||||
break;
|
||||
|
||||
case 't': // test feature: draw a single test pattern
|
||||
if(argLen != 0) goto bad_args;
|
||||
lcd_state.test_pattern = true;
|
||||
lcd_state.activity_bar = false;
|
||||
lcd_state.cursor_type = NO_CURSOR;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
resp = "Bad cmd?";
|
||||
respLen = strlen(resp);
|
||||
break;
|
||||
|
||||
bad_args:
|
||||
resp = "Bad args?";
|
||||
respLen = strlen(resp);
|
||||
break;
|
||||
}
|
||||
|
||||
// critical: flush old data
|
||||
SET_BIT(I2C1->ISR, I2C_ISR_TXE);
|
||||
|
||||
// prepare for N byte response (respLen)
|
||||
//BROKEN//SET_BIT(I2C1->CR1, I2C_CR1_SBC); // start bit control
|
||||
//BROKEN//MODIFY_REG(I2C1->CR2, I2C_CR2_NBYTES_Msk, (respLen << I2C_CR2_NBYTES_Pos));
|
||||
//BROKEN//SET_BIT(I2C1->CR2, I2C_CR2_RELOAD);
|
||||
}
|
||||
}
|
||||
|
||||
while(I2C1->ISR & I2C_ISR_RXNE) {
|
||||
// master sent us a byte
|
||||
uint8_t rx = I2C1->RXDR;
|
||||
|
||||
if(cmd == 0) {
|
||||
cmd = rx;
|
||||
} else {
|
||||
if(argLen < sizeof(args)) {
|
||||
args[argLen++] = rx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(isRead && (I2C1->ISR & I2C_ISR_TXIS)) {
|
||||
// master wants to read a byte from us
|
||||
if(respLen) {
|
||||
// keep sending response
|
||||
I2C1->TXDR = *resp;
|
||||
resp++;
|
||||
respLen--;
|
||||
} else {
|
||||
// send NACK -- Doesn't work...
|
||||
//BROKEN//SET_BIT(I2C1->CR2, I2C_CR2_NACK);
|
||||
//BROKEN//SET_BIT(I2C1->ISR, I2C_ISR_TXE);
|
||||
|
||||
// workaround: give it something
|
||||
I2C1->TXDR = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clock_setup()
|
||||
//
|
||||
// Called from startup.S, before C runtime setup
|
||||
//
|
||||
void
|
||||
clock_setup(void)
|
||||
{
|
||||
// Vector Table Relocation in Internal FLASH (see interrupts.c)
|
||||
SCB->VTOR = FLASH_BASE;
|
||||
|
||||
// HSI configuration and activation
|
||||
LL_RCC_HSI_Enable();
|
||||
while(LL_RCC_HSI_IsReady() != 1)
|
||||
;
|
||||
|
||||
LL_RCC_HSI_SetCalibTrimming(64);
|
||||
LL_RCC_SetHSIDiv(LL_RCC_HSI_DIV_4);
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_HCLK_DIV_1);
|
||||
|
||||
// Sysclk activation on the HSI
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
|
||||
;
|
||||
|
||||
// Set APB1 prescaler
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
// WAS:
|
||||
// LL_Init1msTick(12000000);
|
||||
// LL_SetSystemCoreClock(12000000);
|
||||
// but, this saves 296-324 bytes because it avoids a division that pulls in a math helper
|
||||
//SysTick->LOAD = (uint32_t)((12000000 / 1000) - 1UL); // set reload register
|
||||
SysTick->LOAD = 11999;
|
||||
SysTick->VAL = 0; // Load the SysTick Counter Value
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||
SysTick_CTRL_ENABLE_Msk; // Enable the Systick Timer
|
||||
}
|
||||
|
||||
// mainloop()
|
||||
//
|
||||
// TODO: add naked attr and debug why that kills the code, or waste stack space forever
|
||||
//
|
||||
void __attribute__((noreturn))
|
||||
mainloop(void)
|
||||
{
|
||||
// Reset & enable of all peripherals we are using.
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG | LL_APB2_GRP1_PERIPH_SPI1);
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR | LL_APB1_GRP1_PERIPH_I2C1);
|
||||
|
||||
// Our setup code.
|
||||
gpio_setup();
|
||||
lcd_setup();
|
||||
i2c_setup();
|
||||
|
||||
// If we started ok, flash isn't empty and we don't need to force
|
||||
// entry into bootloader anymore.
|
||||
CLEAR_BIT(FLASH->ACR, FLASH_ACR_PROGEMPTY);
|
||||
|
||||
while(1) {
|
||||
i2c_poll();
|
||||
|
||||
// G_CTRL must be low, and TEAR high, and if so we can write to LCD.
|
||||
if(!LL_GPIO_IsInputPinSet(GPIOA, PIN_G_CTRL)
|
||||
&& LL_GPIO_IsInputPinSet(GPIOA, PIN_TEAR)
|
||||
) {
|
||||
lcd_animate();
|
||||
|
||||
// wait until start of next frame before looking again
|
||||
while(LL_GPIO_IsInputPinSet(GPIOA, PIN_TEAR)) {
|
||||
i2c_poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fatal_error()
|
||||
//
|
||||
void __attribute__((noreturn))
|
||||
fatal_error(const char *msg)
|
||||
{
|
||||
while(1) ;
|
||||
}
|
||||
|
||||
// EOF
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
*/
|
||||
#pragma once
|
||||
#include "basics.h"
|
||||
|
||||
// note: USE_FULL_LL_DRIVER is defined in Makefile for all files.
|
||||
|
||||
#include "stm32c0xx.h"
|
||||
#include "stm32c0xx_ll_bus.h"
|
||||
#include "stm32c0xx_ll_gpio.h"
|
||||
#include "stm32c0xx_ll_spi.h"
|
||||
#include "stm32c0xx_ll_i2c.h"
|
||||
#include "stm32c0xx_ll_rcc.h"
|
||||
#include "stm32c0xx_ll_utils.h"
|
||||
|
||||
// Pins in use: be careful, most are also controlled by main micro
|
||||
|
||||
// Port A
|
||||
#define PIN_G_CTRL LL_GPIO_PIN_0
|
||||
#define PIN_SCLK LL_GPIO_PIN_1
|
||||
#define PIN_MOSI LL_GPIO_PIN_2
|
||||
#define PIN_DATA_CMD LL_GPIO_PIN_3
|
||||
#define PIN_CS LL_GPIO_PIN_4
|
||||
#define PIN_TEAR LL_GPIO_PIN_5
|
||||
#define PIN_GPU_BUSY LL_GPIO_PIN_14 // G_SWCLK_B0 pin to CPU
|
||||
|
||||
#define INPUT_PINS (PIN_TEAR | PIN_G_CTRL)
|
||||
#define OUTPUT_PINS (PIN_GPU_BUSY)
|
||||
#define SPI_PINS (PIN_MOSI | PIN_SCLK)
|
||||
#define SPI_CTRL_PINS (PIN_DATA_CMD | PIN_CS)
|
||||
|
||||
// Port B
|
||||
#define PIN_SCL LL_GPIO_PIN_6
|
||||
#define PIN_SDA LL_GPIO_PIN_7
|
||||
#define I2C_PINS PIN_SCL | PIN_SDA
|
||||
|
||||
// EOF
|
||||
@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Render a little barcode we need for selftest process.
|
||||
#
|
||||
# - packed bytes
|
||||
#
|
||||
import barcode
|
||||
from io import BytesIO
|
||||
from barcode import Code128
|
||||
from barcode.writer import ImageWriter
|
||||
|
||||
|
||||
class Packer(barcode.writer.BaseWriter):
|
||||
# api in <../../ENV/lib/python3.10/site-packages/barcode/writer.py>
|
||||
def __init__(self):
|
||||
super().__init__(initialize=self.do_init,
|
||||
paint_module=self.paint, paint_text=self.do_text, finish=self.do_fin)
|
||||
|
||||
def do_init(self, code):
|
||||
# the answer I want is given to init function: binary for black/white sections
|
||||
assert len(code) == 1, 'not a list?'
|
||||
code = code[0]
|
||||
|
||||
if len(code) % 2:
|
||||
code += '0'
|
||||
while (len(code) % 8) != 0:
|
||||
code = f'0{code}0'
|
||||
|
||||
#code = code.replace('0', '00').replace('1', '11') # double it up
|
||||
code = code.replace('0', '000').replace('1', '111') # 3X
|
||||
#code = code.replace('0', '0000').replace('1', '1111') # 4X
|
||||
|
||||
# pad to 320 pixels (div 8) (centered)
|
||||
while len(code) < 320:
|
||||
code = f'0000{code}0000'
|
||||
|
||||
# convert to bytes
|
||||
self.result = int(code, 2).to_bytes(len(code)//8, 'big')
|
||||
|
||||
def do_text(self, *unused):
|
||||
pass
|
||||
|
||||
def paint(self, xpos, ypos, width, color):
|
||||
#print(f'paint: pos={xpos},{ypos} w={width} c={color}')
|
||||
pass
|
||||
|
||||
def do_fin(self):
|
||||
return self.result
|
||||
|
||||
def doit(ofile='barcode.h'):
|
||||
|
||||
# contents of barcode
|
||||
if 0:
|
||||
# works, but overkill and reads better if simpler
|
||||
version = None
|
||||
with open('version.h') as fd:
|
||||
for ln in fd:
|
||||
if 'RELEASE_VERSION' in ln:
|
||||
version = eval(ln.split()[-1])
|
||||
break
|
||||
assert version
|
||||
msg = f'GPU={version}'
|
||||
|
||||
msg = f'GPU'
|
||||
bc = Code128(msg, writer=Packer())
|
||||
rv = bc.render()
|
||||
|
||||
#bc2 = Code128(msg, writer=ImageWriter())
|
||||
#bc2.write('check.png')
|
||||
|
||||
#print(f'Result: {rv.hex()} len={len(rv)}')
|
||||
|
||||
assert len(rv) * 8 <= 320, 'too wide to fit on screen'
|
||||
assert len(rv) == 40, 'expected 320 pixels'
|
||||
|
||||
enc = rv.hex(' ', 1).replace(' ', ', 0x')
|
||||
|
||||
with open(ofile, 'wt') as fd:
|
||||
fd.write(f'''// autogen file, see make_barcode.py
|
||||
|
||||
// in python: {repr(rv)}
|
||||
static const uint8_t test_barcode[{len(rv)}] = {{
|
||||
0x{enc}
|
||||
}};
|
||||
|
||||
// EOF''')
|
||||
|
||||
print(f"Updated: {ofile}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
doit()
|
||||
|
||||
# EOF
|
||||